本周我們的目標是:B站(嗶哩嗶哩彈幕網 https://www.bilibili.com )視頻評論資料,
我們都知道,B站有很多號稱“鎮站之寶”的視頻,擁有著數量極其恐怖的評論和彈幕,所以這次我們的目標就是,爬取B站視頻的評論資料,分析其為何會深受大家喜愛,
首先去調研一下,B站評論數量最多的視頻是哪一個,,,好在已經有大佬已經統計過了,我們來看一哈!
?【B站大資料可視化】B站評論數最多的視頻究竟是?來自 <https://www.bilibili.com/video/av34900167/>
嗯?《全職高手》,有點意思,第一集和最后一集分別占據了評論數量排行榜的第二名和第一名,遠超了其他很多很火的番,那好,就拿它下手吧,看看它到底強在哪兒,
廢話不多說,先去B站看看這部神劇到底有多好看 https://www.bilibili.com/bangumi/play/ep107656
?
額,需要開通大會員才能觀看,,,
好吧,不看就不看,不過好在雖然視頻看不了,評論卻是可以看的,
?
感受到它的恐怖了嗎?63w6條的評論!9千多頁!果然是不同凡響啊,
接下來,我們就開始撰寫爬蟲,爬取這些資料吧,
使用爬蟲爬取網頁一般分為四個階段:分析目標網頁,獲取網頁內容,提取關鍵資訊,輸出保存,
1. 分析目標網頁
-
首先觀察評論區結構,發現評論區為滑鼠點擊翻頁形式,共 9399 頁,每一頁有 20 條評論,每條評論中包含 用戶名、評論內容、評論樓層、時間日期、點贊數等資訊展示,
?
-
接著我們按 F12 召喚出開發者工具,切換到Network,然后用滑鼠點擊評論翻頁,觀察這個程序有什么變化,并以此來制定我們的爬取策略,
-
我們不難發現,整個程序中 URL 不變,說明評論區翻頁不是通過 URL 控制,而在每翻一頁的時候,網頁會向服務器發出這樣的請求(請看 Request URL),
?
-
點擊 Preview 欄,可以切換到預覽頁面,也就是說,可以看到這個請求回傳的結果是什么,下面是該請求回傳的 json 檔案,包含了在 replies 里包含了本頁的評論資料,在這個 json 檔案里,我們可以發現,這里面包含了太多的資訊,除了網頁上展示的資訊,還有很多沒展示出來的資訊也有,簡直是挖到寶了,不過,我們這里用不到,通通忽略掉,只挑我們關注的部分就好了,
?
2. 獲取網頁內容
網頁內容分析完畢,可以正式寫代碼爬了,
1 import requests 2 3 def fetchURL(url): 4 ''' 5 功能:訪問 url 的網頁,獲取網頁內容并回傳 6 引數: 7 url :目標網頁的 url 8 回傳:目標網頁的 html 內容 9 ''' 10 headers = { 11 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8', 12 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36', 13 } 14 15 try: 16 r = requests.get(url,headers=headers) 17 r.raise_for_status() 18 print(r.url) 19 return r.text 20 except requests.HTTPError as e: 21 print(e) 22 print("HTTPError") 23 except requests.RequestException as e: 24 print(e) 25 except: 26 print("Unknown Error !") 27 28 29 if __name__ == '__main__': 30 url = 'https://api.bilibili.com/x/v2/reply?callback=jQuery172020326544171595695_1541502273311&jsonp=jsonp&pn=2&type=1&oid=11357166&sort=0&_=1541502312050' 31 html = fetchURL(url) 32 print(html)
不過,在運行過后,你會發現,403 錯誤,服務器拒絕了我們的訪問,
運行結果:
403 Client Error: Forbidden for url: https://api.bilibili.com/x/v2/reply?callback=jQuery172020326544171595695_1541502273311&jsonp=jsonp&pn=2&type=1&oid=11357166&sort=0&_=1541502312050 HTTPError None
同樣的,這個請求放瀏覽器地址欄里面直接打開,會變403,什么也訪問不到,
?
這是我們本次爬蟲遇到的第一個坑,在瀏覽器中能正常回傳回應,但是直接打開請求鏈接時,卻會被服務器拒絕,(我第一反應是 cookie ,將瀏覽器中的 cookie 放入爬蟲的請求頭中,重新訪問,發現沒用),或許這也算是一個小的反爬蟲機制吧,
網上查閱資料之后,我找到了解決的方法(雖然不了解原理),原請求的 URL 引數如下:
callback = jQuery1720913511919053787_1541340948898 jsonp = jsonp pn = 2 type = 1 oid = 11357166&sort=0 _ = 1541341035236
其中,真正有用的引數只有三個:pn(頁數),type(=1)和oid(視頻id),洗掉其余不必要的引數之后,用新整理出的url去訪問,成功獲取到評論資料,
https://api.bilibili.com/x/v2/reply?type=1&oid=11357166&pn=2
?
然后,在主函式中,通過寫一個 for 回圈,通過改變 pn 的值,獲取每一頁的評論資料,
1 if __name__ == '__main__': 2 for page in range(0,9400): 3 url = 'https://api.bilibili.com/x/v2/reply?type=1&oid=11357166&pn=' + str(page) 4 html = fetchURL(url)
3. 提取關鍵資訊
通過 json 庫對獲取到的回應內容進行決議,然后提取我們需要的內容:樓層,用戶名,性別,時間,評價,點贊數,回復數,
1 import json 2 import time 3 4 def parserHtml(html): 5 ''' 6 功能:根據引數 html 給定的記憶體型 HTML 檔案,嘗試決議其結構,獲取所需內容 7 引數: 8 html:類似檔案的記憶體 HTML 文本物件 9 ''' 10 s = json.loads(html) 11 12 for i in range(20): 13 comment = s['data']['replies'][i] 14 15 # 樓層,用戶名,性別,時間,評價,點贊數,回復數 16 floor = comment['floor'] 17 username = comment['member']['uname'] 18 sex = comment['member']['sex'] 19 ctime = time.strftime("%Y-%m-%d %H:%M:%S",time.localtime(comment['ctime'])) 20 content = comment['content']['message'] 21 likes = comment['like'] 22 rcounts = comment['rcount'] 23 24 print('--'+str(floor) + ':' + username + '('+sex+')' + ':'+ctime) 25 print(content) 26 print('like : '+ str(likes) + ' ' + 'replies : ' + str(rcounts)) 27 print(' ')
部分運行結果如下:
--204187:day可可鈴(保密):2018-11-05 18:16:22 太太又出本了,這次真的木錢了(′;ω;`) like : 1 replies : 0 --204186:長夜未央233(女):2018-11-05 16:24:52 12區打卡 like : 2 replies : 0 --204185:果然還是人渣一枚(男):2018-11-05 13:48:09 貌似忘來了好幾天 like : 1 replies : 1 --204183:day可可鈴(保密):2018-11-05 13:12:38 要準備去學校了,萬惡的期中考試( ′_ゝ`) like : 2 replies : 0 --204182:拾秋以葉(保密):2018-11-05 12:04:19 11月5日打卡( ̄▽ ̄) like : 1 replies : 0 --204181:芝米士噠(女):2018-11-05 07:53:43 這次是真的錯過了一個億[蛆音娘_扶額] like : 2 replies : 1
4. 保存輸出
我們把這些資料以 csv 的格式保存于本地,即完成了本次爬蟲的全部任務,下面附上爬蟲的全部代碼,
1 import requests 2 import json 3 import time 4 5 def fetchURL(url): 6 ''' 7 功能:訪問 url 的網頁,獲取網頁內容并回傳 8 引數: 9 url :目標網頁的 url 10 回傳:目標網頁的 html 內容 11 ''' 12 headers = { 13 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8', 14 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36', 15 } 16 17 try: 18 r = requests.get(url,headers=headers) 19 r.raise_for_status() 20 print(r.url) 21 return r.text 22 except requests.HTTPError as e: 23 print(e) 24 print("HTTPError") 25 except requests.RequestException as e: 26 print(e) 27 except: 28 print("Unknown Error !") 29 30 31 def parserHtml(html): 32 ''' 33 功能:根據引數 html 給定的記憶體型 HTML 檔案,嘗試決議其結構,獲取所需內容 34 引數: 35 html:類似檔案的記憶體 HTML 文本物件 36 ''' 37 try: 38 s = json.loads(html) 39 except: 40 print('error') 41 42 commentlist = [] 43 hlist = [] 44 45 hlist.append("序號") 46 hlist.append("名字") 47 hlist.append("性別") 48 hlist.append("時間") 49 hlist.append("評論") 50 hlist.append("點贊數") 51 hlist.append("回復數") 52 53 #commentlist.append(hlist) 54 55 # 樓層,用戶名,性別,時間,評價,點贊數,回復數 56 for i in range(20): 57 comment = s['data']['replies'][i] 58 blist = [] 59 60 floor = comment['floor'] 61 username = comment['member']['uname'] 62 sex = comment['member']['sex'] 63 ctime = time.strftime("%Y-%m-%d %H:%M:%S",time.localtime(comment['ctime'])) 64 content = comment['content']['message'] 65 likes = comment['like'] 66 rcounts = comment['rcount'] 67 68 blist.append(floor) 69 blist.append(username) 70 blist.append(sex) 71 blist.append(ctime) 72 blist.append(content) 73 blist.append(likes) 74 blist.append(rcounts) 75 76 commentlist.append(blist) 77 78 writePage(commentlist) 79 print('---'*20) 80 81 def writePage(urating): 82 ''' 83 Function : To write the content of html into a local file 84 html : The response content 85 filename : the local filename to be used stored the response 86 ''' 87 88 import pandas as pd 89 dataframe = pd.DataFrame(urating) 90 dataframe.to_csv('Bilibili_comment5-1000條.csv', mode='a', index=False, sep=',', header=False) 91 92 93 if __name__ == '__main__': 94 for page in range(0,9400): 95 url = 'https://api.bilibili.com/x/v2/reply?type=1&oid=11357166&pn=' + str(page) 96 html = fetchURL(url) 97 parserHtml(html) 98 99 # 為了降低被封ip的風險,每爬20頁便歇5秒, 100 if page%20 == 0: 101 time.sleep(5)
寫在最后
在爬取程序中,還是遇到了很多的小坑的,
1. 請求的 url 不能直接用,需要對引數進行篩選整理后才能訪問,
2. 爬取程序其實并不順利,因為如果爬取期間如果有用戶發表評論,則請求回傳的回應會為空導致程式出錯,所以在實際爬取程序中,記錄爬取的位置,以便出錯之后從該位置繼續爬,(并且,挑選深夜一兩點這種發帖人數少的時間段,可以極大程度的減少程式出錯的機率)
3. 爬取到的資料有多處不一致,其實這個不算是坑,不過這里還是講一下,免得產生困惑,
a. 就是評論區樓層只到了20多萬,但是評論數量卻有63萬多條,這個不一致主要是由于B站的評論是可以回復的,回復的評論也會計算到總評論數里,我們這里只爬樓層的評論,而評論的回復則忽略,只統計回復數即可,
b. 評論區樓層在20萬條左右,但是我們最后爬取下來的資料只有18萬條左右,反復檢查爬蟲程式及原網站后發現,這個屬于正常現象,因為有刪評論的情況,評論洗掉之后,后面的樓層并不會重新排序,而是就這樣把刪掉的那層空下了,導致樓層數和評論數不一致,
如果文章中有哪里沒有講明白,或者講解有誤的地方,歡迎在評論區批評指正,或者掃描下面的二維碼,加我微信,大家一起學習交流,共同進步,

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/115627.html
標籤:Python
上一篇:用post請求制作翻譯
