脚本地址:
项目地址: Gazer
BiliGrab.py
提要
适用于: 登录状态下, 非大会员视频下载.
自动解析任意 B 站非大会员 / 付费视频的视频 & 音频请求链接并下载, 需要添加 Cookie 保证视频清晰度. 使用 FFmpeg 命令无损合并视频和音频.
使用方法
- 克隆或下载项目代码.
- 安装依赖:
pip install requests
, 或者克隆项目代码后 pip install -r requirements.txt
- 脚本顶部: 指定常量
FOLDER_PATH
, 保存视频和音频文件夹路径 - 主函数处: 填写你的
cookie
- 主函数处: 指定
url
, 要下载的视频 URL - 主函数处: 指定
video_file_name
和 audio_file_name
, 要保存的视频和音频文件名
代码结构
get_url
从 HTML 中的 window.__playinfo__
提取解析视频的 2 个请求 URLdownload_video
接收 get_url
的返回链接, 下载视频/音频文件
抓包
登录状态下, 随机打开哔哩哔哩的一个视频, 打开 devtool, 播放一会视频, 让请求一一列出. 然后在 Network
面板的过滤器中, 输入 m4s
, 过滤出与视频相关的请求. 点 Size
从大到小排列.

m4s
过滤可以同时过滤出音频和视频, Type 都是 application/octet-stream
. 这证明了音频和视频数据都是以 m4s 格式存储的, 并且使用了相同的 MIME 类型.
请求为 ...100026...
的是视频请求网址, 请求为 ...30280...
的是音频请求网址.
点开请求可以看到, Status Code: 206 Partial Content, 表示服务器成功处理了部分 GET 请求, 而且说明视频内容是一部分一部分传输的.
复制视频/音频的请求 URL, 分别使用 requests
get 一下, 能够成功下载完整视频和音频.
代码示例 (下载单个 m4s 片段):
importrequestsvideo_url ="Request_URL"headers ={ "Referer":"https://www.bilibili.com/","User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36",}response =requests.get(video_url,headers=headers,stream=True)ifresponse.status_code ==200orresponse.status_code ==206:withopen("video_part.m4s","wb")asf:forchunk inresponse.iter_content(chunk_size=8192):ifchunk:f.write(chunk)print("下载成功!")else:print("下载失败!")print(response.status_code)
自动解析视频和音频的请求 URL
现在这个脚本只能自己通过 devtool 来查看请求的 URL 然后复制到脚本再下载, 那么如何做到我们只需要输入 B 站视频的地址, 即可自动获取它的 2 个请求 (视频和音频) 的 URL 呢?
分析 B 站视频页面的 HTML 结构:
- 找到包含视频和音频 URL 的 HTML 元素或 JavaScript 变量.
- 通常, 视频和音频的 URL 会以某种形式嵌入在网页中, 例如:
- 在
标签的 src
属性中(但 B 站通常不是这样). - 在 JavaScript 变量中(例如一个名为
window.__playinfo__
的变量, 这是 B 站常用的). - 在 JSON 数据中(例如一个包含视频信息的 JSON 对象).
play_info
的提取:
- 原先使用了
soup.select_one('head > script:nth-child(58)')
来定位包含 play_info
的
标签. 这种方法依赖于
标签在 HTML 中的具体位置(第 58 个子元素). 如果 B 站的网页结构发生变化, 这个选择器可能会失效. - 更可靠的方法是使用正则表达式
window\.__playinfo__\s*=\s*({ .*?"video":.*?"audio":.*?"session":.*?})\s*
来提取 window.__playinfo__
变量的值. - 一些有用的网站
- https://regex101.com/ 检查正则表达式能否正确匹配到所有的数据
- https://jsonformatter.org/json-pretty-print 美化 HTML 中的单行 json 格式
- https://jsonlint.com/ 验证 json 格式是否正确
正则表达式详解
window\.__playinfo__\s*=\s*({ .*?"video":.*?"audio":.*?"session":.*?})\s*
这个正则表达式从 HTML 源代码中提取 B 站视频页面的 window.__playinfo__
变量的值. window.__playinfo__
是一个 JavaScript 变量, 它包含了视频和音频的播放信息(例如 URL、清晰度、编码等), 通常以 JSON 格式存储.
详细解释:
window\.__playinfo__
: 匹配字符串 “window.__playinfo__”.
window
: 匹配字面上的 “window”.\.
: 匹配一个点号 “.”. 由于点号在正则表达式中有特殊含义(匹配任意字符), 所以需要使用反斜杠 \
进行转义.__
: 匹配两个下划线 “_”.playinfo
: 匹配字面上的 “playinfo”
\s*
: 匹配零个或多个空白字符(空格、制表符、换行符等).
=
: 匹配等号 “=”.
\s*
: 匹配零个或多个空白字符.
({ .*?})
: 这是整个正则表达式的核心, 用于捕获 window.__playinfo__
变量的值(JSON 对象).
(
和 )
: 定义一个捕获组, 用于提取匹配到的内容.{
和 }
: 匹配大括号, 表示 JSON 对象的开始和结束..*?
: 匹配任意字符(除了换行符), *?
表示非贪婪模式, 尽可能少地匹配字符. .*?"video":
: 匹配任意字符直到 "video":
出现..*?"audio":
: 匹配任意字符直到 "audio":
出现..*?"session":
: 匹配任意字符直到"session"
出现..*?
: 匹配任意字符直到 }
.
\s*
: 匹配在结尾大括号后的零个或多个空白字符.
在 Python 中使用(示例):
importreimportjsonhtml ="""
清华大学新闻中心版权所有,清华大学新闻网编辑部维护,电子信箱: news@tsinghua.edu.cn
Copyright 2001-2020 news.tsinghua.edu.cn. All rights reserved.