溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Python 網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn):爬取 B站《全職高手》20萬條評論數(shù)據(jù)

發(fā)布時間:2020-07-09 19:21:43 來源:網(wǎng)絡(luò) 閱讀:598 作者:機(jī)靈鶴 欄目:編程語言

本周我們的目標(biāo)是:B站(嗶哩嗶哩彈幕網(wǎng) https://www.bilibili.com )視頻評論數(shù)據(jù)。

我們都知道,B站有很多號稱“鎮(zhèn)站之寶”的視頻,擁有著數(shù)量極其恐怖的評論和彈幕。所以這次我們的目標(biāo)就是,爬取B站視頻的評論數(shù)據(jù),分析其為何會深受大家喜愛。

首先去調(diào)研一下,B站評論數(shù)量最多的視頻是哪一個。。。好在已經(jīng)有大佬已經(jīng)統(tǒng)計過了,我們來看一哈!

Python 網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn):爬取 B站《全職高手》20萬條評論數(shù)據(jù)

【B站大數(shù)據(jù)可視化】B站評論數(shù)最多的視頻究竟是?來自 <https://www.bilibili.com/video/av34900167/>


嗯?《全職高手》,有點(diǎn)意思,第一集和最后一集分別占據(jù)了評論數(shù)量排行榜的第二名和第一名,遠(yuǎn)超了其他很多很火的番。那好,就拿它下手吧,看看它到底強(qiáng)在哪兒。

廢話不多說,先去B站看看這部神劇到底有多好看 https://www.bilibili.com/bangumi/play/ep107656

Python 網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn):爬取 B站《全職高手》20萬條評論數(shù)據(jù)

額,需要開通大會員才能觀看。。。

好吧,不看就不看,不過好在雖然視頻看不了,評論卻是可以看的。

Python 網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn):爬取 B站《全職高手》20萬條評論數(shù)據(jù)

感受到它的恐怖了嗎?63w6條的評論!9千多頁!果然是不同凡響啊。

接下來,我們就開始編寫爬蟲,爬取這些數(shù)據(jù)吧。



使用爬蟲爬取網(wǎng)頁一般分為四個階段:分析目標(biāo)網(wǎng)頁,獲取網(wǎng)頁內(nèi)容,提取關(guān)鍵信息,輸出保存。


1. 分析目標(biāo)網(wǎng)頁

  • 首先觀察評論區(qū)結(jié)構(gòu),發(fā)現(xiàn)評論區(qū)為鼠標(biāo)點(diǎn)擊翻頁形式,共 9399 頁,每一頁有 20 條評論,每條評論中包含 用戶名、評論內(nèi)容、評論樓層、時間日期、點(diǎn)贊數(shù)等信息展示。

Python 網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn):爬取 B站《全職高手》20萬條評論數(shù)據(jù)

  • 接著我們按 F12 召喚出開發(fā)者工具,切換到Network。然后用鼠標(biāo)點(diǎn)擊評論翻頁,觀察這個過程有什么變化,并以此來制定我們的爬取策略。

  • 我們不難發(fā)現(xiàn),整個過程中 URL 不變,說明評論區(qū)翻頁不是通過 URL 控制。而在每翻一頁的時候,網(wǎng)頁會向服務(wù)器發(fā)出這樣的請求(請看 Request URL)。

Python 網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn):爬取 B站《全職高手》20萬條評論數(shù)據(jù)

  • 點(diǎn)擊 Preview 欄,可以切換到預(yù)覽頁面,也就是說,可以看到這個請求返回的結(jié)果是什么。下面是該請求返回的 json 文件,包含了在 replies 里包含了本頁的評論數(shù)據(jù)。在這個 json 文件里,我們可以發(fā)現(xiàn),這里面包含了太多的信息,除了網(wǎng)頁上展示的信息,還有很多沒展示出來的信息也有,簡直是挖到寶了。不過,我們這里用不到,通通忽略掉,只挑我們關(guān)注的部分就好了。

Python 網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn):爬取 B站《全職高手》20萬條評論數(shù)據(jù)

2. 獲取網(wǎng)頁內(nèi)容

網(wǎng)頁內(nèi)容分析完畢,可以正式寫代碼爬了。

