轩_雨

青山不厌三杯酒,长日惟消一局棋
Bilibili_解析ProtoBuf格式的弹幕

运行环境

  • 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上的这个教程完成如下几步。

  1. 安装编译环境和工具(这是linux系统下的方法)

    sudo apt-get install autoconf automake libtool curl make g++ unzip
    
  2. 修改子模块的仓库路径

    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
    
  3. 初始化

    cd <protobuf下载路径>/protobuf
    git submodule update --init --recursive
    ./autogen.sh
    
  4. 安装protoc

    cd <protobuf下载路径>/protobuf
    ./configure
    make
    make check
    sudo make install
    sudo ldconfig
    
  5. 验证

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

posted on 2020-11-11 13:17  轩_雨  阅读(2056)  评论(0编辑  收藏  举报