jiji262/douyin-downloader

GitHub: jiji262/douyin-downloader

一款功能完善的抖音内容批量下载与管理工具,支持无水印视频、图文、音乐及直播录制,并集成去重、搜索、评论抓取与 API 服务功能。

Stars: 8498 | Forks: 1343

# 抖音下载器 V2.0

douyin-downloader

中文文档 (Chinese): [README.zh-CN.md](./README.zh-CN.md) 一个实用的抖音下载器,支持视频、图文、合集、音乐、收藏夹和个人主页批量下载,具备进度显示、重试机制、SQLite 去重、下载完整性检查以及浏览器兜底支持。 ## 桌面应用 (Douzy) 基于相同后端构建的桌面 GUI 应用 —— 粘贴链接即可开始,同步你的关注列表,并以可视化方式追踪下载进度。
Download — paste a link to start
Download · paste a link to start
Following sync
Following · synced creator list
Task Center
Task Center · per-job status
Archive with filters
Archive · SQLite history & filters
Settings and naming templates
Settings · naming templates
Live download progress
Live progress · per-job event log
## 功能概览 ### 已支持 | 功能 | 描述 | |---------|-------------| | 单个视频下载 | `/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;" ``` ## 社区群组 qun 点击链接加入群聊【QQ群】:[https://qm.qq.com/q/9xoNt8Wzv4](https://qm.qq.com/q/9xoNt8Wzv4) ## 免责声明 本项目仅供技术研究、学习和个人数据管理之用。请合法、负责任地使用: - 请勿用于侵犯他人隐私、版权或其他合法权益 - 请勿用于任何非法目的 - 用户需对因使用本工具而产生的一切风险和责任自负 - 如果平台政策或接口发生变化导致功能失效,这属于正常的技术风险 继续使用本项目即表示你知悉并接受上述声明。 ## 许可证 本项目基于 MIT 许可证授权。详情请参阅 [LICENSE](./LICENSE)。 ## 友情链接 - [LINUX DO](https://linux.do/)
标签:Petitpotam, Python, SQLite, 下载工具, 去水印, 批量下载, 抖音, 无后门, 特征检测, 请求拦截, 逆向工具