import requestsdef fetchURL(url):
    '''
    功能:訪問 url 的網(wǎng)頁,獲取網(wǎng)頁內(nèi)容并返回
    參數(shù):
        url :目標(biāo)網(wǎng)頁的 url
    返回:目標(biāo)網(wǎng)頁的 html 內(nèi)容
    '''
    headers = {        'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',        'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36',
    }    
    try:
        r = requests.get(url,headers=headers)
        r.raise_for_status()
        print(r.url)        return r.text    except requests.HTTPError as e:
        print(e)
        print("HTTPError")    except requests.RequestException as e:
        print(e)    except:
        print("Unknown Error !")        

if __name__ == '__main__':
    url = 'https://api.bilibili.com/x/v2/reply?callback=jQuery172020326544171595695_1541502273311&jsonp=jsonp&pn=2&type=1&oid=11357166&sort=0&_=1541502312050'
    html = fetchURL(url)
    print(html)

不過,在運(yùn)行過后,你會發(fā)現(xiàn),403 錯誤,服務(wù)器拒絕了我們的訪問。

運(yùn)行結(jié)果:

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,什么也訪問不到。

Python 網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn):爬取 B站《全職高手》20萬條評論數(shù)據(jù)

這是我們本次爬蟲遇到的第一個坑。在瀏覽器中能正常返回響應(yīng),但是直接打開請求鏈接時,卻會被服務(wù)器拒絕。(我第一反應(yīng)是 cookie ,將瀏覽器中的 cookie 放入爬蟲的請求頭中,重新訪問,發(fā)現(xiàn)沒用),或許這也算是一個小的反爬蟲機(jī)制吧。

網(wǎng)上查閱資料之后,我找到了解決的方法(雖然不了解原理),原請求的 URL 參數(shù)如下:

callback = jQuery1720913511919053787_1541340948898
jsonp = jsonp
pn = 2
type = 1
oid = 11357166&sort=0
_ = 1541341035236

其中,真正有用的參數(shù)只有三個:pn(頁數(shù)),type(=1)和oid(視頻id)。刪除其余不必要的參數(shù)之后,用新整理出的url去訪問,成功獲取到評論數(shù)據(jù)。

https://api.bilibili.com/x/v2/reply?type=1&oid=11357166&pn=2

Python 網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn):爬取 B站《全職高手》20萬條評論數(shù)據(jù)

然后,在主函數(shù)中,通過寫一個 for 循環(huán),通過改變 pn 的值,獲取每一頁的評論數(shù)據(jù)。

if __name__ == '__main__':    for page in range(0,9400):
        url = 'https://api.bilibili.com/x/v2/reply?type=1&oid=11357166&pn=' + str(page)
        html = fetchURL(url)

3. 提取關(guān)鍵信息

通過 json 庫對獲取到的響應(yīng)內(nèi)容進(jìn)行解析,然后提取我們需要的內(nèi)容:樓層,用戶名,性別,時間,評價,點(diǎn)贊數(shù),回復(fù)數(shù)。

import jsonimport timedef parserHtml(html):
    '''
    功能:根據(jù)參數(shù) html 給定的內(nèi)存型 HTML 文件,嘗試解析其結(jié)構(gòu),獲取所需內(nèi)容
    參數(shù):
            html:類似文件的內(nèi)存 HTML 文本對象
    '''
    s = json.loads(html)    for i in range(20):
        comment = s['data']['replies'][i]        # 樓層,用戶名,性別,時間,評價,點(diǎn)贊數(shù),回復(fù)數(shù)
        floor = comment['floor']
        username = comment['member']['uname']
        sex = comment['member']['sex']
        ctime = time.strftime("%Y-%m-%d %H:%M:%S",time.localtime(comment['ctime']))
        content = comment['content']['message']
        likes = comment['like']
        rcounts = comment['rcount']

        print('--'+str(floor) + ':' + username + '('+sex+')' + ':'+ctime)
        print(content)
        print('like : '+ str(likes) + '      ' + 'replies : ' + str(rcounts))
        print('  ')

部分運(yùn)行結(jié)果如下:

--204187:day可可鈴(保密):2018-11-05 18:16:22
太太又出本了,這次真的木錢了(′;ω;`)
like : 1      replies : 0
  
--204186:長夜未央233(女):2018-11-05 16:24:52
12區(qū)打卡
like : 2      replies : 0
  
--204185:果然還是人渣一枚(男):2018-11-05 13:48:09
貌似忘來了好幾天
like : 1      replies : 1
  
--204183:day可可鈴(保密):2018-11-05 13:12:38
要準(zhǔn)備去學(xué)校了,萬惡的期中考試( ′_ゝ`)
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. 保存輸出

我們把這些數(shù)據(jù)以 csv 的格式保存于本地,即完成了本次爬蟲的全部任務(wù)。下面附上爬蟲的全部代碼。

