溫馨提示×

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

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

Python如何爬取到B站的彈幕

發(fā)布時(shí)間:2022-01-14 15:26:01 來(lái)源:億速云 閱讀:1249 作者:小新 欄目:大數(shù)據(jù)

這篇文章主要介紹了Python如何爬取到B站的彈幕,具有一定借鑒價(jià)值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。

本文的文字及圖片來(lái)源于網(wǎng)絡(luò),僅供學(xué)習(xí)、交流使用,不具有任何商業(yè)用途,如有問(wèn)題請(qǐng)及時(shí)聯(lián)系我們以作處理。

前言

想必小破站大家都很熟悉叭,里面充滿了各種神奇的視頻,其中的「彈幕」成為了許多人的快樂(lè)源泉。關(guān)于它的爬蟲也有很多,但大部分都受限于彈幕池的數(shù)量,只能爬取到其中很少一部分的彈幕。

那么,有沒(méi)有辦法可以爬取到B站更多的彈幕呢?

一、彈幕抓?。ㄓ袛?shù)量限制)

首先,我們需要找到B站視頻彈幕的接口,通過(guò)瀏覽器的F12調(diào)試工具抓包可以發(fā)現(xiàn),其接口為:

https://api.bilibili.com/x/v1/dm/list.so?oid={oid/cid}

其實(shí),除了這個(gè)接口之外,還有另外一個(gè)接口同樣是可以獲取到彈幕的:

https://comment.bilibili.com/{oid/cid}.xml

其中「oid」「cid」是iB站給每個(gè)視頻分配的一個(gè)id號(hào),但是通常我們?cè)跒g覽器地址看到的是「bvid」,因此需要做個(gè)轉(zhuǎn)換:

Python如何爬取到B站的彈幕

這里相關(guān)的接口有很多,可以定義如下函數(shù):

def get_cid(bvid):
    '''
    通過(guò)視頻的bvid獲得視頻的cid
    輸入:視頻的bvid
    輸出:視頻的cid
    '''
    url = 'https://api.bilibili.com/x/player/pagelist?bvid=%s&jsonp=jsonp'%bvid
    res = requests.get(url)
    data = res.json()
    return data['data'][0]['cid']

有了「cid」之后,我們便可以通過(guò)剛才發(fā)現(xiàn)的彈幕接口爬取彈幕了,代碼如下:

oid = get_cid(bvid) # 這里的cid和oid是一樣的
url = 'https://api.bilibili.com/x/v1/dm/list.so?oid=%d'%oid
res = requests.get(url)
res.encoding = 'utf-8'
text = res.text

注意:這里需要指定res的編碼方式為utf-8,否則會(huì)出現(xiàn)亂碼現(xiàn)象。

我們得到的請(qǐng)求為一個(gè)xml文件,其格式大致如下:

Python如何爬取到B站的彈幕

那么,利用正則可以它們都提前出來(lái):

def parse_dm(text):
    '''
    解析視頻彈幕
    輸入:視頻彈幕的原數(shù)據(jù)
    輸出:彈幕的解析結(jié)果
    '''
    result = [] # 用于存儲(chǔ)解析結(jié)果
    data = re.findall('<d p="(.*?)">(.*?)</d>',text)
    for d in data:
        item = {} # 每條彈幕數(shù)據(jù)
        dm = d[0].split(',') # 彈幕的相關(guān)詳細(xì),如出現(xiàn)時(shí)間,用戶等
        item['出現(xiàn)時(shí)間'] = float(dm[0])
        item['模式'] = int(dm[1])
        item['字號(hào)'] = int(dm[2])
        item['顏色'] = int(dm[3])
        item['評(píng)論時(shí)間'] = time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(int(dm[4])))
        item['彈幕池'] = int(dm[5])
        item['用戶ID'] = dm[6] # 并非真實(shí)用戶ID,而是CRC32檢驗(yàn)后的十六進(jìn)制結(jié)果
        item['rowID'] = dm[7] # 彈幕在數(shù)據(jù)庫(kù)中的ID,用于“歷史彈幕”功能
        item['彈幕內(nèi)容'] = d[1]
        result.append(item)    
    return result

通過(guò)解析requests的請(qǐng)求,便能得到相應(yīng)的彈幕:

dms = parse_dm(text) # 解析彈幕

但是,這里受到彈幕池的限制,每次只能抓取一小部分,當(dāng)彈幕數(shù)量很多時(shí),顯然這個(gè)辦法行不通。

