摘要:站的彈幕服務(wù)器也有類似的機(jī)制,隨便打開一個(gè)未開播的直播間,抓包將看到每隔左右會(huì)給服務(wù)端發(fā)送一個(gè)心跳包,協(xié)議頭第四部分的值從修改為即可。
原文:B 站直播間數(shù)據(jù)爬蟲, 歡迎轉(zhuǎn)載
項(xiàng)目地址:bilibili-live-crawler
去年在 B 站發(fā)現(xiàn)一個(gè)后期超強(qiáng)的 UP 主:修仙不倒大小眼,專出 PDD 這樣知名主播的吃雞精彩集錦,漲粉超快。于是想怎么做這樣的 UP,遇到的第一個(gè)問題便是素材,精彩時(shí)刻需要手動(dòng)從直播錄播中剪輯,很低效。
用戶習(xí)慣我經(jīng)常看直播,但是很少發(fā)彈幕和送禮物,只有在主播玩出很溜的操作或講很好玩的事情時(shí),才會(huì)發(fā)彈幕互動(dòng)、送禮物支持,經(jīng)??粗辈サ氖矣岩彩侨绱?。
基于這個(gè)用戶習(xí)慣,不難推斷出在直播間的彈幕高峰或禮物高峰期,主播應(yīng)該做了些好玩的事情,比如吃到雞了,或者全隊(duì)被殲滅之類的…這些時(shí)刻都可以作為精彩時(shí)刻的素材。能寫程序自動(dòng)截取這些素材嗎?答案是肯定的。
實(shí)現(xiàn)效果 彈幕抓取 數(shù)據(jù)統(tǒng)計(jì) 根據(jù)彈幕和禮物高峰生成的精彩剪輯 實(shí)現(xiàn)思路通過爬蟲抓取 B 站直播間數(shù)據(jù),找出彈幕激增的時(shí)間點(diǎn),使用 FFmpeg 自動(dòng)剪輯時(shí)間點(diǎn)前后的視頻即可。
本文代碼:GitHub
> bilibili-live-crawler $ tree -L 2 . ├── README.md ├── config.php # 配置文件:配置 FFmpeg 可執(zhí)行文件的位置,錄像的保存路徑 ├── const.php # 常量文件:API 地址,定義數(shù)據(jù)庫用戶名和密碼、彈幕激增的判定參數(shù)等 ├── crawler.php # 連接并抓取彈幕服務(wù)器的數(shù)據(jù) ├── cut_words │?? └── seg.php # 分詞腳本:將彈幕做分詞處理,可用于生成本次直播的詞圖 ├── db.sql # 數(shù)據(jù)存儲(chǔ) ├── edit.php # 剪輯腳本 ├── functions.php # 公用函數(shù) └── visual_data.php # 直播數(shù)據(jù)可視化文件腳本準(zhǔn)備 API
以 B 站欠王癢局長(zhǎng)為例,進(jìn)入他的 528 直播間,打開 Chrome 的開發(fā)者工具,看 Network 容易找出這些 API:
直播間原始信息熱門主播會(huì)有 2 個(gè)房間號(hào):易識(shí)記的短房間號(hào)、原始長(zhǎng)房間號(hào),獲取主播原始直播間信息的 API:
Resquest: https://api.live.bilibili.com/room/v1/Room/room_init?id=528 Response: { "code": 0, "data": { "room_id": 5441, // 開通直播間時(shí)的原始房間號(hào),后邊會(huì)用到 "short_id": 528, // 短房間號(hào) ... } }彈幕服務(wù)器信息
直播間在加載時(shí),會(huì)請(qǐng)求彈幕服務(wù)器的地址,即是我們要去爬取數(shù)據(jù)的服務(wù)器:
Request: https://api.live.bilibili.com/api/player?id=cid:5441 // 5441 即原始房間號(hào) Response: ...直播推流信息2243 broadcastlv.chat.bilibili.com ...
直播間會(huì)有 3~4 個(gè)視頻推流地址,選用第一個(gè)主路線會(huì)更穩(wěn)定:
Request: https://api.live.bilibili.com/room/v1/Room/playUrl?cid=5441 Response: { "code": 0, "data": { "durl": [ { "order": 1, "length": 0, "url": "https://bvc.live-play.acgvideo.com/live-bvc/671471/live_322892_3999292.flv?wsSecret=55083259fbc34c4227691ca0feb9c4b8&wsTime=1522465545" // flv 視頻格式的推流地址 }, ... }協(xié)議分析
B 站和斗魚一樣,為傳輸直播數(shù)據(jù)自己設(shè)計(jì)了協(xié)議頭部。需使用 Wireshark 抓包分析協(xié)議的細(xì)節(jié),才能將爬蟲的請(qǐng)求偽裝成瀏覽器的請(qǐng)求,連接彈幕服務(wù)器去爬取直播間的數(shù)據(jù)。
找出彈幕服務(wù)器的 IP 地址:211.159.194.115
查看請(qǐng)求彈幕服務(wù)器的數(shù)據(jù)包:ip.addr == 211.159.194.115
前邊三個(gè)包是我(10.0.1.34)與彈幕服務(wù)器(211.159.194.115)三次握手建立 TCP 連接的包。
請(qǐng)求的打包和解碼,我參考 2016.3 的博客:B站直播彈幕協(xié)議詳解,現(xiàn)在抓到的包協(xié)議頭與博客中的不一樣,B站重新修改過了,不過應(yīng)該是為了兼容,這種舊協(xié)議頭還能用。
請(qǐng)求協(xié)議頭下邊這個(gè)是打開進(jìn)入直播間時(shí),客戶端請(qǐng)求彈幕服務(wù)器的請(qǐng)求協(xié)議頭,響應(yīng)協(xié)議頭類似:
00000000 00 00 00 35 00 10 00 01 00 00 00 07 00 00 00 01 ...5.... ........ # 數(shù)據(jù)包長(zhǎng)度 # 意義不明 #請(qǐng)求類型:7進(jìn)入直播間 # 包類型,1是數(shù)據(jù)包 # 2是心跳包 00000010 7b 22 72 6f 6f 6d 69 64 22 3a 31 30 31 36 2c 22 {"roomid ":1016," 00000020 75 69 64 22 3a 31 35 35 39 37 33 36 38 35 37 32 uid":15597368572 00000030 38 31 36 30 7d 8160} # 請(qǐng)求的數(shù)據(jù)
進(jìn)入直播間,打包生成連接服務(wù)器的協(xié)議頭:
// $roomID 是直播間的長(zhǎng)房間號(hào) // $uid 是當(dāng)前登錄用戶的 uid,游客的是隨機(jī)數(shù) function packMsg($roomID, $uid) { $data = json_encode(["roomid" => $roomID, "uid" => $uid]); // 大端字節(jié)序,使用參數(shù) N (4字節(jié)) 和 n(2字節(jié)) 打包請(qǐng)求 // 占4字節(jié)的數(shù)據(jù)包長(zhǎng)度:16字節(jié)協(xié)議頭長(zhǎng)度 + 請(qǐng)求數(shù)據(jù)長(zhǎng)度 // 占2字節(jié)意義不明:00 10 // 占2字節(jié)意義不明:00 01 // 占4字節(jié)的請(qǐng)求類型:00 00 00 07 // 占4字節(jié)的包類型:00 00 00 01 return pack("NnnNN", 16 + strlen($data), 16, 1, 7, 1) . $data; }響應(yīng)數(shù)據(jù)
服務(wù)返回的 JSON 數(shù)據(jù)包的協(xié)議頭如下:
00000835 7b 22 69 6e 66 6f 22 3a 5b 5b 30 2c 31 2c 32 35 {"info": [[0,1,25 00000845 2c 31 36 37 37 37 32 31 35 2c 31 34 35 37 39 35 ,1677721 5,145795 ... 000008E5 5d 2c 22 63 6d 64 22 3a 22 44 41 4e 4d 55 5f 4d ],"cmd": "DANMU_M 000008F5 53 47 22 7d SG"}
解碼響應(yīng)的數(shù)據(jù)體:
function decodeMessage($socket) { while (socket_last_error($socket)) { while ($out = socket_read($socket, 16)) { $res = @unpack("N", $out); if ($res[1] != 16) { break; } } $message = @socket_read($socket, $res[1] - 16); $resp = json_decode($message, true); switch ($resp["cmd"]) { case "DANMU_MSG": // 彈幕消息 // info[1] 彈幕內(nèi)容 // info[2][1] 發(fā)送者昵稱 echo $resp["info"][2][1] . " : " . $resp["info"][1] . PHP_EOL; break; case "SEND_GIFT": // 直播間送禮物信息 $data = $resp["data"]; // uname 發(fā)送者的昵稱 // giftName 贈(zèng)送的禮物名稱 // unum 一次贈(zèng)送的數(shù)量 // price 禮物的價(jià)值 echo $data["uname"] . " 贈(zèng)送" . $data["num"] . "份" . $data["giftName"] . PHP_EOL; break; case "WELCOME": // 直播間歡迎信息 break; default: // 未知的消息類型 } } socket_close($socket); }心跳包
如果客戶端出現(xiàn)突然斷網(wǎng)等異常情況,服務(wù)端依舊會(huì)繼續(xù)推送數(shù)據(jù),維護(hù)這種半打開的 TCP 連接將會(huì)浪費(fèi)服務(wù)器的資源??蛻舳丝梢悦扛粢恍《螘r(shí)間給服務(wù)端發(fā)送心跳包來?;?,如果服務(wù)端一定超時(shí)時(shí)間內(nèi)沒收到某個(gè)客戶端的心跳包,就主動(dòng)斷開連接。
B 站的彈幕服務(wù)器也有類似的機(jī)制,隨便打開一個(gè)未開播的直播間,抓包將看到每隔 30s 左右會(huì)給服務(wù)端發(fā)送一個(gè)心跳包,協(xié)議頭第四部分的值從 7 修改為 2 即可。如果不發(fā)送心跳包,彈幕服務(wù)器將在 1~2min 內(nèi)主動(dòng)斷開連接。
// 發(fā)送心跳包 function sendHeartBeatPkg($socket) { // 包類型從數(shù)據(jù)包的 7 修改為心跳包的 2 $str = pack("NnnNN", 16, 16, 1, 2, 1); socket_write($socket, $str, strlen($str)); }錄播并剪輯精彩時(shí)刻
錄播:直接使用 FFmpeg 保存推流地址的視頻即可
剪輯:根據(jù) 每分鐘 的彈幕數(shù)量變化情況,如果出現(xiàn)峰值,取峰值 前后的一分鐘 作為精彩部分。
峰值的判斷標(biāo)準(zhǔn):
對(duì)癢局長(zhǎng)這樣的大主播:直播間人很多,玩出甩狙瞬狙這種騷操作彈幕會(huì)激增很多,比如是前一分鐘的三倍
對(duì)小主播:人一般比較少,彈幕數(shù)量波動(dòng)不大,出現(xiàn)精彩操作時(shí)也漲幅也不大
以上加粗的判定粒度、判定標(biāo)準(zhǔn)都可根據(jù)自己喜歡的主播修改,具體參考 edit.php 的實(shí)現(xiàn)。還可以為精彩時(shí)刻加上彈幕判定,比如分析是否有大量的 666、233、基本操作、學(xué)不來之類的詞集中出現(xiàn)等等。
彈幕分析參考 結(jié)巴分詞 的算法,可用于生成直播的詞圖、分析粉絲的習(xí)慣用語等等。我參考的教程:
結(jié)巴分詞1—結(jié)巴分詞系統(tǒng)介紹
結(jié)巴分詞2--基于前綴詞典及動(dòng)態(tài)規(guī)劃實(shí)現(xiàn)分詞
總結(jié)開發(fā)遇到了兩個(gè)難點(diǎn)
協(xié)議頭部:參考的博客里邊逆向 B 站官方 C# 版客戶端代碼,分析協(xié)議組成,感謝博主 lyyyuna
分詞算法:參考的是結(jié)巴分詞的前綴詞典與動(dòng)態(tài)規(guī)劃算法,算法能力待提升 :(
再看看人家斗魚,有開放使用的 《斗魚彈幕服務(wù)器第三方接入?yún)f(xié)議》,協(xié)議也不會(huì)修改,興趣使然寫了 B 站的爬蟲,這種根據(jù)彈幕峰值剪輯視頻的想法應(yīng)用在斗魚上,估計(jì)會(huì)更有價(jià)值 :)
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/28518.html
摘要:不過因?yàn)楦鱾€(gè)平臺(tái)互相挖人的關(guān)系,導(dǎo)致關(guān)注的一些主播分散到了各個(gè)直播平臺(tái),來回切換有點(diǎn)麻煩,所以萌生了做一個(gè)視頻聚合站的想法。后續(xù)我們會(huì)對(duì)這三個(gè)部分的功能做逐一展開說明。正則處理要求比較高,但是幾乎能應(yīng)對(duì)所有的情況,屬于大殺器。 前言 作為一個(gè)爐石傳說玩家,經(jīng)常有事沒事開著直播網(wǎng)站看看大神們的精彩表演。不過因?yàn)楦鱾€(gè)平臺(tái)互相挖人的關(guān)系,導(dǎo)致關(guān)注的一些主播分散到了各個(gè)直播平臺(tái),來回切換有點(diǎn)麻...
摘要:作者微信公眾號(hào)的皮卡丘歡迎大家搜索關(guān)注知乎搶火車票爬網(wǎng)易云音樂爬網(wǎng)易云課堂爬豆瓣爬直播平臺(tái)彈幕爬美團(tuán)爬百度爬抖音爬騰訊爬音悅臺(tái)爬酷狗爬站綜合總結(jié)類其他歡迎關(guān)注我的微信公眾號(hào)的皮卡丘,不定期分享相關(guān)的項(xiàng)目 作者:Charles微信公眾號(hào):Charles的皮卡丘(歡迎大家搜索關(guān)注)知乎:https://zhuanlan.zhihu.com/p/... python搶火車票https://...
摘要:月日,第六屆大會(huì)在深圳召開。這是這次大會(huì)的第二站活動(dòng),第一站已在上海成功舉辦。深圳站視頻及,請(qǐng)?jiān)诠娞?hào)后臺(tái)回復(fù),獲取分享鏈接。據(jù)介紹,目前支持多種開發(fā)庫,如內(nèi)置和等。該協(xié)議的推出,是為了統(tǒng)一標(biāo)準(zhǔn),提高效率。 本文為 PyChina 和「編程派」聯(lián)合首發(fā),作者為 EarlGrey?!妇幊膛伞故且粋€(gè)專注 Python 學(xué)習(xí)交流的微信公眾號(hào)。 9 月 25 日,第六屆 PyCon China...
摘要:前提本項(xiàng)目地址如果需要,可以到本地打開可直接查看爬蟲數(shù)據(jù)目標(biāo)爬取斗魚正在直播的主播數(shù)據(jù)房間號(hào),在線人數(shù),房間標(biāo)題,主播名稱,直播分類等等依賴構(gòu)建安裝包的應(yīng)用程序框架小型漸進(jìn)式客戶端請(qǐng)求庫,和模塊具有相同的,具有許多高級(jí)客戶端功能可以 前提 本項(xiàng)目github地址:https://github.com/janyin/dou...如果需要,可以clone到本地 $ npm install ...
閱讀 893·2019-08-30 15:54
閱讀 467·2019-08-30 12:51
閱讀 2062·2019-08-29 16:28
閱讀 2870·2019-08-29 16:10
閱讀 2363·2019-08-29 14:21
閱讀 439·2019-08-29 14:09
閱讀 2161·2019-08-23 16:13
閱讀 1261·2019-08-23 13:59