import requestsimport jsonimport timedef fetchURL(url):
    '''
    功能:訪問 url 的網(wǎng)頁,獲取網(wǎng)頁內(nèi)容并返回
    參數(shù):
        url :目標(biāo)網(wǎng)頁的 url
    返回:目標(biāo)網(wǎng)頁的 html 內(nèi)容
    '''
    headers = {        'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',        'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36',
    }    
    try:
        r = requests.get(url,headers=headers)
        r.raise_for_status()
        print(r.url)        return r.text    except requests.HTTPError as e:
        print(e)
        print("HTTPError")    except requests.RequestException as e:
        print(e)    except:
        print("Unknown Error !")        

def parserHtml(html):
    '''
    功能:根據(jù)參數(shù) html 給定的內(nèi)存型 HTML 文件,嘗試解析其結(jié)構(gòu),獲取所需內(nèi)容
    參數(shù):
            html:類似文件的內(nèi)存 HTML 文本對象
    '''
    try:
        s = json.loads(html)    except:
        print('error')
        
    commentlist = []
    hlist = []

    hlist.append("序號")
    hlist.append("名字")
    hlist.append("性別")
    hlist.append("時間")
    hlist.append("評論")
    hlist.append("點(diǎn)贊數(shù)")
    hlist.append("回復(fù)數(shù)")    #commentlist.append(hlist)

    # 樓層,用戶名,性別,時間,評價,點(diǎn)贊數(shù),回復(fù)數(shù)
    for i in range(20):
        comment = s['data']['replies'][i]
        blist = []

        floor = comment['floor']
        username = comment['member']['uname']
        sex = comment['member']['sex']
        ctime = time.strftime("%Y-%m-%d %H:%M:%S",time.localtime(comment['ctime']))
        content = comment['content']['message']
        likes = comment['like']
        rcounts = comment['rcount']

        blist.append(floor)
        blist.append(username)
        blist.append(sex)
        blist.append(ctime)
        blist.append(content)
        blist.append(likes)
        blist.append(rcounts)

        commentlist.append(blist)

    writePage(commentlist)
    print('---'*20)def writePage(urating):
    '''
        Function : To write the content of html into a local file
        html : The response content
        filename : the local filename to be used stored the response
    '''
    
    import pandas as pd
    dataframe = pd.DataFrame(urating)
    dataframe.to_csv('Bilibili_comment5-1000條.csv', mode='a', index=False, sep=',', header=False)if __name__ == '__main__':    for page in range(0,9400):
        url = 'https://api.bilibili.com/x/v2/reply?type=1&oid=11357166&pn=' + str(page)
        html = fetchURL(url)
        parserHtml(html)        # 為了降低被封ip的風(fēng)險,每爬20頁便歇5秒。
        if page%20 == 0:
            time.sleep(5)



寫在最后

在爬取過程中,還是遇到了很多的小坑的。

1. 請求的 url 不能直接用,需要對參數(shù)進(jìn)行篩選整理后才能訪問。

2. 爬取過程其實(shí)并不順利,因?yàn)槿绻廊∑陂g如果有用戶發(fā)表評論,則請求返回的響應(yīng)會為空導(dǎo)致程序出錯。所以在實(shí)際爬取過程中,記錄爬取的位置,以便出錯之后從該位置繼續(xù)爬。(并且,挑選深夜一兩點(diǎn)這種發(fā)帖人數(shù)少的時間段,可以極大程度的減少程序出錯的機(jī)率)

3. 爬取到的數(shù)據(jù)有多處不一致,其實(shí)這個不算是坑,不過這里還是講一下,免得產(chǎn)生困惑。

        a. 就是評論區(qū)樓層只到了20多萬,但是評論數(shù)量卻有63萬多條,這個不一致主要是由于B站的評論是可以回復(fù)的,回復(fù)的評論也會計算到總評論數(shù)里。我們這里只爬樓層的評論,而評論的回復(fù)則忽略,只統(tǒng)計回復(fù)數(shù)即可。

        b. 評論區(qū)樓層在20萬條左右,但是我們最后爬取下來的數(shù)據(jù)只有18萬條左右,反復(fù)檢查爬蟲程序及原網(wǎng)站后發(fā)現(xiàn),這個屬于正?,F(xiàn)象,因?yàn)橛袆h評論的情況,評論刪除之后,后面的樓層并不會重新排序,而是就這樣把刪掉的那層空下了。導(dǎo)致樓層數(shù)和評論數(shù)不一致。


向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI