• 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": "灵文姐姐保我期中高分",...
    
  • 相关阅读:
    【mysql优化1】表的优化与列类型选择
    mysql 各数据类型的 大小及长度
    android如何建立数据库。(如何重写SQLiteOpenHelper)
    PHP中统计目录中文件以及目录中目录的大小
    如何下载coursera视频
    JAVAEE filter总结
    zookeeper安装
    如何从硬盘安装fedora 19 (How to install fedora 19 from hard drive, Fedora-19-i386-DVD.iso)
    WCF 项目应用连载[3]
    POJ1184-------操作分离的BFS
  • 原文地址:https://www.cnblogs.com/xuanyu-10-18/p/13956765.html
Copyright © 2020-2023  润新知