摘要:站的彈幕服務器也有類似的機制,隨便打開一個未開播的直播間,抓包將看到每隔左右會給服務端發送一個心跳包,協議頭第四部分的值從修改為即可。
原文:B 站直播間數據爬蟲, 歡迎轉載
項目地址:bilibili-live-crawler
去年在 B 站發現一個后期超強的 UP 主:修仙不倒大小眼,專出 PDD 這樣知名主播的吃雞精彩集錦,漲粉超快。于是想怎么做這樣的 UP,遇到的第一個問題便是素材,精彩時刻需要手動從直播錄播中剪輯,很低效。
用戶習慣我經常看直播,但是很少發彈幕和送禮物,只有在主播玩出很溜的操作或講很好玩的事情時,才會發彈幕互動、送禮物支持,經常看直播的室友也是如此。
基于這個用戶習慣,不難推斷出在直播間的彈幕高峰或禮物高峰期,主播應該做了些好玩的事情,比如吃到雞了,或者全隊被殲滅之類的…這些時刻都可以作為精彩時刻的素材。能寫程序自動截取這些素材嗎?答案是肯定的。
實現效果 彈幕抓取 數據統計 根據彈幕和禮物高峰生成的精彩剪輯 實現思路通過爬蟲抓取 B 站直播間數據,找出彈幕激增的時間點,使用 FFmpeg 自動剪輯時間點前后的視頻即可。
本文代碼:GitHub
> bilibili-live-crawler $ tree -L 2 . ├── README.md ├── config.php # 配置文件:配置 FFmpeg 可執行文件的位置,錄像的保存路徑 ├── const.php # 常量文件:API 地址,定義數據庫用戶名和密碼、彈幕激增的判定參數等 ├── crawler.php # 連接并抓取彈幕服務器的數據 ├── cut_words │?? └── seg.php # 分詞腳本:將彈幕做分詞處理,可用于生成本次直播的詞圖 ├── db.sql # 數據存儲 ├── edit.php # 剪輯腳本 ├── functions.php # 公用函數 └── visual_data.php # 直播數據可視化文件腳本準備 API
以 B 站欠王癢局長為例,進入他的 528 直播間,打開 Chrome 的開發者工具,看 Network 容易找出這些 API:
直播間原始信息熱門主播會有 2 個房間號:易識記的短房間號、原始長房間號,獲取主播原始直播間信息的 API:
Resquest: https://api.live.bilibili.com/room/v1/Room/room_init?id=528 Response: { "code": 0, "data": { "room_id": 5441, // 開通直播間時的原始房間號,后邊會用到 "short_id": 528, // 短房間號 ... } }彈幕服務器信息
直播間在加載時,會請求彈幕服務器的地址,即是我們要去爬取數據的服務器:
Request: https://api.live.bilibili.com/api/player?id=cid:5441 // 5441 即原始房間號 Response: ...直播推流信息2243 broadcastlv.chat.bilibili.com ...
直播間會有 3~4 個視頻推流地址,選用第一個主路線會更穩定:
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 視頻格式的推流地址 }, ... }協議分析
B 站和斗魚一樣,為傳輸直播數據自己設計了協議頭部。需使用 Wireshark 抓包分析協議的細節,才能將爬蟲的請求偽裝成瀏覽器的請求,連接彈幕服務器去爬取直播間的數據。
找出彈幕服務器的 IP 地址:211.159.194.115
查看請求彈幕服務器的數據包:ip.addr == 211.159.194.115
前邊三個包是我(10.0.1.34)與彈幕服務器(211.159.194.115)三次握手建立 TCP 連接的包。
請求的打包和解碼,我參考 2016.3 的博客:B站直播彈幕協議詳解,現在抓到的包協議頭與博客中的不一樣,B站重新修改過了,不過應該是為了兼容,這種舊協議頭還能用。
請求協議頭下邊這個是打開進入直播間時,客戶端請求彈幕服務器的請求協議頭,響應協議頭類似:
00000000 00 00 00 35 00 10 00 01 00 00 00 07 00 00 00 01 ...5.... ........ # 數據包長度 # 意義不明 #請求類型:7進入直播間 # 包類型,1是數據包 # 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} # 請求的數據
進入直播間,打包生成連接服務器的協議頭:
// $roomID 是直播間的長房間號 // $uid 是當前登錄用戶的 uid,游客的是隨機數 function packMsg($roomID, $uid) { $data = json_encode(["roomid" => $roomID, "uid" => $uid]); // 大端字節序,使用參數 N (4字節) 和 n(2字節) 打包請求 // 占4字節的數據包長度:16字節協議頭長度 + 請求數據長度 // 占2字節意義不明:00 10 // 占2字節意義不明:00 01 // 占4字節的請求類型:00 00 00 07 // 占4字節的包類型:00 00 00 01 return pack("NnnNN", 16 + strlen($data), 16, 1, 7, 1) . $data; }響應數據
服務返回的 JSON 數據包的協議頭如下:
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"}
解碼響應的數據體:
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] 彈幕內容 // info[2][1] 發送者昵稱 echo $resp["info"][2][1] . " : " . $resp["info"][1] . PHP_EOL; break; case "SEND_GIFT": // 直播間送禮物信息 $data = $resp["data"]; // uname 發送者的昵稱 // giftName 贈送的禮物名稱 // unum 一次贈送的數量 // price 禮物的價值 echo $data["uname"] . " 贈送" . $data["num"] . "份" . $data["giftName"] . PHP_EOL; break; case "WELCOME": // 直播間歡迎信息 break; default: // 未知的消息類型 } } socket_close($socket); }心跳包
如果客戶端出現突然斷網等異常情況,服務端依舊會繼續推送數據,維護這種半打開的 TCP 連接將會浪費服務器的資源。客戶端可以每隔一小段時間給服務端發送心跳包來保活,如果服務端一定超時時間內沒收到某個客戶端的心跳包,就主動斷開連接。
B 站的彈幕服務器也有類似的機制,隨便打開一個未開播的直播間,抓包將看到每隔 30s 左右會給服務端發送一個心跳包,協議頭第四部分的值從 7 修改為 2 即可。如果不發送心跳包,彈幕服務器將在 1~2min 內主動斷開連接。
// 發送心跳包 function sendHeartBeatPkg($socket) { // 包類型從數據包的 7 修改為心跳包的 2 $str = pack("NnnNN", 16, 16, 1, 2, 1); socket_write($socket, $str, strlen($str)); }錄播并剪輯精彩時刻
錄播:直接使用 FFmpeg 保存推流地址的視頻即可
剪輯:根據 每分鐘 的彈幕數量變化情況,如果出現峰值,取峰值 前后的一分鐘 作為精彩部分。
峰值的判斷標準:
對癢局長這樣的大主播:直播間人很多,玩出甩狙瞬狙這種騷操作彈幕會激增很多,比如是前一分鐘的三倍
對小主播:人一般比較少,彈幕數量波動不大,出現精彩操作時也漲幅也不大
以上加粗的判定粒度、判定標準都可根據自己喜歡的主播修改,具體參考 edit.php 的實現。還可以為精彩時刻加上彈幕判定,比如分析是否有大量的 666、233、基本操作、學不來之類的詞集中出現等等。
彈幕分析參考 結巴分詞 的算法,可用于生成直播的詞圖、分析粉絲的習慣用語等等。我參考的教程:
結巴分詞1—結巴分詞系統介紹
結巴分詞2--基于前綴詞典及動態規劃實現分詞
總結開發遇到了兩個難點
協議頭部:參考的博客里邊逆向 B 站官方 C# 版客戶端代碼,分析協議組成,感謝博主 lyyyuna
分詞算法:參考的是結巴分詞的前綴詞典與動態規劃算法,算法能力待提升 :(
再看看人家斗魚,有開放使用的 《斗魚彈幕服務器第三方接入協議》,協議也不會修改,興趣使然寫了 B 站的爬蟲,這種根據彈幕峰值剪輯視頻的想法應用在斗魚上,估計會更有價值 :)
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/28518.html
摘要:不過因為各個平臺互相挖人的關系,導致關注的一些主播分散到了各個直播平臺,來回切換有點麻煩,所以萌生了做一個視頻聚合站的想法。后續我們會對這三個部分的功能做逐一展開說明。正則處理要求比較高,但是幾乎能應對所有的情況,屬于大殺器。 前言 作為一個爐石傳說玩家,經常有事沒事開著直播網站看看大神們的精彩表演。不過因為各個平臺互相挖人的關系,導致關注的一些主播分散到了各個直播平臺,來回切換有點麻...
摘要:作者微信公眾號的皮卡丘歡迎大家搜索關注知乎搶火車票爬網易云音樂爬網易云課堂爬豆瓣爬直播平臺彈幕爬美團爬百度爬抖音爬騰訊爬音悅臺爬酷狗爬站綜合總結類其他歡迎關注我的微信公眾號的皮卡丘,不定期分享相關的項目 作者:Charles微信公眾號:Charles的皮卡丘(歡迎大家搜索關注)知乎:https://zhuanlan.zhihu.com/p/... python搶火車票https://...
摘要:月日,第六屆大會在深圳召開。這是這次大會的第二站活動,第一站已在上海成功舉辦。深圳站視頻及,請在公眾號后臺回復,獲取分享鏈接。據介紹,目前支持多種開發庫,如內置和等。該協議的推出,是為了統一標準,提高效率。 本文為 PyChina 和「編程派」聯合首發,作者為 EarlGrey。「編程派」是一個專注 Python 學習交流的微信公眾號。 9 月 25 日,第六屆 PyCon China...
摘要:前提本項目地址如果需要,可以到本地打開可直接查看爬蟲數據目標爬取斗魚正在直播的主播數據房間號,在線人數,房間標題,主播名稱,直播分類等等依賴構建安裝包的應用程序框架小型漸進式客戶端請求庫,和模塊具有相同的,具有許多高級客戶端功能可以 前提 本項目github地址:https://github.com/janyin/dou...如果需要,可以clone到本地 $ npm install ...
閱讀 795·2019-08-30 15:54
閱讀 439·2019-08-30 12:51
閱讀 2022·2019-08-29 16:28
閱讀 2842·2019-08-29 16:10
閱讀 2331·2019-08-29 14:21
閱讀 409·2019-08-29 14:09
閱讀 2127·2019-08-23 16:13
閱讀 1236·2019-08-23 13:59