那么,我們得另尋它路了~

二、彈幕抓?。o(wú)數(shù)量限制)

通過(guò)分析,我們可以找到另外一個(gè)接口,用于獲取每天的彈幕歷史數(shù)據(jù):

https://api.bilibili.com/x/v2/dm/history?type=1&oid={oid/cid}&date=xx-xx

「date」為日期,通過(guò)遍歷日期便可獲得更多的彈幕。需要注意的是,這個(gè)接口需要登陸,因此在請(qǐng)求的時(shí)候必須得加入cookies,可以定義如下函數(shù):

def get_history(bvid,date): 
    '''
    獲取視頻歷史彈幕
    輸入:視頻bvid,日期
    輸出:視頻某一日期開(kāi)始的彈幕
    '''
    oid = get_cid(bvid)
    url = 'https://api.bilibili.com/x/v2/dm/history?type=1&oid=%d&date=%s'%(oid,date)
    headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:82.0) Gecko/20100101 Firefox/82.0',
               'Accept': '*/*',
               'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
               'Origin': 'https://www.bilibili.com',
               'Connection': 'keep-alive',
               'Referer': 'https://www.bilibili.com/video/BV1k54y1U79J',
               'TE': 'Trailers'}
    # 此接口需要登陸,因此需要cookies
    cookies = {}
    res = requests.get(url,headers=headers,cookies=cookies)
    res.encoding = 'utf-8'
    text = res.text
    dms = parse_dm(text) # 解析彈幕
    return dms

想要獲得更多的彈幕,遍歷每一天的彈幕數(shù)據(jù)即可,但是這樣存在兩個(gè)問(wèn)題:「效率太低」、「數(shù)據(jù)重復(fù)」。因?yàn)?,每次獲取通??梢缘玫娇缭綆滋斓臄?shù)據(jù),所以我們無(wú)需每天都去訪問(wèn),而是根據(jù)結(jié)果逐步往前推即可:

def get_dms(bvid):
    '''
    獲取視頻彈幕(此方法獲取的彈幕數(shù)量更多)
    輸入:視頻的bvid
    輸出:視頻的彈幕
    '''
    print('視頻解析中...')
    info = get_info(bvid)
    print('視頻解析完成!')
    print('【視頻標(biāo)題】: %s\n【視頻播放量】:%d\n【彈幕數(shù)量】:  %d\n【上傳日期】:  %s'%(info[0],info[1],info[2],info[3]))
    dms = get_dm(bvid) # 存儲(chǔ)彈幕
    if len(dms) >= info[2]: # 如果彈幕數(shù)量已抓滿
        return dms
    else:
        dms = []
        date = time.strftime('%Y-%m-%d',time.localtime(time.time())) # 從今天開(kāi)始
        while True:
            dm = get_history(bvid,date)
            dms.extend(dm)
            print('"%s"彈幕爬取完成!(%d條)'%(date,len(dm)))
            if len(dm) == 0: # 如果為空
                break
            end = dm[-1]['評(píng)論時(shí)間'].split()[0] # 取最后一條彈幕的日期
            if end == date: # 如果最后一條仍為當(dāng)天,則往下推一天
                end = (datetime.datetime.strptime(end,'%Y-%m-%d')-datetime.timedelta(days=1)).strftime('%Y-%m-%d')
            if end == info[3]: # 如果已經(jīng)到達(dá)上傳那天
                break
            else:
                date = end
        dm = get_history(bvid,info[3]) # 避免忽略上傳那天的部分?jǐn)?shù)據(jù)
        dms.extend(dm)
        print('彈幕爬取完成!(共%d條)'%len(dms))
        print('數(shù)據(jù)去重中...')
        dms = del_repeat(dms,'rowID') # 按rowID給彈幕去重
        print('數(shù)據(jù)去重完成!(共%d條)'%len(dms))
        return dms

運(yùn)行主函數(shù):

if __name__ == '__main__':
    dms = get_dms('BV1HJ411L7DP')
    dms = pd.DataFrame(dms)
    dms.to_csv('一路向北.csv',index=False)

爬取過(guò)程如下:

Python如何爬取到B站的彈幕

感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享的“Python如何爬取到B站的彈幕”這篇文章對(duì)大家有幫助,同時(shí)也希望大家多多支持億速云,關(guān)注億速云行業(yè)資訊頻道,更多相關(guān)知識(shí)等著你來(lái)學(xué)習(xí)!

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

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

AI