bili-course
Bilibili 课程字幕爬取 & 本地知识库构建系统
Bilibili URL → Course → Videos → Subtitles → Local Knowledge Base → AI Agent
把 Bilibili 上的计算机课程(MySQL、CS224N、深度学习、Python……)一键变成 本地 Markdown 知识库,直接交给 Claude Code / Codex / 任意 LLM Agent 学习。
为什么需要它
在 Bilibili 学课程,手动流程是:打开视频 → 复制字幕 → 粘贴给 AI → 下一节。 一门 42 节的课,重复 42 次。
本工具一条命令完成全部:
bili-course import "课程URL"
→ 自动发现全部视频(合集/播放列表/番剧/多 P)
→ 并发抓取字幕(限速 3 并发,自动重试,断点续传)
→ 标准化为 SRT + Markdown
→ 自动镜像进你的 Obsidian Vault
快速开始
# 1. 安装(需要 Python 3.12+)
pip install bili-course
# 2. 初始化全局配置(自动探测 Obsidian vault)
bili-course init
# 3. 登录(打开浏览器窗口,扫码/密码登录一次即可;会话存入本地配置)
bili-course login
# 4. 验证环境
bili-course doctor
# 5. 用起来(任意目录)
bili-course import "https://www.bilibili.com/video/BV1xxxxx"
bili-course subtitle BV1Kr4y1i7ru # 单个视频快速抓取
bili-course login 打开的浏览器是独立的空白会话:你自己在 Bilibili 官网
登录(密码/二维码都直接输给 B 站,工具看不到),之后工具只从这个窗口读取
会话 Cookie 存入 ~/.bili-course/.env。你的日常浏览器、密码库都不会被触碰。
(也可以手动配置,见下文「Cookie 配置」)
输出
工作区(~/.bili-course/data/courses/<slug>/,每节一组文件):
01-数据库介绍/
├── metadata.json
├── subtitle.json # 标准化字幕段 [{index, start, end, text}]
├── subtitle.srt # 播放器可用的标准字幕
└── transcript.md # 带时间戳的忠实转写(Agent 学习入口)
Obsidian 镜像(<vault>/Captions/<slug>/,扁平布局,一视频一文件):
├── course.md # 课程地图,链接指向下面每个文件
├── 01-数据库介绍.md
├── 02-SQL基础.md
└── 100-streamlit入门.md # 三位数页码保持排序
transcript.md 示例:
# 第 03 节:分组查询
> Course: MySQL 数据库入门到精通
> BVID: BVxxxxxxxx > CID: xxxxx > Duration: 32:15
> Subtitle: AI Generated (中文)
## Transcript
[00:00:00]
大家好,今天我们来学习分组查询。
[00:00:08]
首先我们来看 GROUP BY。
为什么要登录(Cookie)
2025 年起 Bilibili 将字幕列表改为登录态可见:匿名请求大多返回空列表和
need_login_subtitle=true。所以要拿到字幕,需要一个你自己的账号会话。
两种方式(选一):
| 方式 | 命令 | 说明 |
|---|---|---|
| 浏览器登录(推荐) | bili-course login |
打开独立浏览器窗口手动登录,自动保存会话 |
| 手动粘贴 | 编辑 ~/.bili-course/.env |
F12 → Network → 任意 api.bilibili.com 请求 → 复制 Cookie: 头的值填入 BILI_COOKIE |
安全说明,务必阅读:
- Cookie 等价于你的账号登录态,任何人拿到它都能以你的身份操作。
- 本工具只在你自己登录的会话中读取,绝不读取你的浏览器密码、绝不提取 其他浏览器保存的凭据、绝不绕过付费权限或访问控制。
- 日志系统会全局自动打码所有 Cookie 值;
.env文件在 Linux/macOS 上以 0600 权限创建;.gitignore已排除所有.env。 - Cookie 会过期(通常几个月),失效时重新
bili-course login即可,bili-course doctor可以随时检查会话有效性。
CLI 参考
| 命令 | 作用 |
|---|---|
bili-course import <URL> |
导入课程/合集/播放列表/多 P 视频,抓取全部字幕 |
bili-course subtitle <BVID|URL> |
单视频快速抓取 |
bili-course list |
列出所有课程 |
bili-course show <COURSE> |
课程详情 + 每节状态 |
bili-course status <COURSE> |
逐节导入状态 |
bili-course resume <COURSE> |
续传(自动跳过已完成节) |
bili-course fetch <COURSE> |
续传别名 |
bili-course retry <COURSE> |
重试失败节 |
bili-course export <COURSE> |
从数据库重建 course.json/course.md |
bili-course sync <COURSE> |
重新镜像课程到 Obsidian vault |
bili-course login |
浏览器登录,保存会话 Cookie |
bili-course init |
创建全局配置 ~/.bili-course/.env |
bili-course doctor |
环境诊断(安全输出,可直接贴进 issue) |
bili-course serve |
启动 FastAPI 服务 |
<COURSE> 可以是数字 id(list 里显示的)或 slug。
加 --debug 可显示完整堆栈。Ctrl-C 中断后 resume 从断点继续,
已成功的节不会重复请求。
支持的 URL 格式
| 类型 | 示例 |
|---|---|
| 单视频 | bilibili.com/video/BV1GJ411x7h7、...?p=2、bilibili.com/video/av170001 |
| 短链 | b23.tv/xxxxx(实测通过) |
| 合集 | space.bilibili.com/<mid>/channel/collectiondetail?sid=<sid>(实测通过) |
| 播放列表(收藏夹) | space.bilibili.com/<mid>/favlist?fid=<fid>(私密收藏夹需登录;待真实数据验证) |
| 番剧 | bilibili.com/bangumi/play/ep<id> / ss<id>(⚠ 实验性,见下) |
| 付费课程 | 识别但拒绝处理(不绕过付费墙) |
番剧(实验性):2026-09 实测 pgc/view/web/season 接口对 season/ep 参数
返回空数据(接口疑似改版),当前番剧 URL 会明确报错而不是产出空课程。
欢迎带具体番剧链接提 issue 协助适配新接口。
配置(.env)
| 变量 | 默认 | 说明 |
|---|---|---|
BILI_COOKIE / BILI_SESSDATA |
空 | 登录 Cookie(见上) |
OBSIDIAN_CAPTIONS_DIR |
空 | 绝对路径 <vault>/Captions;空 = 禁用镜像 |
MAX_CONCURRENCY |
3 | 并发数(请保持礼貌,勿调高) |
REQUEST_TIMEOUT |
15 | 单请求超时(秒) |
MAX_RETRIES |
3 | 请求级重试次数 |
REQUEST_INTERVAL |
0.4 | 客户端限速:请求最小间隔(秒) |
PREFERRED_LANGUAGES |
zh-CN,zh-Hans,zh,zh-Hant |
字幕语言偏好(按序) |
PREFER_MANUAL_SUBTITLES |
true | 人工字幕优先于 AI 字幕 |
OUTPUT_DIR / CACHE_DIR / DB_PATH |
~/.bili-course/data/... |
存储位置(与 CWD 无关) |
ENABLE_BROWSER_FALLBACK |
false | Playwright 兜底(实验性) |
BILI_WBI_MIXIN_KEY |
空 | WBI 密钥表轮换时的手动覆盖 |
配置查找顺序:环境变量 > ./.env(开发模式)> ~/.bili-course/.env(日常)。
字幕选择策略
一个视频可能有多条字幕,按明确优先级选择,而不是取 tracks[0]:
- 语言偏好(
PREFERRED_LANGUAGES顺序;ai-zh按 base 语言zh匹配) - 人工字幕 > AI 字幕(AI 转录可能听错技术术语,同语言层人工优先)
- 未锁定 > 锁定
- 列表原始顺序
FAQ
Q: 为什么提示 "requires a logged-in session"?
B 站从 2025 年起对匿名请求隐藏字幕列表。跑一次 bili-course login,或手动配置 Cookie。
Q: 报 403 / "风控校验失败" 怎么办?
程序会自动重试并升级 WBI 签名。若仍失败:降低并发(MAX_CONCURRENCY=1)、
增大 REQUEST_INTERVAL、确认 Cookie 有效(bili-course doctor)。
Q: Cookie 多久过期?
通常几个月。失效表现:所有视频都报登录相关错误。重新 bili-course login 即可。
Q: 为什么数据不在我的 OneDrive/网盘里?
默认数据目录 ~/.bili-course/data/ 刻意放在云同步目录之外——SQLite 被云同步
并发写入有损坏风险。真正的学习材料(课程 Markdown)会自动镜像进你的 Obsidian
vault(它通常在云同步里,但那是静态文件,同步安全)。
Q: 多 P 视频是什么? 一个 BV 号可以包含多个视频页(P1/P2/P3…),每一页有独立的 cid 和字幕。 本工具把它们展开为一门课程的多个小节。
Q: 字幕里能不能让 AI 帮我改写/总结? 本工具不会(忠实原则:原始转写必须可审计)。改写是下游 Agent 的事—— 把 transcript.md 交给 Claude Code 即可。
Q: 支持哪些平台?
主要开发与测试在 Windows;macOS / Linux 理论支持(路径与权限已做跨平台
处理)。如有问题,欢迎带着 bili-course doctor 的输出提 issue。
HTTP API(FastAPI)
bili-course serve # http://127.0.0.1:8000(/docs 有交互文档)
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /courses/import |
{"url": "..."} → 202 + course_id(后台处理) |
| GET | /courses |
课程列表 |
| GET | /courses/{id} |
课程详情 |
| GET | /courses/{id}/status |
导入状态(计数 + 逐节) |
| GET | /courses/{id}/videos |
节列表 |
| GET | /videos/{id} |
视频 + 字幕记录 |
| GET | /videos/{id}/subtitle |
字幕记录 |
| POST | /videos/{id}/retry |
重试单个失败视频 |
架构约定:API → Service → Repository,API 层不含业务逻辑。
架构
src/bili_course/
├── cli.py # CLI 入口(typer + rich)
├── config.py # 全部配置(pydantic-settings + 全局 .env)
├── logging.py # 日志 + 全局 Cookie 打码
├── errors.py # 类型化异常体系
├── service.py # 组合根:唯一依赖装配点
├── bilibili/ # ★ Bilibili 专用层(API 变了只改这里)
│ ├── urls.py # URL 解析(纯函数,零网络)
│ ├── http.py # 异步客户端:重试/退避/限速/缓存/自动 WBI
│ ├── wbi.py # WBI 签名(2026-09 实测验证)
│ ├── auth.py # Cookie 封装(永不落盘、掩码 repr)
│ ├── video.py # metadata(view/pagelist,多 P 感知)
│ ├── subtitle.py # 字幕轨道发现 + 下载
│ ├── selector.py # 轨道选择策略
│ └── course.py # 课程发现(合集/收藏夹/番剧/多 P)
├── models/ # 领域模型(Pydantic)
├── storage/ # SQLite / repository / filesystem / cache
├── pipeline/ # 业务管线(normalizer/exporter/downloader/tasks/importer)
├── browser/ # Playwright 兜底(可选依赖,惰性导入)
└── api/ # FastAPI(app factory)
设计原则:
- Bilibili 是 Source,不是系统的骨架——API 细节全部隔离在
bilibili/; - 领域模型是通用词汇表——未来接入别的视频源(YouTube 等)时, Course/Video/Subtitle/Task 不变,只需新增 adapter;
- 存储分两层——SQLite 是元数据索引,文件系统是知识本体;
即使删掉数据库,
data/courses/里的 Markdown 依然是完整知识库。
AI Agent 集成(设计)
当前:Agent(Claude Code / Codex)直接读文件系统:
courses/<slug>/course.md # 课程地图 → 引导 Agent 阅读顺序
courses/<slug>/<lesson>/transcript.md # 每节带时间戳的忠实转写
例如在课程目录里对 Claude Code 说:"读 course.md,学习第 03 节,出 5 道练习题"。
未来(接口已预留,勿过度实现):
- MCP Server —
ImportService/Repository已足够干净,直接包一层 MCP adapter 即可暴露search_course / get_course / list_lessons / get_lesson / get_transcript / search_transcript(Service 层零改动)。 - 学习管线 —
bili-course learn <COURSE> <LESSON>:读 transcript → 知识点解释 → 学习笔记 → 练习题 → 进度写入数据库。 - RAG / 向量库 — transcript.md 是干净的分段文本,直接切块嵌入; 时间戳保留在 Markdown 里,可回跳原视频。
忠实原则:transcript 阶段绝不做 LLM 改写/总结——原始转写必须可审计, 改写/笔记是下游 Agent 的事,不是摄取管线的责任。
开发与贡献
pip install -e ".[dev]"
pytest # 单元测试,零网络依赖(mock transport)
ruff check src tests scripts # lint
RUN_LIVE_TESTS=1 pytest tests/integration -v # 可选:真实网络冒烟
python scripts/probe_subtitles.py --selftest-wbi # 随时验证 WBI 表是否过期
单元测试覆盖:URL 解析、WBI 签名(已知向量)、多 P metadata、字幕轨道选择、 标准化、SRT/Markdown 生成、重试/限速、任务状态机、repository、cache、 API 路由、日志打码、Obsidian 镜像。fixtures 基于真实 API 响应的形状。
兼容性与稳定性
- Bilibili 接口会变——这是本项目最大的外部风险。对策:
- 所有 API 逻辑隔离在
bilibili/层,上游变更只改这一层; scripts/probe_subtitles.py是探测当前接口行为的调试工具;- WBI 密钥表已实测验证(2026-09-04),且可通过
.env热覆盖; tests/integration/用RUN_LIVE_TESTS=1随时对真实接口做冒烟。
- 所有 API 逻辑隔离在
- 浏览器兜底(Playwright)标记为实验性:需要
pip install "bili-course[browser]" && playwright install chromium。 - 已知现状(2026-09):字幕轨道列表对匿名请求基本不可见 → 需要 Cookie; 双栈网络下 Bilibili 的 IPv6 路径会被黑洞/风控(实测表现为 -404"啥都木有"), 本工具强制走 IPv4。
合规与免责声明
本项目的唯一目的:获取你自己正常有权访问的视频字幕,用于个人学习与 知识整理。不实现、不鼓励:绕过付费/权限限制、破解 DRM、窃取 Cookie 或密码、读取浏览器凭据、大规模攻击式抓取、规避平台安全措施。 我们内置限速、退避重试、缓存,以最小化对平台的负载。
本工具与 Bilibili 官方无任何隶属关系;请遵守 Bilibili 的服务条款, 使用者对自身行为负责。
Roadmap
- MVP:单视频 → 字幕 → SRT/Markdown
- 课程发现(合集/播放列表/番剧/多 P)+ 任务系统(并发/重试/resume)
- CLI + FastAPI + 单元测试 + Obsidian 镜像
- 全局配置 + login/doctor 命令(v0.1)
- MCP Server adapter
-
bili-course learn学习管线 - RAG / 向量索引(基于 transcript.md)
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file bili_course-0.1.1.tar.gz.
File metadata
- Download URL: bili_course-0.1.1.tar.gz
- Upload date:
- Size: 73.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
06f29da77781957c943eb73780d948526fca3923c85d548e19b4b55f2c4ee945
|
|
| MD5 |
0d2c542a2b17a322179c32b2f3c6e21b
|
|
| BLAKE2b-256 |
a434cbc180a37a606ddaef349c8c43dbd9fa9c0b1489af8a69eb77346f32d509
|
Provenance
The following attestation bundles were made for bili_course-0.1.1.tar.gz:
Publisher:
publish.yml on Crribbe/bili-course
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
bili_course-0.1.1.tar.gz -
Subject digest:
06f29da77781957c943eb73780d948526fca3923c85d548e19b4b55f2c4ee945 - Sigstore transparency entry: 2726940834
- Sigstore integration time:
-
Permalink:
Crribbe/bili-course@4e6abad4eae7b64178f31c06697d14b6fc3a3555 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/Crribbe
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@4e6abad4eae7b64178f31c06697d14b6fc3a3555 -
Trigger Event:
push
-
Statement type:
File details
Details for the file bili_course-0.1.1-py3-none-any.whl.
File metadata
- Download URL: bili_course-0.1.1-py3-none-any.whl
- Upload date:
- Size: 80.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2f56d2ae81444021fd9e19e209779b05ddbc3cedb898e47423773f705b36f6f3
|
|
| MD5 |
12638d7fa992c51a26ede87523756781
|
|
| BLAKE2b-256 |
4864be4a89286844a2a8591ac1a93f5eadbcf1b543fb40f425ae411cfed623fd
|
Provenance
The following attestation bundles were made for bili_course-0.1.1-py3-none-any.whl:
Publisher:
publish.yml on Crribbe/bili-course
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
bili_course-0.1.1-py3-none-any.whl -
Subject digest:
2f56d2ae81444021fd9e19e209779b05ddbc3cedb898e47423773f705b36f6f3 - Sigstore transparency entry: 2726941145
- Sigstore integration time:
-
Permalink:
Crribbe/bili-course@4e6abad4eae7b64178f31c06697d14b6fc3a3555 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/Crribbe
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@4e6abad4eae7b64178f31c06697d14b6fc3a3555 -
Trigger Event:
push
-
Statement type: