受限於彈幕池的數量,沒有辦法能夠爬取到B站更多的彈幕呢?

本文的文字及圖片來源於網絡,僅供學習、交流使用,不具備任何商業用途,若有問題請及時聯繫咱們以做處理。數據庫

如下文章源於Python乾貨鋪子 ,做者:不正經的kimol君json

剛接觸Python的新手、小白,能夠複製下面的連接去免費觀看Python的基礎入門教學視頻api

https://v.douyu.com/author/y6AZ4jn9jwKW

 

前言

想必小破站你們都很熟悉叭,裏面充滿了各類神奇的視頻,其中的「彈幕」成爲了許多人的快樂源泉。關於它的爬蟲也有不少,但大部分都受限於彈幕池的數量,只能爬取到其中不多一部分的彈幕。瀏覽器

那麼,有沒有辦法能夠爬取到B站更多的彈幕呢?cookie

1、彈幕抓取(有數量限制)

首先,咱們須要找到B站視頻彈幕的接口,經過瀏覽器的F12調試工具抓包能夠發現,其接口爲:網絡

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

其實,除了這個接口以外,還有另一個接口一樣是能夠獲取到彈幕的:app

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

其中「oid」「cid」是iB站給每一個視頻分配的一個id號,可是一般咱們在瀏覽器地址看到的是「bvid」,所以須要作個轉換:ide

 

 

這裏相關的接口有不少,能夠定義以下函數:函數

def get_cid(bvid):
    '''
    經過視頻的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」以後,咱們即可以經過剛纔發現的彈幕接口爬取彈幕了,代碼以下:工具

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,不然會出現亂碼現象。

咱們獲得的請求爲一個xml文件,其格式大體以下:

 

所以,須要將其中的信息提取出來,「p」標籤對應的字段分別爲:

 

那麼,利用正則能夠它們都提早出來:

def parse_dm(text):
    '''
    解析視頻彈幕
    輸入:視頻彈幕的原數據
    輸出:彈幕的解析結果
    '''
    result = [] # 用於存儲解析結果
    data = re.findall('<d p="(.*?)">(.*?)</d>',text)
    for d in data:
        item = {} # 每條彈幕數據
        dm = d[0].split(',') # 彈幕的相關詳細,如出現時間,用戶等
        item['出現時間'] = float(dm[0])
        item['模式'] = int(dm[1])
        item['字號'] = int(dm[2])
        item['顏色'] = int(dm[3])
        item['評論時間'] = time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(int(dm[4])))
        item['彈幕池'] = int(dm[5])
        item['用戶ID'] = dm[6] # 並不是真實用戶ID,而是CRC32檢驗後的十六進制結果
        item['rowID'] = dm[7] # 彈幕在數據庫中的ID,用於「歷史彈幕」功能
        item['彈幕內容'] = d[1]
        result.append(item)    
    return result

經過解析requests的請求,便能獲得相應的彈幕:

dms = parse_dm(text) # 解析彈幕

可是,這裏受到彈幕池的限制,每次只能抓取一小部分,當彈幕數量不少時,顯然這個辦法行不通。

那麼,咱們得另尋它路了~

2、彈幕抓取(無數量限制)

經過分析,咱們能夠找到另一個接口,用於獲取天天的彈幕歷史數據:

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

「date」爲日期,經過遍歷日期即可得到更多的彈幕。須要注意的是,這個接口須要登錄,所以在請求的時候必須得加入cookies,能夠定義以下函數:

def get_history(bvid,date): 
    '''
    獲取視頻歷史彈幕
    輸入:視頻bvid,日期
    輸出:視頻某一日期開始的彈幕
    '''
    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

想要得到更多的彈幕,遍歷每一天的彈幕數據便可,可是這樣存在兩個問題:「效率過低」「數據重複」。由於,每次獲取一般能夠獲得跨越幾天的數據,因此咱們無需天天都去訪問,而是根據結果逐步往前推便可:

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

運行主函數:

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

爬取過程以下:

 

能夠看到,一共「11471條」彈幕,咱們獲取到了「11000條」,佔比已經很高了,並且csv文件已經安靜地躺在了個人電腦中,打開它:

 

至此,大功告成,舒坦了~~

本文同步分享在 博客"松鼠愛吃餅乾"(CSDN)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索