運行環境
- python 3.7.4
目標
- 解析Bilibili彈幕
不知道從什么時候開始,嗶哩嗶哩的彈幕就變成ProtoBuf的格式了,如果對這個格式不了解,就會覺得爬下來的是一堆亂碼,難以處理。
由於最近期末論文是個爬蟲,所以就整理了一下解析的方法,發出來分享。
主要之前查找資料時,沒有看到過python解析的方法,所以分享一下。其它語言可以到github上搜DmSegMobileReply
,就會有其它語言的方法。
介紹一下ProtoBuf
2008年,可能更早,Google推出了Protocol Buffers。那時候我還小,不太清楚這段歷史,網上也沒有很多資料,有了解過的大佬還奢望能科普一下。
總之,這是一種數據格式,可以類比於xml、json等。
我們要解析一份ProtoBuf數據,需要一份參照文檔,也就是消息定義文檔,這可以類比於對象(class)。
后台通常用一份.proto為擴展名的文件來定義消息,而前端則以json格式來存放這份參照文檔。Bilibili的參照文檔是這樣子的。
{
"DmSegMobileReply": {
"fields": {
"elems": {
"rule": "repeated",
"type": "DanmakuElem",
"id": 1
}
}
},
"DanmakuElem": {
"fields": {
"id": {"type": "int64","id": 1},
"progress": {"type": "int32","id": 2},
"mode": {"type": "int32","id": 3},
"fontsize": {"type": "int32","id": 4},
"color": {"type": "uint32","id": 5},
"midHash": {"type": "string","id": 6},
"content": {"type": "string","id": 7},
"ctime": {"type": "int64","id": 8},
"weight": {"type": "int32","id": 9},
"action": {"type": "string","id": 10},
"pool": {"type": "int32","id": 11},
"idStr": {"type": "string","id": 12},
"attr": {"type": "int32","id": 13}}}
}
Bilibili為什么使用ProtoBuf取代原先的xml,大概是為了壓縮彈幕數據,提高彈幕上限吧。
至於ProtoBuf為什么可以減少數據量,可以看其它博客或者官方文檔。
以《天官賜福》為例做彈幕解析
一、爬取數據
url = "https://api.bilibili.com/x/v2/dm/web/seg.so?type=1&oid=253627085&pid=712552253&segment_index=1"
import requests
with open("./message","wb") as f:
f.write(requests.get(url).content)
沒有解析的數據如下:
L������H��
(���220d3f395: 嗚嗚嗚@����Hb40756202282418179p���
f�������HǑ
(���22f369812:$為你戰死是至高無上的榮耀@����Hb40757157776326659p�ޱU
a�������H�� (���29d69092a:靈文姐姐保我期中高分@����H b40758306907619333p����
二、解析字幕
1、安裝protoc
注. macOS brew install應該就可以了。以下介紹的安裝方法不一定簡便,而且更多針對的是linux,僅供參考。
我是直接下載的源碼,然后編譯,不過需要c++的編譯環境和一些相關工具。如果不想安裝的話,就可以直接在github上下載release版本。
這里稍微介紹一下怎么安裝,先克隆倉庫。
git clone --depth=1 https://github.com.cnpmjs.org/protocolbuffers/protobuf.git
考慮到github在國內特別慢,建議用鏡像並且只克隆最近一次commit,不過安全性需要自己考量一下。
接下來按照github上的這個教程完成如下幾步。
-
安裝編譯環境和工具(這是linux系統下的方法)
sudo apt-get install autoconf automake libtool curl make g++ unzip
-
修改子模塊的倉庫路徑
cd <protobuf下載路徑>/protobuf vim .gitmodules
替換url,修改完如下:
[submodule "third_party/benchmark"] path = third_party/benchmark url = https://github.com.cnpmjs.org/google/benchmark.git [submodule "third_party/googletest"] path = third_party/googletest url = https://github.com.cnpmjs.org/google/googletest.git ignore = dirty
-
初始化
cd <protobuf下載路徑>/protobuf git submodule update --init --recursive ./autogen.sh
-
安裝protoc
cd <protobuf下載路徑>/protobuf ./configure make make check sudo make install sudo ldconfig
-
驗證
protoc --version
如果安裝成功,就會顯示版本號。
2、安裝python第三方庫 - google.protobuf
參照這個教程
cd <protobuf下載路徑>/protobuf/python
python setup.py build
python setup.py test
python setup.py install
3、生成python解析文件
因為我們已經有一個json格式的消息定義,所以現在需要把它轉成.proto為后綴的文件,命名為dm.proto。轉化完的文件如下:
syntax = "proto3";
package dm;
message DmSegMobileReply{
repeated DanmakuElem elems=1;
}
message DanmakuElem{
int64 id = 1;
int32 progress = 2;
int32 mode = 3;
int32 fontsize = 4;
uint32 color = 5;
string midHash = 6;
string content = 7;
int64 ctime = 8;
int32 weight = 9;
string action = 10;
int32 pool = 11;
string idStr = 12;
}
利用protoc工具將其編譯為python解析文件。
protoc --python_out=./ ./dm.proto
4、解析彈幕
執行完上面語句,就會拿到一份dm_pb2.py文件,我們需要在解析字幕的時候引用他。使用方法如下:
from dm_pb2 import DmSegMobileReply
from google.protobuf.json_format import MessageToJson,Parse
import json
DM = DmSegMobileReply()
with open("./message","rb") as f:
DM.ParseFromString(f.read())
with open("./message.json","w") as f:
f.write(json.dumps(json.loads(MessageToJson(DM)),ensure_ascii=False))
最后的解析結果:
{"elems": [{"id": "40756202282418179", "progress": 222379, "mode": 1, "fontsize": 25, "color": 16777215, "midHash": "20d3f395", "content": "嗚嗚嗚", "ctime": "1604878461", "weight": 1, "idStr": "40756202282418179"}, {"id": "40757157776326659", "progress": 215239, "mode": 1, "fontsize": 25, "color": 16777215, "midHash": "2f369812", "content": "為你戰死是至高無上的榮耀", "ctime": "1604880284", "weight": 1, "idStr": "40757157776326659"}, {"id": "40758306907619333", "progress": 348984, "mode": 1, "fontsize": 25, "color": 16777215, "midHash": "9d69092a", "content": "靈文姐姐保我期中高分", "ctime": "1604882475", "weight": 9, "idStr": "40758306907619333"}, {"id": "40758308418093059", "progress": 325174, "mode": 1, "fontsize": 25, "color": 16777215, "midHash": "155c80d8", "content": "靈文姐姐保我們期中考高分✧(≖ ◡ ≖✿)", "ctime": "1604882478", "weight": 1, "idStr": "40758308418093059"}, {"id": "40758855406714887", "progress": 346591, "mode": 1, "fontsize": 25, "color": 16777215, "midHash": "b22a34e2", "content": "靈文姐姐保我期中高分",...