python3网络爬虫--最新爬取B站视频弹幕 so文件(附源码)
作者:mmseoamin日期:2023-12-20

文章目录

  • 一.前言
  • 二.配置Protobuf 环境&生成编译文件
    • 1.配置Protobuf 环境
    • 2.生成编译文件
    • 三.解析弹幕
    • 四.自动解析弹幕
    • 五.总结
    • 六.参考

      本篇博文记录一下爬取小破站弹幕的主要思路以及完整代码

      一.前言

      审核求过

      小破站在2023年将弹幕接口的返回值从.xml改成了.so文件

      比如下面这个地址:

      https://api.bilibili.com/x/v2/dm/wbi/web/seg.so?type=1&oid=1258114431&pid=575703555&segment_index=1&pull_mode=1&ps=0&pe=120000&web_location=1315873&w_rid=fec78ad870a48b68b35024304ba8460f&wts=1694223505

      返回值示例:

      在这里插入图片描述

      很明显部分数据是被加密了。

      二.配置Protobuf 环境&生成编译文件

      1.配置Protobuf 环境

      通过搜索知道了,这种格式叫做**Protobuf **,这个格式为二进制编码传输

      Protobuf(Protocol Buffers)是一种轻量级的数据序列化协议,由Google开发。它可以用于结构化数据的序列化和反序列化,常用于网络通信、数据存储和配置文件等场景。

      Protobuf使用简洁的语法定义数据结构,然后通过编译器生成相应的代码,用于在不同的编程语言中进行数据的序列化和反序列化操作。相比于其他序列化协议,Protobuf具有更高的性能和更小的数据体积。

      使用Protobuf,你可以定义消息的字段类型、字段名称和字段顺序等信息,然后通过编译器生成的代码来进行数据的读写操作。Protobuf支持多种编程语言,包括C++、Java、Python等,因此可以在不同的平台和语言之间进行数据的传输和交换。

      总的来说,Protobuf协议是一种高效、灵活和可扩展的数据序列化协议,适用于各种场景下的数据交换和存储需求。

      简单来说就是一种比XML还轻量的数据。

      需要解密这种格式数据需要我们下载Protobuf 的编译器(我的电脑是windows64位的,直接下载win64位即可)

      https://github.com/protocolbuffers/protobuf/releases/tag/v3.17.3

      在这里插入图片描述

      下载完成后,解压出来

      在这里插入图片描述

      其中bin目录为可执行程序存放目录,我们把它加到环境变量里来:

      win10的操作步骤是:

      右击“此电脑”-高级系统设置-环境变量-双击path-新建-输入值-确定在这里插入图片描述

      在cmd中输入protoc来验证我们的配置是否成功,如果你的控制台输出结果和我的一样,那么恭喜你,环境配置成功

      在这里插入图片描述

      2.生成编译文件

      首先要去下载dm.proto

      https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/grpc_api/bilibili/community/service/dm/v1/dm.proto

      然后在控制台中输入

      protoc --python_out=. dm.proto
      

      在这里插入图片描述

      就会在同目录下生成一个dm_pb2.py的文件

      在这里插入图片描述

      这个文件很关键。

      三.解析弹幕

      将上一步编译出来的dm_pb2.py文件放在脚本的同一级,这里演示解析本地.so文件

      撰写代码

      import dm_pb2
      from google.protobuf import text_format
      my_seg = dm_pb2.DmSegMobileReply()
      with open('./seg.so', 'rb') as f:
          DATA = f.read()
      my_seg.ParseFromString(DATA)
      parse_data = text_format.MessageToString(my_seg.elems[0], as_utf8=True)
      print(parse_data)
      

      输出结果

      在这里插入图片描述

      四.自动解析弹幕

      这里本人贡献出一种自动解析弹幕,输入视频的BVID即可

      import json
      import requests
      import google.protobuf.text_format as text_format
      import dm_pb2 as Danmaku
      import re
      class BEngine():
          """
          bilibili引擎
          """
          def __init__(self):
              self.headers = {
                  "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36"}
          def do_request(self, url):
              headers = {
                  "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36"
              }
              r = requests.get(url, headers=headers)
              if r.status_code == 200:
                  r.encoding = 'utf-8'
                  return r.text
              else:
                  return False
          def get_video_cid(self, bvid):
              """
              通过bvid获取cid
              :param bvid:
              :return:
              """
              api_url = f'https://api.bilibili.com/x/web-interface/view?bvid={bvid}'
              try:
                  html = self.do_request(api_url)
                  if html:
                      _json = json.loads(html)
                      cid = _json['data'].get('cid')
                      return cid
                  else:
                      return False
              except:
                  return False
          def bvid_to_avid(self, bvid):
              """
              通过bvid获取avid
              :param bvid:
              :return:
              """
              table = 'fZodR9XQDSUm21yCkr6zBqiveYah8bt4xsWpHnJE7jL5VG3guMTKNPAwcF'
              tr = {}
              for i in range(58):
                  tr[table[i]] = i
              s = [11, 10, 3, 8, 4, 6]
              xor = 177451812
              add = 8728348608
              def dec(x):
                  r = 0
                  for i in range(6):
                      r += tr[x[s[i]]] * 58 ** i
                  return (r - add) ^ xor
              return dec(bvid)
          def get_danmu(self, avid, cid):
              """
              通过so文件获取解密后的弹幕列表
              :return:
              """
              result = []
              url = 'http://api.bilibili.com/x/v2/dm/web/seg.so'
              params = {
                  'type': 1,  # 弹幕类型
                  'oid': cid,  # cid
                  'pid': avid,  # avid
                  'segment_index': 1  # 弹幕分段
              }
              resp = requests.get(url, params, headers=self.headers)
              data = resp.content
              danmaku_seg = Danmaku.DmSegMobileReply()
              danmaku_seg.ParseFromString(data)
              for j in danmaku_seg.elems:
                  parse_data = text_format.MessageToString(j, as_utf8=True)
                  result.append(parse_data.replace("\n", ",").rstrip(","))
              print(result)
              return result
          def parse_danmu(self, danmu_list):
              """
              解析出每个弹幕列表内容
              :param danmu_list:
              :return:
              """
              result = []
              for each_dm in danmu_list:
                  res = re.findall(
                      '''id: \d+,progress: (\d+),mode: (\d+),fontsize: (\d+),color: (\d+),midHash: "(.*?)",content: "(.*?)",ctime: (\d+),weight: (\d+),idStr: "(\d+)"''',
                      each_dm)
                  if res and len(res[0]) == 9:
                      item = {
                          "progress": res[0][0],
                          "mode": res[0][1],
                          "fontsize": res[0][2],
                          "color": res[0][3],
                          "midHash": res[0][4],
                          "content": res[0][5],
                          "ctime": res[0][6],
                          "weight": res[0][7],
                          "idStr": res[0][8],
                      }
                      result.append(item)
                  else:
                      continue
              return result
          def getdanmu_format(self, bvid):
              """
              弹幕直接格式化
              :param bvid:
              :return:
              """
              avid = e.bvid_to_avid(bvid)
              cid = e.get_video_cid(bvid)
              danmu_raw = self.get_danmu(avid, cid)
              return self.parse_danmu(danmu_raw)
      if __name__ == '__main__':
          e = BEngine()
          bvid = "BV1Dz4y1L7hj"
          print(e.getdanmu_format(bvid))
      

      输出结果示例

      在这里插入图片描述

      五.总结

      本次通过调研protobuf协议通过搭建环境,使用Python撰写代码实现了对B战弹幕的解析,对于大多数人而言,可能搭建本地环境那里有些难,在此奉上封装好的dm_pb2.py文件点击下载,大家放在自己的脚本同级目录下即可。最后祝大家玩得开心能给点个赞么?

      在这里插入图片描述

      六.参考

      小破站弹幕 Protobuf 格式解析

      Python实现对Bilibili视频点赞等信息的爬取

      小破站弹幕 so文件解析/逆序列化

      python进行小破站av号和bv号的转换