V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
evenIfAlsoGo
V2EX  ›  程序员

疑问:请问如何解压 B 站弹幕文件?

  •  
  •   evenIfAlsoGo ·
    evgo2017 · 2019-07-15 08:16:43 +08:00 · 4785 次点击
    这是一个创建于 1740 天前的主题,其中的信息可能已经有所发展或是发生改变。

    这个问题困扰了两三天,目前一步一步分析出来应该是解压的问题。请问此文件到底是什么解压方式?该如何通过工具解压?

    文件示例: https://api.bilibili.com/x/v1/dm/list.so?oid=71163662

    部分 Header:

    Content-Encoding: deflate

    Content-Type: text/xml

    Transfer-Encoding: chunked

    Accept-Encoding: gzip, deflate, br

    最初:采用 Aria2 下载时,已添加 --http-accept-gzip,但依旧报错,incorrect header check

    Exception: [AbstractCommand.cc:350] errorCode=1 URI=https://api.bilibili.com/x/v1/dm/list.so?oid=71163662 -> [GZipDecodingStreamFilter.cc:110] errorCode=1 libz::inflate() failed. cause:incorrect header check

    已尝试的方法:

    ① curl -O url --compressed,成功解压,文件正常显示!唯一成功的途径。

    ② bandzip 为文件损坏,gzip 为 not in gzip format

    ③ fs.readFile('1.gz', function(err, data){ console.log(data) })

    Buffer 94 bd 5b 73 5c 49 72 26 f8 57 30 78 18 d3 8c 38 47 e1 11 ee 1e 11 36 25 68 6a ba 5b 9a b5 51 4b 6d dd db 3b ad 7d 59 4b 24 12 dd 1c 75 b1 ca ea 22 55 ...

    ④ node.js:Gunzip/Inflate, Error: incorrect header check

    const fs = require('fs'), zlib = require('zlib') fs.createReadStream('./1.gz').pipe(zlib.createGunzip()).pipe(fs.createWriteStream('1.xml')) console.log("文件解压完成。")

    希望大家可以帮忙看看,感激不尽!

    第 1 条附言  ·  2019-08-16 00:27:06 +08:00

    一打岔忘记备注了。问题已解决。

    这个问题的原因有两点:

    ① B站返回的 deflate 不是标准的!而是 raw deflate,它没有被 zlib 包装,这是错误的。

    ② Aria2 软件问题。它不支持对于 raw deflate 的解压。

    此问题出自我写 bilibili_video_download 时,下载视频同时下载弹幕,以及之后可更新弹幕的目标。

    最终的解决方案:用 node.js 写了一个小型服务器,zlib.createInflateRaw(),同时完成了更新弹幕是累加(不删除旧弹幕)的目标。

    更多内容请查看项目内的 CHANGELOG_Zh_CN.md 文件(7月20号附近)的记录,以及相关提交代码时的描述。相关文章:HTTP 协议中的 Content-Encodingdeflate——过时的网页压缩格式,最好禁用[转]

    谢谢大家的帮助。

    34 条回复    2019-07-16 01:21:28 +08:00
    ztcaoll222
        1
    ztcaoll222  
       2019-07-15 08:24:25 +08:00
    这不是个 xml 文件吗, 不能直接解析吗
    mystrylw
        2
    mystrylw  
       2019-07-15 08:28:37 +08:00
    +1 浏览器直接打开识别为 xml 了
    evenIfAlsoGo
        3
    evenIfAlsoGo  
    OP
       2019-07-15 08:29:36 +08:00
    @ztcaoll222 主要是想根据 URL 把文件下载下来用
    evenIfAlsoGo
        4
    evenIfAlsoGo  
    OP
       2019-07-15 08:30:31 +08:00
    @mystrylw 不通过浏览器,比如使用 aria2 进行下载
    ztcaoll222
        5
    ztcaoll222  
       2019-07-15 08:35:58 +08:00
    @evenIfAlsoGo #3 你下载下来的文件不能当 xml 解析吗
    evenIfAlsoGo
        6
    evenIfAlsoGo  
    OP
       2019-07-15 08:41:38 +08:00
    @ztcaoll222 直接 ctrl+s 是 xml,但是使用下载工具通过 URL 下载就不可以。忘记说明了:只通过 URL 下载下来的文件是 46K 左右,解压后可正常查看的文件是 114KB。
    littlewing
        7
    littlewing  
       2019-07-15 08:47:29 +08:00 via iPhone
    header 不全
    des
        8
    des  
       2019-07-15 08:50:20 +08:00 via Android   ❤️ 1
    第一,curl 和浏览器能正常下载和打开,说明你的下载方式有问题
    第二,not in gzip format 以及各种解压报错说明这个文件根本不是 gzip 文件,我不知道为啥你坚信这是个 gz 文件,不信你自己用 file 命令看看
    第三,你也不熟悉 http,因为根本就不是压缩文件,那个压缩只是传输过程中的压缩,是透明的,不需要你处理。个人猜测是你把 Accept-Encoding: gzip, deflate, br 这个 header 复制进去了,然后 aria2 还不支持
    littlewing
        9
    littlewing  
       2019-07-15 08:51:29 +08:00 via iPhone
    Accept-Encoding 头去掉
    darlinghsu
        10
    darlinghsu  
       2019-07-15 08:52:56 +08:00   ❤️ 1
    油猴上 有关于 哔哩哔哩的脚本?/哔哩哔哩助手扩展程序 可以下载 xml 字幕 和 ass 字幕 可以看看
    ztcaoll222
        11
    ztcaoll222  
       2019-07-15 08:52:59 +08:00   ❤️ 1
    @evenIfAlsoGo #6
    伪造个 ua 就可以了
    des
        12
    des  
       2019-07-15 09:02:17 +08:00
    @evenIfAlsoGo 抱歉,事先没有测试就妄下结论了,希望不要介意
    expy
        13
    expy  
       2019-07-15 09:03:22 +08:00   ❤️ 1
    大概是这个:Transfer-Encoding: chunked 要先解码,在解压。

    https://en.wikipedia.org/wiki/Chunked_transfer_encoding
    dingyx99
        14
    dingyx99  
       2019-07-15 09:14:47 +08:00
    B 站弹幕不是 xml 嘛。。而且获取弹幕不是直接通过 comment.bilibili.com/aid.xml 就能直接拖下来的吗?
    zsdroid
        15
    zsdroid  
       2019-07-15 09:20:06 +08:00   ❤️ 1
    php 是世界上最好的语言
    ```
    $url = 'https://api.bilibili.com/x/v1/dm/list.so?oid=71163662';
    $data = file_get_contents($url);
    $data = gzinflate($data);
    var_dump($data);
    ```
    lzvezr
        16
    lzvezr  
       2019-07-15 09:20:55 +08:00 via iPhone
    用 nodejs 了为什么不直接用 request 模块呢
    evenIfAlsoGo
        17
    evenIfAlsoGo  
    OP
       2019-07-15 09:22:39 +08:00
    @littlewing 去掉也是的,采用 IDM 下载是相同的文件。可能是下载工具的原因,刚用其他工具下载,没有问题。可能是里面有 transfer-encoding: chunked,部分工具不支持。
    evenIfAlsoGo
        18
    evenIfAlsoGo  
    OP
       2019-07-15 09:58:56 +08:00
    @des 你说的基本没错,我的方向有错误,问题不是解压方式而是下载工具,这个过程应该不需要我 /用户的参与。不过我没有坚信它是 gzip,要不也不会问是什么方式。此处我的思路出现了矛盾,它本身是 chunked+gzip,我还问怎么解压干啥...一直看 gzip 迷了,而且觉得应该我来处理(当然多学一些也不错)。此外又刚才测试了其他工具,成功下载。

    不过目前我对 HTTP 的理解确实不够扎实,谢谢你的回复。
    evenIfAlsoGo
        19
    evenIfAlsoGo  
    OP
       2019-07-15 10:04:44 +08:00
    @expy 对的,IDM 和 Aria2 可能不支持这种方式,所以导致这个问题的出现。并且我标题应该问如何用工具解析,而不是如何解压。
    evenIfAlsoGo
        20
    evenIfAlsoGo  
    OP
       2019-07-15 10:07:29 +08:00
    @dingyx99 需要通过下载工具和 URL 来下载,不采用浏览器的方式。获取弹幕这两个 API 都可以,目前网站采用的是 list.so?oid=cid 这个。
    evenIfAlsoGo
        21
    evenIfAlsoGo  
    OP
       2019-07-15 10:11:48 +08:00
    @zsdroid 哈哈,其实用 JS 的 XMLHttpRequest 也可以直接获取到内容(手动滑稽)
    evenIfAlsoGo
        22
    evenIfAlsoGo  
    OP
       2019-07-15 10:14:14 +08:00
    @lzvezr 因为我是导出所有下载链接的 URL,然后采用工具批量创建对应文件夹、重命名和下载的。具体你可以看看这个: https://github.com/evgo2017/bilibili_video_download
    evenIfAlsoGo
        23
    evenIfAlsoGo  
    OP
       2019-07-15 10:15:20 +08:00
    lzvezr
        24
    lzvezr  
       2019-07-15 10:57:12 +08:00 via iPhone
    @evenIfAlsoGo 但是你说的这些 nodejs 都能做到啊
    evenIfAlsoGo
        25
    evenIfAlsoGo  
    OP
       2019-07-15 11:14:29 +08:00
    @lzvezr 是的,只是大部分用户应该不会直接使用 node.js ,所以需要选择适合的方案和下载工具(后续添加浏览器直接下载),考虑到用户体验就需要做的更多一些。
    jinliming2
        26
    jinliming2  
       2019-07-15 12:05:04 +08:00 via iPhone
    curl -I -XGET https://api.bilibili.com/x/v1/dm/li st.so?oid=71163662
    HTTP/1.1 200 OK
    Date: Mon, 15 Jul 2019 04:01:06 GMT
    Content-Type: text/xml
    Transfer-Encoding: chunked
    Connection: keep-alive
    Bili-Trace-Id: 3c3183c3895d2bfa:3c3183c3895d2bfa:0:0
    Content-Encoding: deflate
    Last-Modified: Mon, 15 Jul 2019 12:02:12 GMT
    X-Cache-Webcdn: BYPASS from cds-uswest-webcdn-v6-01

    由此可知,他们不是用 gzip 压缩的,而是 deflate,你用 gunzip 解压当然失败!
    jinliming2
        27
    jinliming2  
       2019-07-15 12:11:53 +08:00 via iPhone   ❤️ 1
    curl -I -XGET [ V 站禁止外链???] -H "Accept-Encoding: gzip"
    HTTP/1.1 200 OK
    Date: Mon, 15 Jul 2019 04:06:30 GMT
    Content-Type: text/xml
    Transfer-Encoding: chunked
    Connection: keep-alive
    Bili-Trace-Id: d9c73fea75d2bfc:d9c73fea75d2bfc:0:0
    Content-Encoding: deflate
    Last-Modified: Mon, 15 Jul 2019 12:07:36 GMT
    X-Cache-Webcdn: BYPASS from cds-uswest-webcdn-v6-01

    由此可知,他们会忽略你给的 Accept-Encoding 头,固执的继续使用 deflate 压缩。
    shansing
        28
    shansing  
       2019-07-15 12:34:21 +08:00
    @jinliming2 我也注意到 Content-Encoding 是 deflate 了。但是这个能说是服务端忽略了 Accept-Encoding 头吗?因为 Accept-Encoding 中也包含了 deflate,所以也是没问题的吧。倒是优先级,不知道有没有问题,但肯定不是在前面的优先级高,因为 br 更高级,服务端实现一般会优先返回 brotli 压缩的内容,而它却是写在 Accept-Encoding 最后的。
    Paladinfeng
        29
    Paladinfeng  
       2019-07-15 12:47:13 +08:00   ❤️ 1
    你可以去 Github 搜一个项目叫 bilibili-api

    /**
    * 弹幕文件解析器.
    * 弹幕文件(list.so)有三个部分
    * 第一个部分为一个 Int, 表示第二部分的长度
    * 第二部分为一个 Json, 标识各个弹幕的等级(用于屏蔽设置)
    * 第三部分为一个 gzip 压缩过的 xml
    *
    * Web 端的弹幕是一个明文 xml, 与 APP 的接口是不一样的.
    *
    * json 部分形如 {"dmflags":[{"dmid":12551893546958848,"flag":10}],"rec_flag":1,"rec_text":"开启后,全站视频将按等级等优化弹幕","rec_switch":1}
    * xml 部分形如 <d p="12509048833835076,0,117373,5,25,16777215,1551001292,0,d2c5fc5">硬核劈柴</d>
    *
    * @see com.hiczp.bilibili.api.danmaku.DanmakuAPI.list
    */
    evenIfAlsoGo
        30
    evenIfAlsoGo  
    OP
       2019-07-15 13:08:06 +08:00
    @jinliming2
    @shansing 我采用 curl --http1.0 想去掉 chunked 看看是否是这个原因,是不成功的,依旧是 HTTP/1.1
    evenIfAlsoGo
        31
    evenIfAlsoGo  
    OP
       2019-07-15 13:11:03 +08:00
    @Paladinfeng 好的,谢谢!
    shansing
        32
    shansing  
       2019-07-15 13:20:51 +08:00   ❤️ 1
    @evenIfAlsoGo 我尝试请求 Accept-Encoding 不包含 deflate 甚至要求不压缩都不成功,可能服务端放弃这些向下兼容的东西了。
    evenIfAlsoGo
        33
    evenIfAlsoGo  
    OP
       2019-07-15 17:27:38 +08:00
    @shansing 谢谢!
    jinliming2
        34
    jinliming2  
       2019-07-16 01:21:28 +08:00 via iPhone   ❤️ 1
    @shansing #28 看我的第二个回复,里面我指定了 Accept-Encoding 为 gzip 了,不包含 deflate,但服务器依旧以 deflate 进行回复,可见服务端是忽略这个头的……
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   2719 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 00:21 · PVG 08:21 · LAX 17:21 · JFK 20:21
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.