python3——爬取B站彈幕

在簡(jiǎn)書發(fā)現(xiàn)一篇有趣的文章:爬蟲,走起,用Excel實(shí)現(xiàn)5min抓取B站彈幕及初步處理
講到了如何根據(jù)開發(fā)者工具,獲得B站視頻的彈幕信息,不過有個(gè)不足就是手動(dòng)保存彈幕信息。而通過python我們可以輕松地自動(dòng)存儲(chǔ)。

以《周刊嗶哩嗶哩排行榜#359》http://www.bilibili.com/video/av10394940/ 為例:
根據(jù)文中的方法,我們得到彈幕網(wǎng)址: http://comment.bilibili.com/17168035.xml

很容易就猜想到彈幕的網(wǎng)址格式就是:
http://comment.bilibili.com/數(shù)字.xml ,我們可以嘗試著修改一下數(shù)字,發(fā)現(xiàn)確實(shí)如此。
那么如何確定數(shù)字“17168035”,就是我們自動(dòng)獲取彈幕列表的關(guān)鍵。
我們?cè)谝曨l網(wǎng)頁(yè)的html搜索“17168035”


天啊,竟然有。那問題就太簡(jiǎn)單了。用正則表達(dá)式一下子就拿出來了:

danmu_id = re.findall(r'cid=(\d+)&', html)[0]

直接看下代碼吧,沒什么難的。

import requests, re
from bs4 import BeautifulSoup as BS
#打開網(wǎng)頁(yè)函數(shù)
def open_url(url):
    headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.103 Safari/537.36'}
    response = requests.get(url=url, headers=headers)
    response.encoding = 'utf-8'
    html = response.text
    return html
#獲取彈幕url中的數(shù)字id號(hào)
def get_danmu_id(html):
    #用try防止有些av號(hào)沒視頻
    try:
        soup = BS(html, 'lxml')
        #視頻名
        title = soup.select('div.v-title > h1')[0].get_text()
        #投稿人
        author = soup.select('meta[name="author"]')[0]['content']
        #彈幕的網(wǎng)站代碼
        danmu_id = re.findall(r'cid=(\d+)&', html)[0]
        print(title, author)
        return danmu_id
    except:
        print('視頻不見了喲')
        return False

video_url ='http://www.bilibili.com/video/av10394940/'
video_html = open_url(video_url)
danmu_id = get_danmu_id(video_html)
if danmu_id:
    danmu_url = 'http://comment.bilibili.com/{}.xml'.format(danmu_id)
    danmu_html = open_url(url=danmu_url)
    soup = BS(danmu_html, 'lxml')
    all_d = soup.select('d')
    for d in all_d:
        #把d標(biāo)簽中P的各個(gè)屬性分離開
        danmu_list = d['p'].split(',')
        #d.get_text()是彈幕內(nèi)容
        danmu_list.append(d.get_text())
        print(danmu_list)

顯示結(jié)果如下,可以再把這些個(gè)列表保存起來。


注意:
后來發(fā)現(xiàn)有一些B站網(wǎng)址還是不行, 比如埃羅芒阿老師這種網(wǎng)址,不是傳統(tǒng)的av號(hào)形式(http://bangumi.bilibili.com/anime/5997/play#103921 )。
靜態(tài)頁(yè)面是實(shí)現(xiàn)不了,不過在開發(fā)者工具中還是能找到彈幕對(duì)應(yīng)的數(shù)字號(hào)的,因此可以考慮之前說過的selenium+Chrome或PhantomJS的組合。

很好奇P中的各個(gè)數(shù)值都代表了什么,查了下。

引自:https://zhidao.baidu.com/question/1430448163912263499.html
<d p="0,1,25,16777215,1312863760,0,eff85771,42759017">前排占位置</d>
p這個(gè)字段里面的內(nèi)容:
0,1,25,16777215,1312863760,0,eff85771,42759017
中幾個(gè)逗號(hào)分割的數(shù)據(jù)
第一個(gè)參數(shù)是彈幕出現(xiàn)的時(shí)間 以秒數(shù)為單位。
第二個(gè)參數(shù)是彈幕的模式1..3 滾動(dòng)彈幕 4底端彈幕 5頂端彈幕 6.逆向彈幕 7精準(zhǔn)定位 8高級(jí)彈幕
第三個(gè)參數(shù)是字號(hào), 12非常小,16特小,18小,25中,36大,45很大,64特別大
第四個(gè)參數(shù)是字體的顏色 以HTML顏色的十進(jìn)制為準(zhǔn)
第五個(gè)參數(shù)是Unix格式的時(shí)間戳?;鶞?zhǔn)時(shí)間為 1970-1-1 08:00:00
第六個(gè)參數(shù)是彈幕池 0普通池 1字幕池 2特殊池 【目前特殊池為高級(jí)彈幕專用】
第七個(gè)參數(shù)是發(fā)送者的ID,用于“屏蔽此彈幕的發(fā)送者”功能
第八個(gè)參數(shù)是彈幕在彈幕數(shù)據(jù)庫(kù)中rowID 用于“歷史彈幕”功能。

第一個(gè)參數(shù),可以按照如下轉(zhuǎn)換成時(shí)間(沒找到對(duì)應(yīng)的函數(shù)):

