自己搭一个 bilibili 视频下载工具
我一直想要一个能解析 bilibili 视频并转格式下载的工具,最好能塞到博客上做个网页版,或者直接让助手(Hermes)帮我下载。折腾了一圈,踩了不少坑,最后用 BBDown 后端方案跑通了。这篇文章把整个过程、技术结论、以及为什么”纯前端直拉走不通”讲清楚,希望对想搞类似工具的人有帮助。
一、需求
- 网页上粘贴 bilibili 链接 / BV 号,能解析视频并转格式下载
- 支持下载 MP4 视频 / M4A 音频
- 尽量别占用太多服务器流量
- 一开始还纠结过 ffmpeg 体积太大,以及要不要支持 YouTube
二、先验证一个”想当然”的方案:纯前端直拉
一开始的想法很美好:浏览器前端解析视频链接 → 直接拉视频流 → 用 ffmpeg.wasm 本地合并。这样视频完全不走服务器,最省流量。
但实测下来,这条路被 bilibili 的防盗链 + CORS 双重锁死:
1. API 层 CORS 全拦截
api.bilibili.com 的所有接口对第三方跨域请求直接 OPTIONS 403,公共 CORS 代理也全军覆没:
# 跨域预检curl -X OPTIONS "https://api.bilibili.com/x/player/playurl?bvid=BV...&fnval=4048" \ -H "Origin: https://noyorin.top" -H "Access-Control-Request-Method: GET"# → 4032. 视频 CDN 防盗链锁死 Referer 域名
即使拿到了视频流 URL,bilivideo.com 的 CDN 会校验 Referer:
| 请求的 Referer | 结果 |
|---|---|
https://www.bilibili.com/... | ✅ 206(放行) |
https://noyorin.top | ❌ 403 |
| 空 / 任意假域名 | ❌ 403 |
也就是说,防盗链不是只验”有没有 Referer”,而是锁死了”必须来自 bilibili.com 域名”。
3. 浏览器无法伪造跨域 Referer(关键死穴)
这是最致命的一点。我用 fetch 尝试伪造 Referer:
// 期望 CDN 看到 Referer 是 bilibili.comfetch(streamUrl, { referrer: "https://www.bilibili.com/" });// 实测仍是 403
fetch(streamUrl, { referrer: "https://www.bilibili.com/", referrerPolicy: "unsafe-url" });// 仍是 403浏览器出于安全模型,不允许 JS 把跨域请求的 Referer 设为第三方域名。fetch 的 referrer 选项只能设本站或同源,跨域伪造会被浏览器忽略/降级。所以从你博客页面发起的请求,Referer 永远是 noyorin.top → CDN 必 403。
结论:纯前端直拉 CDN 物理上走不通。这不是 ffmpeg 大小的问题,是 bilibili 防盗链 + 浏览器安全基线的双重封锁。
三、换思路:后端 BBDown 方案
既然浏览器直拉不行,那就让后端去下载。关键的转折是发现 BBDown——一个开源的 bilibili 下载器,它是命令行/桌面程序,可以带任意 Referer,所以能在服务器端绕过防盗链。
实测 BBDown 能在服务器上真正下载
在 Linux 服务器上部署 BBDown 后,实测它能:
- 解析 bilibili 视频(拿标题、cid、画质档)
- 带登录 cookie 下载视频 / 音频
- 自动把分离的音视频合并成 MP4
- 自动处理 CDN 镜像替换(防盗链)
BBDown -c <cookie> --work-dir <目录> https://www.bilibili.com/video/BV1iH4y1f7ic# 输出: 【官方MV】Never Gonna Give You Up - Rick Astley.mp4 (27MB, h264+aac)为什么后端能行,前端不行?
| 浏览器前端 | BBDown 后端 | |
|---|---|---|
| 能带 bilibili Referer? | ❌ 浏览器不给 | ✅ 完全可控 |
| 能过 CDN 防盗链? | ❌ 403 | ✅ 206 |
| 画质上限 | 匿名低清 | 登录更高 |
BBDown 是桌面程序语境,发出的 HTTP 请求 Header 完全由自己控制,不受浏览器安全模型约束。
四、最终架构
浏览器网页 (noyorin.top/tools/bili-downloader/) │ 粘贴链接 / 选MP4/M4A ▼nginx 反代 /api/bili/download ▼Flask API (:8094) ──调用──> BBDown 下载脚本 bili_dl.py │ 下载并合并音视频 ▼/opt/bbdown/dl/xxx.mp4 ──> /dl/ 静态服务 ──> 用户拿到直链- 后端:Flask API + systemd 常驻,调
bili_dl.py下载 - 前端:单页 HTML,输入链接 → 选格式 → 展示下载直链
- 默认画质 720P(够用省流量,用户明确说没必要超高清)
五、踩的几个坑
坑 1:文件名含中文导致 500
BBDown 输出的文件名是中文(如 赵雷-鼓楼.mp4),我一开始的 SAFE_NAME 正则只允许 [A-Za-z0-9._-],把中文拒了,报 “bad filename from downloader”。改成允许 CJK 即可:
SAFE_NAME = re.compile(r"^[^\x00/\\]{1,200}$")坑 2:BBDown “文件已存在跳过下载”
如果下载目录里已有同名输出文件,BBDown 会检测到并跳过视频下载,只下了音频,导致最终只有 m4a 没有视频。修复:每次用独立的临时工作目录(tempfile.mkdtemp)下载,完成后再 move 到输出目录,避免文件冲突。
坑 3:网站 build 偶发 postcss 报错
pnpm build 偶发 [vite:css] [postcss] ... The link class does not exist,这是@apply 引用自定义类的已知偶发问题,重跑一次就过,不用改代码。
六、成品效果
网页端实测:输入 BV1iH4y1f7ic → 点下载 → 进度到 100% → 出现直链 https://noyorin.top/dl/赵雷-鼓楼.mp4(25.9MB,h264+aac),点开即可下载。
也支持音频:?type=audio 输出 .m4a。
七、再加一招:让助手自动识别并下载
除了网页,我还给自家助手(Hermes)配了一个 skill,让它能自动识别用户消息里的 bilibili 标识并直接下载,不用用户明说”下载”。
核心是一个识别脚本 bili_auto.py,能处理这几种输入形态:
| 用户输入的形态 | 识别逻辑 | 结果 |
|---|---|---|
完整链接 bilibili.com/video/BVxx | 正则提取 BV 段 | 直接下载 |
裸 BV 号 BV1iH4y1f7ic | 自动补全成完整链接 | 转链接后下载 |
| 聊天里夹链接/BV号 | 从任意文本正则提取,忽略其它乱码 | 提取后下载 |
b23.tv 短链 | 跟随重定向解析真实视频 | 下载 |
# bili_auto.py 里的识别正则BV_RE = re.compile(r"\bBV[0-9A-Za-z]{10}\b") # 裸 BV 号LINK_RE = re.compile(r"https?://(?:www\.|m\.)?bilibili\.com/video/(BV\w{10})", re.I)调用的方式是:只要消息里匹配到 BV\w{10} 或 bilibili.com/video/ 或 b23.tv/,就自动跑一次脚本,让脚本判断到底能不能下载,而不是靠助手猜话题。脚本会返回识别结果 + 成品文件,助手直接把文件发给你。
python3 /opt/bbdown/bili_auto.py "<用户消息原文>" # 自动识别+下载实测:发一句”帮我下载这个 https://www.bilibili.com/video/BV1iH4y1f7ic 谢谢”就能自动识别出链接、下载出 mp4 成品。退一步,哪怕只发个 BV1iH4y1f7ic,也会自动先转成完整链接再下载。
这样整个工具闭环了:网页版给人用、skill 给助手用,两条入口共用同一个后端 BBDown 下载引擎。
八、局限 & 现状
- 只支持 bilibili,不支持 YouTube(DRM 无解)
- 画质受视频源本身档位限制:老视频/搬运视频可能最高只有 480P,账号权限再高也拿不到更高——这是源的问题,不是工具问题
- 下载走服务器流量(免费流量 20GB/月),大视频会费流量
- 默认 720P,可传
quality覆盖 - 目前只有假进度条(后端同步下载无真实进度流),真实进度反馈是已知的下一步优化点
九、总结
这个项目最有价值的其实不是最终成品,而是搞清楚了一个硬边界:bilibili 的防盗链 + 浏览器安全模型决定了”纯前端直拉 CDN”这条路永远走不通。想绕过防盗链,要么用能自定义 HTTP 头的后端程序(BBDown),要么靠浏览器扩展/更底层的工具。
如果你也想搞类似的下载工具,记住这条结论能省很多试错时间。