返回 douyin-downloader
README.zh-CN.md
根目录 / README.zh-CN.md
1 # 抖音下载器 V2.0(Douyin Downloader)
2
3 <p align="center">
4 <img src="https://socialify.git.ci/jiji262/douyin-downloader/image?custom_description=%E6%8A%96%E9%9F%B3%E6%89%B9%E9%87%8F%E4%B8%8B%E8%BD%BD%E5%B7%A5%E5%85%B7%EF%BC%8C%E5%8E%BB%E6%B0%B4%E5%8D%B0%EF%BC%8C%E6%94%AF%E6%8C%81%E8%A7%86%E9%A2%91%E3%80%81%E5%9B%BE%E9%9B%86%E3%80%81%E4%BD%9C%E8%80%85%E4%B8%BB%E9%A1%B5%E6%89%B9%E9%87%8F%E4%B8%8B%E8%BD%BD%E3%80%82&description=1&font=Jost&forks=1&logo=https%3A%2F%2Fraw.githubusercontent.com%2Fjiji262%2Fdouyin-downloader%2Frefs%2Fheads%2FV1.0%2Fimg%2Flogo.png&name=1&owner=1&pattern=Circuit+Board&pulls=1&stargazers=1&theme=Light" alt="douyin-downloader" width="820" />
5 </p>
6
7 一个面向实用场景的抖音下载工具,支持视频、图文、合集、音乐、收藏夹等多种类型下载,以及作者主页批量下载,默认带进度展示、重试、数据库去重、下载完整性校验和浏览器兜底能力。
8
9 > 当前文档对应 **V2.0(main 分支)**。
10 > 如需使用旧版,请切回 **V1.0**:`git fetch --all && git switch V1.0`
11
12 ## 功能概览
13
14 ### 已支持
15
16 | 功能 | 说明 |
17 |------|------|
18 | 单个视频下载 | `/video/{aweme_id}` |
19 | 单个图文下载 | `/note/{note_id}`、`/gallery/{note_id}` |
20 | 单个合集下载 | `/collection/{mix_id}`、`/mix/{mix_id}` |
21 | 单个音乐下载 | `/music/{music_id}`(优先原声文件,缺失时回退到该音乐下首条作品) |
22 | 短链自动解析 | `https://v.douyin.com/...`、`v.iesdouyin.com`,含裸 host |
23 | 用户主页批量下载 | `/user/{sec_uid}` + `mode: [post, like, mix, music]` |
24 | 当前登录账号收藏夹下载 | `/user/self?showTab=favorite_collection` + `mode: [collect, collectmix]` |
25 | 无水印优先 | 自动选择无水印视频源 |
26 | 最高清自动挑选 | 基于 `video.bit_rate` 数组自动选最高码率(视频 + 实况图生效) |
27 | **直播录制** | `live.douyin.com/{room_id}` → FLV/HLS,主播下播时保留已录数据 |
28 | **评论采集** | 按作品抓评论(可含二级回复),输出 `*_comments.json` |
29 | **热搜榜 + 关键词搜索** | `--hot-board [N]` / `--search "关键词"`,结果落 JSONL |
30 | **REST API 服务模式** | `--serve --serve-port 8000`(可选 `fastapi + uvicorn`) |
31 | **完成通知推送** | 下载完成后推 Bark / Telegram / Webhook |
32 | 附加资源下载 | 封面、音乐、头像、JSON 元数据 |
33 | 视频转写 | 可选功能,调用 OpenAI Transcriptions API |
34 | 并发下载 | 可配置并发数,默认 5 |
35 | 失败重试 | 指数退避重试(1s, 2s, 5s) |
36 | 速率限制 | 默认 2 请求/秒 |
37 | SQLite 去重 | 数据库 + 本地文件双重去重 |
38 | 增量下载 | `increase.post/like/mix/music` |
39 | 时间过滤 | `start_time` / `end_time` |
40 | 浏览器兜底 | 翻页受限时启动浏览器,支持人工过验证码 |
41 | 下载完整性校验 | Content-Length 比对,不完整文件自动清理并重试 |
42 | 进度条展示 | Rich 进度条,支持 `progress.quiet_logs` 静默模式 |
43 | Docker 部署 | 提供 Dockerfile |
44 | CI/CD | GitHub Actions 自动测试和 lint |
45
46 ### 限制说明
47
48 - 浏览器兜底当前仅针对 `post` 完整验证,`like/mix/music` 主要依赖 API 正常分页
49 - `number.allmix` / `increase.allmix` 作为兼容别名保留,运行时会归一化到 `mix`
50 - `collect` / `collectmix` 当前仅支持当前已登录 Cookie 对应账号
51 - `collect` / `collectmix` 必须单独使用,不能和 `post` / `like` / `mix` / `music` 混用
52 - `increase` 当前仅支持 `post` / `like` / `mix` / `music`;收藏夹模式不支持增量截断
53 - 直播录制 FLV 可直接播放;HLS 源只保存 playlist 文件(需要用 ffmpeg 后处理)
54 - webcast 直播接口未覆盖所有场景,视为 experimental
55
56 ## 快速开始
57
58 ### 1) 环境准备
59
60 - Python 3.8+
61 - macOS / Linux / Windows
62
63 ### 2) 安装依赖
64
65 ```bash
66 pip install -r requirements.txt
67 ```
68
69 如需浏览器兜底或自动获取 Cookie:
70
71 ```bash
72 pip install playwright
73 python -m playwright install chromium
74 ```
75
76 ### 3) 复制配置
77
78 ```bash
79 cp config.example.yml config.yml
80 ```
81
82 ### 4) 获取 Cookie(推荐自动方式)
83
84 ```bash
85 python -m tools.cookie_fetcher --config config.yml
86 ```
87
88 登录抖音后回到终端按 Enter,程序会自动写入配置。
89
90 ### 5) Docker 部署(可选)
91
92 ```bash
93 docker build -t douyin-downloader .
94 docker run -v $(pwd)/config.yml:/app/config.yml -v $(pwd)/Downloaded:/app/Downloaded douyin-downloader
95 ```
96
97 ## 最小可用配置
98
99 ```yaml
100 link:
101 - https://www.douyin.com/user/MS4wLjABAAAAxxxx
102
103 path: ./Downloaded/
104 mode:
105 - post
106
107 number:
108 post: 0
109 collect: 0
110 collectmix: 0
111
112 thread: 5
113 retry_times: 3
114 proxy: ""
115 database: true
116 database_path: dy_downloader.db
117
118 progress:
119 quiet_logs: true
120
121 cookies:
122 msToken: ""
123 ttwid: YOUR_TTWID
124 odin_tt: YOUR_ODIN_TT
125 passport_csrf_token: YOUR_CSRF_TOKEN
126 sid_guard: ""
127
128 browser_fallback:
129 enabled: true
130 headless: false
131 max_scrolls: 240
132 idle_rounds: 8
133 wait_timeout_seconds: 600
134
135 transcript:
136 enabled: false
137 model: gpt-4o-mini-transcribe
138 output_dir: ""
139 response_formats: ["txt", "json"]
140 api_url: https://api.openai.com/v1/audio/transcriptions
141 api_key_env: OPENAI_API_KEY
142 api_key: ""
143 ```
144
145 ## 使用方式
146
147 ### 使用配置文件运行
148
149 ```bash
150 python run.py -c config.yml
151 ```
152
153 ### 命令行追加参数
154
155 ```bash
156 python run.py -c config.yml \
157 -u "https://www.douyin.com/video/7604129988555574538" \
158 -t 8 \
159 -p ./Downloaded
160 ```
161
162 ### 参数说明
163
164 | 参数 | 说明 |
165 |------|------|
166 | `-u, --url` | 追加下载链接(可重复传入) |
167 | `-c, --config` | 指定配置文件(默认 `config.yml`) |
168 | `-p, --path` | 指定下载目录 |
169 | `-t, --thread` | 指定并发数 |
170 | `--show-warnings` | 显示 warning/error 日志 |
171 | `-v, --verbose` | 显示 info/warning/error 日志 |
172 | `--hot-board [N]` | 拉取抖音热搜榜并导出 JSONL,可选上限 N |
173 | `--search KEYWORD` | 按关键词搜索作品并导出 JSONL |
174 | `--search-max N` | `--search` 场景下最多拉取条数(默认 50) |
175 | `--serve` | 以 REST API 服务模式运行(需要 `pip install fastapi uvicorn`) |
176 | `--serve-host HOST` | REST 服务监听地址(默认 127.0.0.1) |
177 | `--serve-port PORT` | REST 服务监听端口(默认 8000) |
178 | `--version` | 显示版本号 |
179
180 ## 典型场景
181
182 ### 下载单个视频
183
184 ```yaml
185 link:
186 - https://www.douyin.com/video/7604129988555574538
187 ```
188
189 ### 下载单个图文
190
191 ```yaml
192 link:
193 - https://www.douyin.com/note/7341234567890123456
194 ```
195
196 ### 下载单个合集
197
198 ```yaml
199 link:
200 - https://www.douyin.com/collection/7341234567890123456
201 ```
202
203 ### 下载单个音乐
204
205 ```yaml
206 link:
207 - https://www.douyin.com/music/7341234567890123456
208 ```
209
210 ### 批量下载作者主页作品
211
212 ```yaml
213 link:
214 - https://www.douyin.com/user/MS4wLjABAAAAxxxx
215 mode:
216 - post
217 number:
218 post: 50
219 ```
220
221 ### 批量下载作者点赞作品
222
223 ```yaml
224 link:
225 - https://www.douyin.com/user/MS4wLjABAAAAxxxx
226 mode:
227 - like
228 number:
229 like: 0 # 0 表示全量下载
230 ```
231
232 ### 同时下载多种模式
233
234 ```yaml
235 link:
236 - https://www.douyin.com/user/MS4wLjABAAAAxxxx
237 mode:
238 - post
239 - like
240 - mix
241 - music
242 ```
243
244 跨模式自动去重:同一个 aweme_id 在不同模式下不会重复下载。
245
246 ### 批量下载当前登录账号收藏夹作品
247
248 ```yaml
249 link:
250 - https://www.douyin.com/user/self?showTab=favorite_collection
251 mode:
252 - collect
253 number:
254 collect: 0
255 ```
256
257 ### 批量下载当前登录账号收藏合集
258
259 ```yaml
260 link:
261 - https://www.douyin.com/user/self?showTab=favorite_collection
262 mode:
263 - collectmix
264 number:
265 collectmix: 0
266 ```
267
268 ### 录制直播(实验性)
269
270 ```yaml
271 link:
272 - https://live.douyin.com/123456789 # 也支持 /follow/live/{room_id}
273 live:
274 max_duration_seconds: 3600 # 0 = 录到主播下播
275 chunk_size: 65536
276 idle_timeout_seconds: 30
277 ```
278
279 录制的 FLV 会保存在 `Downloaded/{作者}/live/` 下,并附带 `*_room.json` 直播间元数据快照。
280 主播下播、网络空闲或 Ctrl+C 中断时,**已录制的字节会被保留**(.tmp 文件自动提升为正式文件)。
281
282 ### 采集作品评论
283
284 ```yaml
285 comments:
286 enabled: true
287 include_replies: false # 设为 true 会多拉每条评论的二级回复(额外请求量)
288 max_comments: 500 # 0 = 不限
289 page_size: 20
290 ```
291
292 会在媒体文件旁生成 `{date}_{title}_{aweme_id}_comments.json`。
293
294 ### 导出热搜榜快照
295
296 ```bash
297 python run.py --hot-board 30 -p ./Downloaded
298 # 输出:./Downloaded/hot_board/20260424_221530.jsonl
299 ```
300
301 ### 关键词搜索
302
303 ```bash
304 python run.py --search "猫咪" --search-max 100 -p ./Downloaded
305 # 输出:./Downloaded/search/猫咪_20260424_221530.jsonl
306 ```
307
308 ### 以 REST API 服务模式运行
309
310 ```bash
311 pip install fastapi uvicorn # 一次性可选依赖
312 python run.py --serve --serve-port 8000
313 ```
314
315 接口:
316
317 | Method | Path | 说明 |
318 |--------|------|------|
319 | POST | `/api/v1/download` | 提交 `{"url": "..."}`,返回 `{job_id, status}` |
320 | GET | `/api/v1/jobs/{job_id}` | 查询指定 job 的状态/计数 |
321 | GET | `/api/v1/jobs` | 列出最近的 job(按 TTL + 容量剪裁) |
322 | GET | `/api/v1/health` | 健康探针 |
323
324 完成态的 job 会按 TTL(默认 24 小时)+ 最大数量(默认 500)自动剪裁;in-flight 的 job 永不被裁掉。
325 可通过 `server.max_jobs` / `server.job_ttl_seconds` 调整。
326
327 ### 完成后发送通知
328
329 ```yaml
330 notifications:
331 enabled: true
332 on_success: true
333 on_failure: true
334 providers:
335 - type: bark
336 url: https://api.day.app/YOUR_DEVICE_KEY
337 sound: bell
338 - type: telegram
339 bot_token: "123456:ABC..."
340 chat_id: "987654321"
341 - type: webhook # 企业微信/飞书/钉钉 bot URL 同样可用
342 url: https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxx
343 extra_body:
344 msgtype: text
345 ```
346
347 所有启用的 provider 会并发推送;单个 provider 失败不会阻塞主下载流程。
348
349 ### 增量下载(只下载新作品)
350
351 ```yaml
352 increase:
353 post: true
354 database: true # 增量模式依赖数据库记录
355 ```
356
357 ### 全量抓取(不限制数量)
358
359 ```yaml
360 number:
361 post: 0
362 ```
363
364 ## 可选功能:视频转写(transcript)
365
366 当前实现仅对**视频作品**生效(图文不会生成转写)。
367
368 ### 1) 开启方式
369
370 ```yaml
371 transcript:
372 enabled: true
373 model: gpt-4o-mini-transcribe
374 output_dir: "" # 留空: 与视频同目录;非空: 镜像到指定目录
375 response_formats:
376 - txt
377 - json
378 api_key_env: OPENAI_API_KEY
379 api_key: "" # 可直接填,或使用环境变量
380 ```
381
382 推荐通过环境变量提供密钥:
383
384 ```bash
385 export OPENAI_API_KEY="sk-xxxx"
386 ```
387
388 ### 2) 输出文件
389
390 启用后会生成:
391
392 - `xxx.transcript.txt`
393 - `xxx.transcript.json`
394
395 若 `database: true`,会在数据库 `transcript_job` 表记录状态(`success/failed/skipped`)。
396
397 ## 测试
398
399 推荐使用:
400
401 ```bash
402 python3 -m pytest -q
403 ```
404
405 当前也支持直接运行:
406
407 ```bash
408 pytest -q
409 ```
410
411 ## 关键配置项
412
413 | 配置项 | 说明 |
414 |--------|------|
415 | `mode` | 支持 `post`/`like`/`mix`/`music`;当前登录收藏夹模式额外支持单独使用的 `collect`/`collectmix` |
416 | `number.post/like/mix/music/collect/collectmix` | 各模式下载数量限制,0 为不限 |
417 | `increase.post/like/mix/music` | 各模式增量开关 |
418 | `start_time` / `end_time` | 时间过滤(格式 `YYYY-MM-DD`) |
419 | `folderstyle` | 按作品维度创建子目录 |
420 | `browser_fallback.*` | `post` 翻页受限时启用浏览器兜底 |
421 | `progress.quiet_logs` | 进度阶段静默日志,减少刷屏 |
422 | `transcript.*` | 视频下载后的可选转写 |
423 | `proxy` | 为 API 请求和媒体下载设置 HTTP/HTTPS 代理,例如 `http://127.0.0.1:7890` |
424 | `comments.*` | 按作品采集评论(默认关闭) |
425 | `live.*` | 直播录制参数(max_duration_seconds / chunk_size / idle_timeout_seconds) |
426 | `notifications.*` | 下载完成后 Bark/Telegram/Webhook 推送 |
427 | `server.*` | REST API 服务调优(max_jobs、job_ttl_seconds) |
428 | `database` | 启用 SQLite 去重和历史记录 |
429 | `database_path` | SQLite 文件路径,默认在当前工作目录生成 `dy_downloader.db` |
430 | `thread` | 并发下载数 |
431 | `retry_times` | 失败重试次数 |
432
433 ## 输出目录
434
435 默认 `folderstyle: true` 且 `database_path: dy_downloader.db` 时:
436
437 ```text
438 工作目录/
439 ├── config.yml
440 ├── dy_downloader.db # database: true 时默认生成在这里
441 └── Downloaded/
442 ├── download_manifest.jsonl
443 └── 作者名/
444 ├── post/
445 │ └── 2024-02-07_作品标题_aweme_id/
446 │ ├── ...mp4
447 │ ├── ..._cover.jpg
448 │ ├── ..._music.mp3
449 │ ├── ..._data.json
450 │ ├── ..._avatar.jpg
451 │ ├── ...transcript.txt
452 │ └── ...transcript.json
453 ├── like/
454 │ └── ...
455 ├── mix/
456 │ └── ...
457 ├── music/
458 │ └── ...
459 ├── collect/
460 │ └── ...
461 └── collectmix/
462 └── ...
463 Downloaded/
464 ├── download_manifest.jsonl
465 ├── dy_downloader.db # database: true 时生成
466 ├── hot_board/ # 使用 --hot-board 时生成
467 │ └── 20260424_221530.jsonl
468 ├── search/ # 使用 --search 时生成
469 │ └── 猫咪_20260424_221530.jsonl
470 └── 作者名/
471 ├── post/
472 │ └── 2024-02-07_作品标题_aweme_id/
473 │ ├── ...mp4
474 │ ├── ..._cover.jpg
475 │ ├── ..._music.mp3
476 │ ├── ..._data.json
477 │ ├── ..._avatar.jpg
478 │ ├── ..._comments.json # comments.enabled 时生成
479 │ ├── ...transcript.txt
480 │ └── ...transcript.json
481 ├── like/
482 │ └── ...
483 ├── mix/
484 │ └── ...
485 ├── music/
486 │ └── ...
487 └── live/ # 录制直播时生成
488 └── 2026-04-24_2215_直播标题_房间号/
489 ├── ...flv
490 └── ..._room.json
491 ```
492
493 ## 重新下载
494
495 程序通过**数据库记录 + 本地文件**双重检查判断是否跳过已下载内容。要重新下载,需要按以下方式清理数据:
496
497 ### 重新下载特定作品
498
499 ```bash
500 # 删除本地文件(文件名中包含 aweme_id)
501 rm -rf Downloaded/作者名/post/*_<aweme_id>/
502
503 # 删除数据库记录
504 sqlite3 dy_downloader.db "DELETE FROM aweme WHERE aweme_id = '<aweme_id>';"
505 ```
506
507 ### 重新下载某个作者的全部作品
508
509 ```bash
510 rm -rf Downloaded/作者名/
511 sqlite3 dy_downloader.db "DELETE FROM aweme WHERE author_name = '作者名';"
512 ```
513
514 ### 全部从零重新下载
515
516 ```bash
517 rm -rf Downloaded/
518 rm dy_downloader.db
519 ```
520
521 > **注意:** 只删数据库不删文件不会触发重新下载——程序会扫描本地文件名中的 aweme_id 进行去重。只删文件不删数据库会触发重新下载(数据库中有记录但文件不存在时视为需要重新下载)。
522
523 ## 常见问题
524
525 ### 1) 只能抓到 20 条作品怎么办?
526
527 这是翻页风控的常见现象。确保:
528
529 - `browser_fallback.enabled: true`
530 - `browser_fallback.headless: false`
531 - 浏览器弹窗出现后手动完成验证,不要立即关闭窗口
532
533 ### 2) 进度条出现重复刷屏怎么办?
534
535 默认 `progress.quiet_logs: true` 会在进度阶段静默日志。
536 调试时再临时加 `--show-warnings` 或 `-v`。
537
538 ### 3) Cookie 失效怎么办?
539
540 重新执行:
541
542 ```bash
543 python -m tools.cookie_fetcher --config config.yml
544 ```
545
546 ### 4) 为什么没有生成 transcript 文件?
547
548 请依次检查:
549
550 - `transcript.enabled` 是否为 `true`
551 - 是否下载的是视频(图文不转写)
552 - `OPENAI_API_KEY`(或 `transcript.api_key`)是否有效
553 - `response_formats` 是否包含 `txt` 或 `json`
554
555 ### 5) 如何查看下载历史?
556
557 ```bash
558 sqlite3 dy_downloader.db "SELECT aweme_id, title, author_name, datetime(download_time, 'unixepoch', 'localtime') FROM aweme ORDER BY download_time DESC LIMIT 20;"
559 ```
560
561 ## 旧版切换(V1.0)
562
563 如果你要继续使用老脚本风格(V1.0),可切换到旧分支:
564
565 ```bash
566 git fetch --all
567 git switch V1.0
568 ```
569
570 ## 沟通群
571
572 <img src="./img/fuye.jpg" alt="qun" width="360" />
573
574 点击链接加入群聊【QQ群】:[https://qm.qq.com/q/GDCzZCO3mM](https://qm.qq.com/q/GDCzZCO3mM)
575
576
577 ## 免责声明
578
579 本项目仅用于技术研究、学习交流与个人数据管理。请在合法合规前提下使用:
580
581 - 不得用于侵犯他人隐私、版权或其他合法权益
582 - 不得用于任何违法违规用途
583 - 使用者应自行承担因使用本项目产生的全部风险与责任
584 - 如平台规则、接口策略变更导致功能失效,属于正常技术风险
585
586 如果你继续使用本项目,即视为已阅读并同意上述声明。
587
588 ## 许可证
589
590 本项目采用 MIT License,详见 [LICENSE](./LICENSE)。
591
591 lines MARKDOWN