seconds = eval('1306.3900146484')
#商,余數(shù) = divmod(被除數(shù), 除數(shù))
m, s = divmod(seconds, 60)
h, m = divmod(m, 60)
print ("%02d:%02d:%02d" % (h, m, s))

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

-----------------------------------------------------------------------------------
-----------------------------------------------------------------------------------
其中第四個(gè)參數(shù),我們?cè)趺粗谰唧w的顏色是什么呢
比如輸出反復(fù)出現(xiàn)的16777215是什么顏色。我們可以進(jìn)行以下的轉(zhuǎn)換:
十進(jìn)制->十六進(jìn)制->RGB顏色表示
十進(jìn)制數(shù)16777215換成十六進(jìn)制數(shù)FFFFFF,也就是255,255,255。白色!
知道方法后,我們可以在在線調(diào)色板(點(diǎn)我進(jìn)入)就能知道任意數(shù)字的顏色了,比如那個(gè)41194(十六進(jìn)制00A0EA)。


原來是藍(lán)色呀。
-----------------------------------------------------------------------------------

第五個(gè)參數(shù),我們可以把它轉(zhuǎn)換下:

import time
print(time.ctime(1494238353))

輸出結(jié)果:
Mon May  8 18:12:33 2017

后續(xù)更新了下程序,修正了某些網(wǎng)址不能用靜態(tài)獲取彈幕數(shù)字號(hào)的問題。

import requests, re, time, csv
from bs4 import BeautifulSoup as BS
from selenium import webdriver
#打開網(wǎng)頁(yè)函數(shù)
def open_url(url):
    headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.103 Safari/537.36'}
    response = requests.get(url=url, headers=headers)
    response.encoding = 'utf-8'
    html = response.text
    return html
#獲取彈幕url中的數(shù)字id號(hào)

#當(dāng)requests行不通時(shí),采用selenium的方法。
def sele_get(url):
    SERVICE_ARGS = ['--load-images=false', '--disk-cache=true']
    driver = webdriver.PhantomJS(service_args = SERVICE_ARGS)
    driver.get(url)
    time.sleep(2)
    danmu_id = re.findall(r'cid=(\d+)&', driver.page_source)[0]

    return danmu_id


def get_danmu_id(html, url):
    try:
        soup = BS(html, 'lxml')
        #視頻名
        title = soup.select('title[data-vue-meta="true"]')[0].get_text()
        #投稿人
        author = soup.select('meta[name="author"]')[0]['content']
        #彈幕的網(wǎng)站代碼
        try:

            danmu_id = re.findall(r'cid=(\d+)&', html)[0]
            #danmu_id = re.findall(r'/(\d+)-1-64', html)[0]
            #print(danmu_id)
        except:
            danmu_id = sele_get(url)
        print(title, author)
        return danmu_id
    except:
        print('視頻不見了喲')
        return False
#秒轉(zhuǎn)換成時(shí)間
def sec2str(seconds):
    seconds = eval(seconds)
    m, s = divmod(seconds, 60)
    h, m = divmod(m, 60)
    time = "%02d:%02d:%02d" % (h, m, s)
    return time

#csv保存函數(shù)
def csv_write(tablelist):
    tableheader = ['出現(xiàn)時(shí)間', '彈幕模式', '字號(hào)', '顏色', '發(fā)送時(shí)間' ,'彈幕池', '發(fā)送者id', 'rowID', '彈幕內(nèi)容']
    with open('danmu.csv', 'w', newline='', errors='ignore') as f:
        writer = csv.writer(f)
        writer.writerow(tableheader)
        for row in tablelist:
            writer.writerow(row)


video_url ='http://www.bilibili.com/video/av5038338/'
video_html = open_url(video_url)
danmu_id = get_danmu_id(video_html, video_url)
all_list = []
if danmu_id:
    danmu_url = 'http://comment.bilibili.com/{}.xml'.format(danmu_id)
    danmu_html = open_url(url=danmu_url)
    soup = BS(danmu_html, 'lxml')
    all_d = soup.select('d')
    for d in all_d:
        #把d標(biāo)簽中P的各個(gè)屬性分離開
        danmu_list = d['p'].split(',')
        #d.get_text()是彈幕內(nèi)容
        danmu_list.append(d.get_text())
        danmu_list[0] = sec2str(danmu_list[0])
        danmu_list[4] = time.ctime(eval(danmu_list[4]))
        all_list.append(danmu_list)
        print(danmu_list)
    all_list.sort()
    csv_write(all_list)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,789評(píng)論 25 709
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,506評(píng)論 19 139
  • 在鳳鳴山的北邊有一個(gè)小村落,也沒有啥正經(jīng)的名字,只因村里的人大多姓吳,就叫吳家村。吳家村是個(gè)很貧窮的小村落,村里統(tǒng)...
    兮云閱讀 849評(píng)論 6 9
  • 松間明月照不透 清泉石上常斷流 流觴曲水不見酒 黃鶴樓前猛抬頭 長(zhǎng)天秋水隔兩邊 落霞孤鶩沒出現(xiàn) 黃昏約人空等候 月...
    雕兄_KYP閱讀 428評(píng)論 0 0

友情鏈接更多精彩內(nèi)容