Python 網絡爬蟲實戰:爬取 B站《全職高手》20萬條評論數據

Python 網絡爬蟲實戰:爬取 B站《全職高手》20萬條評論數據

本周我們的目標是: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萬條左右,反覆檢查爬蟲程序及原網站后發現,這個屬於正常現象,因為有刪評論的情況,評論刪除之後,後面的樓層並不會重新排序,而是就這樣把刪掉的那層空下了。導致樓層數和評論數不一致。

 

 

 如果文章中有哪裡沒有講明白,或者講解有誤的地方,歡迎在評論區批評指正,或者掃描下面的二維碼,加我微信,大家一起學習交流,共同進步。

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※教你寫出一流的銷售文案?

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※回頭車貨運收費標準

※別再煩惱如何寫文案,掌握八大原則!

※超省錢租車方案

※產品缺大量曝光嗎?你需要的是一流包裝設計!