1831 字
9 分钟
自己搭一个 bilibili 视频下载工具(后端 BBDown 方案)

自己搭一个 bilibili 视频下载工具#

我一直想要一个能解析 bilibili 视频并转格式下载的工具,最好能塞到博客上做个网页版,或者直接让助手(Hermes)帮我下载。折腾了一圈,踩了不少坑,最后用 BBDown 后端方案跑通了。这篇文章把整个过程、技术结论、以及为什么”纯前端直拉走不通”讲清楚,希望对想搞类似工具的人有帮助。

一、需求#

  • 网页上粘贴 bilibili 链接 / BV 号,能解析视频并转格式下载
  • 支持下载 MP4 视频 / M4A 音频
  • 尽量别占用太多服务器流量
  • 一开始还纠结过 ffmpeg 体积太大,以及要不要支持 YouTube

二、先验证一个”想当然”的方案:纯前端直拉#

一开始的想法很美好:浏览器前端解析视频链接 → 直接拉视频流 → 用 ffmpeg.wasm 本地合并。这样视频完全不走服务器,最省流量。

但实测下来,这条路被 bilibili 的防盗链 + CORS 双重锁死:

1. API 层 CORS 全拦截#

api.bilibili.com 的所有接口对第三方跨域请求直接 OPTIONS 403,公共 CORS 代理也全军覆没:

Terminal window
# 跨域预检
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"
# → 403

2. 视频 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.com
fetch(streamUrl, { referrer: "https://www.bilibili.com/" });
// 实测仍是 403
fetch(streamUrl, { referrer: "https://www.bilibili.com/", referrerPolicy: "unsafe-url" });
// 仍是 403

浏览器出于安全模型,不允许 JS 把跨域请求的 Referer 设为第三方域名fetchreferrer 选项只能设本站或同源,跨域伪造会被浏览器忽略/降级。所以从你博客页面发起的请求,Referer 永远是 noyorin.top → CDN 必 403。

结论:纯前端直拉 CDN 物理上走不通。这不是 ffmpeg 大小的问题,是 bilibili 防盗链 + 浏览器安全基线的双重封锁。

三、换思路:后端 BBDown 方案#

既然浏览器直拉不行,那就让后端去下载。关键的转折是发现 BBDown——一个开源的 bilibili 下载器,它是命令行/桌面程序,可以带任意 Referer,所以能在服务器端绕过防盗链。

实测 BBDown 能在服务器上真正下载#

在 Linux 服务器上部署 BBDown 后,实测它能:

  • 解析 bilibili 视频(拿标题、cid、画质档)
  • 带登录 cookie 下载视频 / 音频
  • 自动把分离的音视频合并成 MP4
  • 自动处理 CDN 镜像替换(防盗链)
Terminal window
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/,就自动跑一次脚本,让脚本判断到底能不能下载,而不是靠助手猜话题。脚本会返回识别结果 + 成品文件,助手直接把文件发给你。

Terminal window
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),要么靠浏览器扩展/更底层的工具。

如果你也想搞类似的下载工具,记住这条结论能省很多试错时间。


工具已上线:noyorin.top/tools/bili-downloader