jiji262/douyin-downloader
GitHub: jiji262/douyin-downloader
一款功能完善的抖音内容批量下载与管理工具,支持无水印视频、图文、音乐及直播录制,并集成去重、搜索、评论抓取与 API 服务功能。
Stars: 8498 | Forks: 1343
# 抖音下载器 V2.0
## 功能概览
### 已支持
| 功能 | 描述 |
|---------|-------------|
| 单个视频下载 | `/video/{aweme_id}` |
| 单个图文下载 | `/note/{note_id}` 和 `/gallery/{note_id}` |
| 单个合集下载 | `/collection/{mix_id}` 和 `/mix/{mix_id}` |
| 单个音乐下载 | `/music/{music_id}`(优先直接音频,兜底为首个相关 aweme) |
| 短链解析 | `https://v.douyin.com/...`, `v.iesdouyin.com`, 裸域名 |
| 个人主页批量下载 | `/user/{sec_uid}` + `mode: [post, like, mix, music]` |
| 登录用户收藏夹 | `/user/self?showTab=favorite_collection` + `mode: [collect, collectmix]` |
| 无水印优先 | 自动选择无水印的视频源 |
| 最高画质选择 | 自动从 `video.bit_rate` 阶梯中选取最高码率(视频 + 实况照片) |
| **直播录制** | `live.douyin.com/{room_id}` → FLV/HLS,在流结束时保留部分数据 |
| **评论收集** | 每个 aweme 的评论(+ 可选回复)保存为 `*_comments.json` |
| **热搜 + 关键词搜索** | `--hot-board [N]` / `--search "keyword"` 导出为 JSONL |
| **REST API 服务器模式** | `--serve --serve-port 8000`(可选 `fastapi + uvicorn`) |
| **通知推送** | 下载完成后通过 Bark / Telegram / Webhook 推送 |
| 额外资源 | 封面、音乐、头像、JSON metadata |
| 视频转录 | 可选,使用 OpenAI Transcriptions API |
| 并发下载 | 可配置并发数,默认 5 |
| 退避重试 | 指数退避(1秒, 2秒, 5秒) |
| 速率限制 | 默认 2 req/s |
| SQLite 去重 | 数据库 + 本地文件双重去重 |
| 增量下载 | `increase.post/like/mix/music` |
| 时间过滤 | `start_time` / `end_time` |
| 浏览器兜底 | 当分页被限制时启动浏览器,支持手动验证码 |
| 下载完整性检查 | Content-Length 校验,自动清理不完整文件 |
| 进度显示 | Rich 进度条,支持 `progress.quiet_logs` 静默模式 |
| Docker 部署 | 包含 Dockerfile |
| CI/CD | 使用 GitHub Actions 进行测试和 linting |
### 当前限制
- 浏览器兜底机制已在 `post` 模式下完全验证;`like/mix/music` 目前依赖 API 分页
- `number.allmix` / `increase.allmix` 作为兼容性别名保留,并统一标准化为 `mix`
- `collect` / `collectmix` 目前仅对登录 cookie 对应的账号有效
- `collect` / `collectmix` 必须单独使用,不能与 `post` / `like` / `mix` / `music` 组合使用
- `increase` 目前适用于 `post` / `like` / `mix` / `music`;收藏夹模式不支持增量停止
- 直播录制原生保存 FLV;HLS 源仅保存播放列表(使用 ffmpeg 获取可播放的输出)
| webcast room endpoint 尚未在所有直播场景下验证 —— 请视作实验性功能
## 快速开始
### 1) 环境要求
- Python 3.8+
- macOS / Linux / Windows
### 2) 安装依赖
```
pip install -r requirements.txt
```
对于浏览器兜底和自动 cookie 捕获:
```
pip install playwright
python -m playwright install chromium
```
### 3) 复制配置文件
```
cp config.example.yml config.yml
```
### 4) 获取 cookies(推荐:自动获取)
```
python -m tools.cookie_fetcher --config config.yml
```
登录抖音后,返回终端并按回车键。Cookies 将自动写入你的配置中。
### 5) Docker 部署(可选)
```
docker build -t douyin-downloader .
docker run -v $(pwd)/config.yml:/app/config.yml -v $(pwd)/Downloaded:/app/Downloaded douyin-downloader
```
## 最小可用配置
```
link:
- https://www.douyin.com/user/MS4wLjABAAAAxxxx
path: ./Downloaded/
mode:
- post
number:
post: 0
collect: 0
collectmix: 0
thread: 5
retry_times: 3
proxy: ""
database: true
database_path: dy_downloader.db
progress:
quiet_logs: true
cookies:
msToken: ""
ttwid: YOUR_TTWID
odin_tt: YOUR_ODIN_TT
passport_csrf_token: YOUR_CSRF_TOKEN
sid_guard: ""
browser_fallback:
enabled: true
headless: false
max_scrolls: 240
idle_rounds: 8
wait_timeout_seconds: 600
transcript:
enabled: false
model: gpt-4o-mini-transcribe
output_dir: ""
response_formats: ["txt", "json"]
api_url: https://api.openai.com/v1/audio/transcriptions
api_key_env: OPENAI_API_KEY
api_key: ""
```
## 用法
### 使用配置文件运行
```
python run.py -c config.yml
```
### 追加 CLI 参数
```
python run.py -c config.yml \
-u "https://www.douyin.com/video/7604129988555574538" \
-t 8 \
-p ./Downloaded
```
### 参数
| 参数 | 描述 |
|----------|-------------|
| `-u, --url` | 追加下载链接,可重复使用 |
| `-c, --config` | 指定配置文件(默认:`config.yml`) |
| `-p, --path` | 指定下载目录 |
| `-t, --thread` | 指定并发数 |
| `--show-warnings` | 显示警告/错误日志 |
| `-v, --verbose` | 显示 info/warning/error 级别日志 |
| `--hot-board [N]` | 获取抖音热搜榜并写入 JSONL;可选 Top-N |
| `--search KEYWORD` | 按关键词搜索视频,写入 JSONL |
| `--search-max N` | `--search` 的最大数量(默认 50) |
| `--serve` | 作为 REST API 服务器运行(需要 `pip install fastapi uvicorn`) |
| `--serve-host HOST` | REST 服务器监听主机(默认 127.0.0.1) |
| `--serve-port PORT` | REST 服务器监听端口(默认 8000) |
| `--version` | 显示版本号 |
## 典型场景
### 下载单个视频
```
link:
- https://www.douyin.com/video/7604129988555574538
```
### 下载单个图文
```
link:
- https://www.douyin.com/note/7341234567890123456
```
### 下载合集
```
link:
- https://www.douyin.com/collection/7341234567890123456
```
### 下载音乐
```
link:
- https://www.douyin.com/music/7341234567890123456
```
### 批量下载创作者发布的作品
```
link:
- https://www.douyin.com/user/MS4wLjABAAAAxxxx
mode:
- post
number:
post: 50
```
### 批量下载创作者点赞的作品
```
link:
- https://www.douyin.com/user/MS4wLjABAAAAxxxx
mode:
- like
number:
like: 0 # 0 means download all
```
### 同时下载多种模式
```
link:
- https://www.douyin.com/user/MS4wLjABAAAAxxxx
mode:
- post
- like
- mix
- music
```
跨模式去重:相同的 aweme_id 不会在不同模式中被重复下载。
### 下载登录用户收藏夹内容
```
link:
- https://www.douyin.com/user/self?showTab=favorite_collection
mode:
- collect
number:
collect: 0
```
### 下载登录用户收藏的合集
```
link:
- https://www.douyin.com/user/self?showTab=favorite_collection
mode:
- collectmix
number:
collectmix: 0
```
### 录制直播(实验性)
```
link:
- https://live.douyin.com/123456789 # or /follow/live/{room_id}
live:
max_duration_seconds: 3600 # 0 = record until broadcaster ends
chunk_size: 65536
idle_timeout_seconds: 30
```
录制器会在 `Downloaded/{author}/live/` 目录下保存一个 FLV 文件,以及一份
`*_room.json` metadata 快照。如果主播结束直播、网络空闲或你
按下 Ctrl+C,任何已录制的字节都会被保留(`.tmp` 文件会升级为
最终文件)。
### 收集单个 aweme 的评论
```
comments:
enabled: true
include_replies: false # true will fetch each comment's second-level replies (extra API calls)
max_comments: 500 # 0 = no cap
page_size: 20
```
在媒体文件旁生成一个 `{date}_{title}_{aweme_id}_comments.json` 文件。
### 导出热搜榜
```
python run.py --hot-board 30 -p ./Downloaded
# 输出: ./Downloaded/hot_board/20260424_221530.jsonl
```
### 关键词搜索
```
python run.py --search "猫咪" --search-max 100 -p ./Downloaded
# Output: ./Downloaded/search/猫咪_20260424_221530.jsonl
```
### 作为 REST API 服务器运行
```
pip install fastapi uvicorn # one-time optional dep
python run.py --serve --serve-port 8000
```
Endpoints:
| Method | Path | Description |
|--------|------|-------------|
| POST | `/api/v1/download` | 提交 `{"url": "..."}`,返回 `{job_id, status}` |
| GET | `/api/v1/jobs/{job_id}` | 获取特定任务的状态/计数 |
| GET | `/api/v1/jobs` | 列出最近的 jobs(受 TTL 和最大容量限制) |
| GET | `/api/v1/health` | 健康探针 |
已完成的任务会根据 TTL(默认 24小时)和 max-jobs(默认 500)进行清理 —— 进行中的任务永远不会被清理。可通过 `server.max_jobs` / `server.job_ttl_seconds` 进行配置。
### 下载完成后发送通知
```
notifications:
enabled: true
on_success: true
on_failure: true
providers:
- type: bark
url: https://api.day.app/YOUR_DEVICE_KEY
sound: bell
- type: telegram
bot_token: "123456:ABC..."
chat_id: "987654321"
- type: webhook # works with 企业微信/飞书/钉钉 bot URLs too
url: https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxx
extra_body:
msgtype: text
```
所有已启用的推送渠道将并行通知;某个渠道失败永远不会阻断下载流程。
### 增量下载(仅限新内容)
```
increase:
post: true
database: true # incremental mode requires database
```
### 全量抓取(无数量限制)
```
number:
post: 0
```
## 可选功能:视频转录 (`transcript`)
当前行为仅适用于**视频内容**(图文内容不生成转录)。
### 1) 在配置中启用
```
transcript:
enabled: true
model: gpt-4o-mini-transcribe
output_dir: "" # empty: same folder as video; non-empty: mirrored to target dir
response_formats:
- txt
- json
api_key_env: OPENAI_API_KEY
api_key: "" # can be set directly, or via environment variable
```
建议通过环境变量提供密钥:
```
export OPENAI_API_KEY="sk-xxxx"
```
### 2) 输出文件
启用后,将生成:
- `xxx.transcript.txt`
- `xxx.transcript.json`
如果 `database: true`,任务状态也会记录在 SQLite 表 `transcript_job` 中(`success/failed/skipped`)。
## 测试
推荐方式:
```
python3 -m pytest -q
```
目前也支持原生 `pytest`:
```
pytest -q
```
## 核心配置字段
| 字段 | 描述 |
|-------|-------------|
| `mode` | 支持 `post`/`like`/`mix`/`music`;登录收藏模式额外支持单独的 `collect`/`collectmix` |
| `number.post/like/mix/music/collect/collectmix` | 各模式的下载限制,0 = 无限制 |
| `increase.post/like/mix/music` | 各模式的增量开关 |
| `start_time` / `end_time` | 时间过滤器(格式:`YYYY-MM-DD`) |
| `folderstyle` | 为单条内容创建独立子目录 |
| `browser_fallback.*` | 当分页受限时对 `post` 启用浏览器兜底 |
| `progress.quiet_logs` | 在进度阶段开启静默日志 |
| `transcript.*` | 视频下载后的可选转录 |
| `comments.*` | 每个 aweme 的评论收集(选择性开启) |
| `live.*` | 直播录制选项(max_duration_seconds / chunk_size / idle_timeout_seconds) |
| `notifications.*` | 完成后通过 Bark/Telegram/Webhook 推送 |
| `server.*` | REST API 服务器调优(max_jobs, job_ttl_seconds) |
| `proxy` | 可选的 HTTP/HTTPS 代理设置 |
| `database` | 启用 SQLite 去重和历史记录 |
| `database_path` | SQLite 路径,默认为当前工作目录下的 `dy_downloader.db` |
| `thread` | 并发下载数 |
| `retry_times` | 失败时的重试次数 |
## 输出结构
`folderstyle: true` 且 `database_path: dy_downloader.db` 时的默认结构:
```
workspace/
├── config.yml
├── dy_downloader.db # default location when database: true
└── Downloaded/
├── download_manifest.jsonl
├── hot_board/ # when --hot-board is used
│ └── 20260424_221530.jsonl
├── search/ # when --search is used
│ └── 猫咪_20260424_221530.jsonl
└── AuthorName/
├── post/
│ └── 2024-02-07_Title_aweme_id/
│ ├── ...mp4
│ ├── ..._cover.jpg
│ ├── ..._music.mp3
│ ├── ..._data.json
│ ├── ..._avatar.jpg
│ ├── ..._comments.json # when comments.enabled
│ ├── ...transcript.txt
│ └── ...transcript.json
├── like/
│ └── ...
├── mix/
│ └── ...
├── music/
│ └── ...
├── collect/
│ └── ...
├── collectmix/
│ └── ...
└── live/ # when recording live streams
└── 2026-04-24_2215_LiveTitle_RoomId/
├── ...flv
└── ..._room.json
```
## 重新下载内容
程序使用**数据库记录 + 本地文件**双重检查机制来决定是否跳过已下载的内容。要强制重新下载,你需要进行相应的清理:
### 重新下载特定项目
```
# 删除本地文件(文件夹名称包含 aweme_id)
rm -rf Downloaded/AuthorName/post/*_/
# 删除数据库记录
sqlite3 dy_downloader.db "DELETE FROM aweme WHERE aweme_id = '';"
```
### 重新下载特定作者的所有内容
```
rm -rf Downloaded/AuthorName/
sqlite3 dy_downloader.db "DELETE FROM aweme WHERE author_name = 'AuthorName';"
```
### 完全重置(重新下载所有内容)
```
rm -rf Downloaded/
rm dy_downloader.db
```
## 常见问题
### 1) 为什么我只能获取到大概 20 条内容?
这是一种常见的翻页风控行为。请确保:
- `browser_fallback.enabled: true`
- `browser_fallback.headless: false`
- 在弹出的浏览器中手动完成验证,且不要过早关闭
### 2) 为什么进度输出很杂乱/重复?
默认情况下,`progress.quiet_logs: true` 会在进度阶段抑制日志输出。
在调试时可暂时使用 `--show-warnings` 或 `-v`。
### 3) 如果 cookies 过期了怎么办?
运行:
```
python -m tools.cookie_fetcher --config config.yml
```
### 4) 为什么没有生成转录文件?
按顺序检查:
- `transcript.enabled` 是否为 `true`
- 下载的内容是否是视频(图文不会被转录)
- `OPENAI_API_KEY`(或 `transcript.api_key`)是否有效
- `response_formats` 是否包含 `txt` 或 `json`
### 5) 如何查看下载历史?
```
sqlite3 dy_downloader.db "SELECT aweme_id, title, author_name, datetime(download_time, 'unixepoch', 'localtime') FROM aweme ORDER BY download_time DESC LIMIT 20;"
```
## 社区群组
点击链接加入群聊【QQ群】:[https://qm.qq.com/q/9xoNt8Wzv4](https://qm.qq.com/q/9xoNt8Wzv4)
## 免责声明
本项目仅供技术研究、学习和个人数据管理之用。请合法、负责任地使用:
- 请勿用于侵犯他人隐私、版权或其他合法权益
- 请勿用于任何非法目的
- 用户需对因使用本工具而产生的一切风险和责任自负
- 如果平台政策或接口发生变化导致功能失效,这属于正常的技术风险
继续使用本项目即表示你知悉并接受上述声明。
## 许可证
本项目基于 MIT 许可证授权。详情请参阅 [LICENSE](./LICENSE)。
## 友情链接
- [LINUX DO](https://linux.do/)
![]() Download · paste a link to start |
![]() Following · synced creator list |
![]() Task Center · per-job status |
![]() Archive · SQLite history & filters |
![]() Settings · naming templates |
![]() Live progress · per-job event log |
点击链接加入群聊【QQ群】:[https://qm.qq.com/q/9xoNt8Wzv4](https://qm.qq.com/q/9xoNt8Wzv4)
## 免责声明
本项目仅供技术研究、学习和个人数据管理之用。请合法、负责任地使用:
- 请勿用于侵犯他人隐私、版权或其他合法权益
- 请勿用于任何非法目的
- 用户需对因使用本工具而产生的一切风险和责任自负
- 如果平台政策或接口发生变化导致功能失效,这属于正常的技术风险
继续使用本项目即表示你知悉并接受上述声明。
## 许可证
本项目基于 MIT 许可证授权。详情请参阅 [LICENSE](./LICENSE)。
## 友情链接
- [LINUX DO](https://linux.do/)标签:Petitpotam, Python, SQLite, 下载工具, 去水印, 批量下载, 抖音, 无后门, 特征检测, 请求拦截, 逆向工具





