运行环境
- 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": "灵文姐姐保我期中高分",...