辭職之后 休息了一段時間,最近准備開始恢復去工作的狀態了,所以搞點事情來練練手。由於沉迷b站女妝大佬想做個收集彈幕的然后根據彈幕自動回復一些彈幕的東西。網上搜了一下有個c#的版本,感覺還做得不錯,於是准備自己也搞一個,順便分析一下b站家的協議。
收集需要的信息:
我首先使用charles或者如果你是windows平台的話使用findder抓下http包。看下是否彈幕信息使用的是http api來進行傳輸的。抓了半天,我並沒有發現任何關於彈幕的信息,就可以判定沒有走http。其實仔細想下,彈幕是有可能非常大量的,例如某個房間刷了一個小電視。整個屏幕都飄着bilibili干杯。。如果是http協議進行拉取的話我估計后台服務器是遭不住。但是抓取http包依然是有意義的,有一些api肯定會回傳房間的信息,不然沒有辦法進行下面的工作。后面抓到的包也的確證實了我這個猜測。
來看下一個被捕捉到的有用的包里面的內容是什么:
https://api.live.bilibili.com
request: GET /api/player?id=cid:1250611&ts=15f2f17b037 HTTP/1.1 response: <uid></uid> # 用戶uid只有登陸之后的用戶才會返回這個,沒有登陸的用戶不會 <uname></uname> # 用戶昵稱,同上 <login></login> # 登陸狀態 <isadmin></isadmin> # 是否管理員 <time>1508301890</time> # 時間戳 <rank></rank> # 排名 <level></level> # 等級 <state>LIVE</state> # 這個應該是直播間狀態 <chatid>1250611</chatid> # 交談id 這個不太清楚做什么用 <server>livecmt-2.bilibili.com</server> # 服務器地址(特別注意的時這個服務器也可以接收到彈幕推送。應該是它們網站歷史遺留的現在dm服務器有專門的地址返回了) <sheid_user></sheid_user> # sheid這個不知道是啥 <block_time>0</block_time> # 這個應該是被封的時間 <block_type>0</block_type> # 被封禁的類型 <room_shield>1</room_shield> # 房間的sheid <level_sheid>0</level_sheid> # sheid的level <user_sheid_keyword></user_sheid_keyword> # 不知道。。 <room_silent_type></room_silent_type> # 這個應該是房間禁言類型 <room_silent_level>0</room_silent_level> # 禁言類型 <room_silent_second>0</room_silent_second> # 禁言秒數 <user_silent_level></user_silent_level> # 用戶禁言等級 <user_silent_rank></user_silent_rank> # 用戶禁言排名 <user_silent_verify></user_silent_verify> # 用戶禁言確認 <dm_ws_port>2244</dm_ws_port> # (重頭戲)彈幕服務器端口號 <dm_wss_port>2245</dm_wss_port> # 彈幕服務器端口號2 <dm_port>2243</dm_port> # 彈幕服務器端口號3 <dm_server>broadcastlv.chat.bilibili.com</dm_server> # 彈幕服務器地址 <need_authority>0</need_authority> # 是否需要認證 <authority_range>日本</authority_range> # 授權區域(這個應該是本機ip地址的地區) <forbidden>0</forbidden>
這是一個請求player詳情的api,方便理解我都標注了每個字段大概是什么意思。(我也是根據對比猜測。因為這不是開放api)
可以看到其實跟我們需要的彈幕相關的只有幾項,一個是彈幕服務器地址<dm_server> 還有就是dm_port 彈幕端口。彈幕端口最近新家了<dm_ws_port>我估計這個按字面意思來理解應該是html5 live 播放器可以直接使用websocket和服務器握手,然后接受彈幕推送。沒有測試過,這一部分不做深度討論了。
另外需要注意的一個api是:
https://api.live.bilibili.com/room/v1/Room/getRoomInfoMain?roomid=74723
這個是得到房間信息的api,可以得到用戶信息的api對比看看,發現它們的api設計真的有點分裂,感覺應該不是同一時期設計的api。
response: { "code": 0, "msg": "ok", "message": "ok", "data": { "MASTERID": 540217, "ANCHOR_NICK_NAME": "萬四屋", "ROOMID": 74723, "_status": "on", "LIVE_STATUS": "LIVE", "ROUND_STATUS": "1", "AREAID": "1", "BACKGROUND_ID": 4, "ROOMTITLE": "技術型蘿莉吃雞(。・ω・。)自走移動盒", "COVER": "https://i0.hdslb.com/bfs/live/74723.jpg?10191128", "LIVE_TIMELINE": 1508380144 } }
這里可以看到一個room_id。這個room_id其實蠻重要的,因為最開始我在對很多房間進行訪問的時候發現幾乎所有有人氣的主播都是用的短房間碼,但是這個三位數的短房間碼並不能作為傳輸數據使用,所以真正的房間好嗎是這里的room_id,我們后面在和服務器進行通信的時候會使用到這個。
既然http只能獲取到這些信息,那么我們使用wireshark來進一步查看當直播間打開之后發生了什么,以便作更詳細的分析。在osx平台下面第一次使用wireshark使用wifi抓包可能會被提示權限不足等說法
You don't have permission to capture on that device
使用sudo chmod 777 /dev/bpf*開放設備權限給wireshark使用就可以監聽到數據包了。
如果我們需要知道數據包發送和抓取相關包我們可能需要在wireshark里面過濾出來自己需要的數據。那么我們可以基於ip地址來過濾獲取數據。那么我們怎么知道該過濾哪個地址?其實蠻簡單的,我嘗試了一下用上面api返回的彈幕服務器地址,然后直接就ping出了地址183.240.17.138 常用彈幕地址還有另外一個ip地址 223.99.231.13
piperck➜ /dev ᐅ ping broadcastlv.chat.bilibili.com PING broadcastlv.chat.bilibili.com (183.240.17.138): 56 data bytes 64 bytes from 183.240.17.138: icmp_seq=0 ttl=50 time=49.544 ms --- broadcastlv.chat.bilibili.com ping statistics --- 1 packets transmitted, 1 packets received, 0.0% packet loss round-trip min/avg/max/stddev = 49.544/49.544/49.544/0.000 ms
拿到地址之后使用wireshark過濾出我們需要的包。下面讓我們來看數據:
前面三個包就是正常的tcp三次握手包。length為66字節的包是有填充tcp option選項的包,54字節是標准只包含頭的包。
我們來看第一個從本地發送到彈幕服務器的包里面有些什么內容。忽略前面的包頭我們從數據開始看:
00 00 00 37 這里我們可以看到數據包的length是55bytes 這里37hex應該對應十進制的正好是55,我們可以猜測這里放數據大小,可以多查看幾個其他包來證明這個推斷。
00 10這個我沒有看出來是啥。。大概是個 magic_number吧,先放着。
00 01 這個應該是版本號。
00 00 00 07 這個是進入房間的數據。單從這個包里面是看不出來這個意思的,要比較更多通訊包和其他包可以推斷。我先把結論放在這里。
00 00 00 01 這個是包類型。像這里就代表的一個數據認證 傳輸通訊類似這個意思的包,說明還在跟服務器交換信息,並不是一個正常的彈幕包之類的。與之相關的還有后面的心跳包。
除開前面這16字節用於交換數據(應該算是它們自己的協議了,用前面這16個字節交換一些需要的信息和數據)剩下的都是數據。我們需要模仿這個格式發送數據給b站的彈幕服務器告訴它們我們要去的房間號,uid這個參數如果是匿名登錄的話是不會有固定數值的是一個隨機數。因為我們弄彈幕機並不需要鎖定上我們的賬號的uid,所以這里可以就傳隨機數就行。
例如:
self._uid = u_id or int(100000000000000.0 + 200000000000000.0 * random.random())
在介紹下面數據傳輸相關的東西之前我必須說明一下。上面一定要正確握手,下面才能得到正常的數據。另外如果是抓取的網頁的,時間在2017年10月之后貌似它們又更新了,在傳輸的時候加上了ssl 現在已經抓不到我上面說的那些包了。我只看到一些什么賽門鐵克coperation,估計是加入了證書之類的東西。但是為了向下兼容上面說的方法依然可以獲取彈幕和相關的數據。另外要說的是,b站的app傳輸的數據和網站的也略微不一樣。如果是抓取的app的包,可以直接看到utf-8編碼的數據,而且可以直接解,但是以前網站上的似乎還進行了gzip,抓包的時候看到的數據也不是能直接utf-8解碼的數據要稍微麻煩一點。 這點我也是踩了坑,當時拿到無法看懂的數據也是一臉懵逼沒有想到還被壓縮了一次。
我將在下篇里面介紹彈幕傳輸的數據意義,以及心跳相關的東西。
相關代碼我已經放在了github上面:
https://github.com/piperck/b_danmu_chicken/tree/master