抖音下载器 douyin-downloader 深度拆解:核心机制 + 从配置到排障完整指南
【免费下载链接】douyin-downloaderA practical Douyin downloader for both single-item and profile batch downloads, with progress display, retries, SQLite deduplication, and browser fallback support. 抖音批量下载工具,去水印,支持视频、图集、合集、音乐(原声)。项目地址: https://gitcode.com/GitHub_Trending/do/douyin-downloader
douyin-downloader 是一个 Python 写的抖音批量下载器:给它博主主页链接,它并发拉取作品、去水印,用 SQLite 加磁盘扫描双重去重,再按"日期_标题_作品ID"归档落盘。它适合两类人:需要持续跟踪博主产出的内容运营,以及想把"批量下载"嵌进自己流水线的工程师。
🔑 一句话定位 + 30秒上手
最短路径三步,全部复制即可跑通:
# 1. 克隆并安装依赖(Python 3.9+) git clone https://gitcode.com/GitHub_Trending/do/douyin-downloader cd douyin-downloader pip install -r requirements.txt# 2. 准备配置并抓取登录 Cookie cp config.example.yml config.yml python -m tools.cookie_fetcher --config config.yml# 3. 改完 config.yml 的 link 字段后直接运行 python run.py -c config.yml先说一个必须知道的现状:2026 年 8 月起抖音的 Argus 风控门禁覆盖了单个作品、合集、音乐、点赞和收藏类接口,命令行版对这些模式基本失效,主页作品批量下载需要开 Playwright 浏览器兜底才稳定。所以这篇文章的主线是"主页批量下载"这条仍然可用的路径,其余能力点到为止。
🏗️ 拆开看内部
仓库按职责切成六个模块,每个一句话能说清:
douyin-downloader/ ├── cli/ # 参数解析、异步主循环、rich 进度展示 ├── core/ # API 客户端、URL 解析、各下载器实现 │ └── user_modes/ # 用户主页各下载模式(策略模式) ├── control/ # 限速器、重试器、并发队列 ├── storage/ # SQLite 数据库、文件管理、元数据 ├── auth/ # Cookie 与 msToken 管理 ├── config/ # YAML 配置加载与环境变量覆盖 └── utils/ # xbogus 签名、命名模板、日志三个机制值得展开,因为它们直接对应你后面会调的每个配置项。
双重去重:磁盘索引是第一道闸。任务启动时先扫描下载目录,从所有媒体文件名里正则提取 15-20 位作品 ID 建内存索引;命中就跳过。没命中再查 SQLite 历史记录,历史库只作兜底,状态不明时宁可补下。核心逻辑去掉防御性代码后就是这样:
# core/downloader_base.py 的 _should_download(简化版) async def _should_download(self, aweme_id, *, force=False): if force: return True await self._ensure_local_aweme_index() # 扫描磁盘建索引 if self._is_locally_downloaded(aweme_id): # 第一道:本地文件 return False if self.database is None: return True if await self.database.is_downloaded(aweme_id): # 第二道:SQLite 历史 return False return True限速器:固定间隔外加随机抖动。每次 API 请求前都要过 control/rate_limiter.py,默认 2 req/s,每次再随机睡 0-0.5 秒——抖动故意放在锁内执行,保证下一个请求的计时基准是"上一次真正发出去"的时间,而不是拿锁的时间。这就是为什么你观察到的请求间隔总在 0.5-1 秒之间浮动。
并发与兜底:下载任务进 QueueManager,thread默认 5 个 worker;每条视频还有个 15 分钟的总时限,防止"4 轮重试 × 4 个候选地址"最坏情况挂 80 分钟拖死整个队列。签名层面,utils/xbogus.py 用纯 Python 复刻了抖音前端签名算法,配合 gmssl 的国密算法做请求头,这部分你不用碰。
机制讲完就能对上配置了:thread管并发、retry_times管重试、increase管去重开关,下面逐行过。
⚙️ 按你的方式跑
最小可用配置。把 config.example.yml 复制为config.yml,核心就这几行:
link: - https://www.douyin.com/user/YOUR_SEC_UID # 博主主页链接 path: ./Downloaded/ # 下载根目录 mode: [post] # 下载模式:post 发布作品 number: post: 50 # 每个博主最多 50 条,0 表示不限 increase: post: true # 增量:跳过已下载作品 redownload_missing_files: true # 数据库有记录但文件缺失时补下 thread: 5 # 并发下载线程数 retry_times: 3 # 失败重试次数 browser_fallback: enabled: true # 翻页被风控时启用浏览器兜底 headless: false # 显示浏览器窗口,便于人工过验证桌面端 Douzy 把同样的选项做成了界面,下载选项、作品类型过滤、数量上限都在一个面板里勾选:
Douzy 桌面版下载选项面板:内容类型、作品类型过滤与各模式数量上限
Cookie 获取两条路。自动方式重跑python -m tools.cookie_fetcher --config config.yml,命令会拉起浏览器,你登录抖音后回终端按 Enter,Cookie 就写回config.yml的cookies段;手动方式则是自己从浏览器开发者工具复制msToken、ttwid、odin_tt、passport_csrf_token五个值填进去。Cookie 有效期有限,失效后重跑第一条命令即可——但它解决不了风控问题,下面"出问题了"一节会讲区别。
进阶配置四组。画质、目录组织、命名模板、附加产物:
video_quality: original # original 探测原片,失败回退最高转码档;highest 默认 author_dir: nickname_uid # 作者目录:nickname 直观 / sec_uid 唯一 / 组合推荐 filename_template: "{date}_{title}_{id}" # 文件名模板,必须含 {id} folder_template: "{date}_{title}_{id}" # 子目录模板,逻辑相同 cover: true # 额外保存封面 music: true # 额外保存背景音乐 json: true # 保存作品原始 JSON download_pinned: false # 是否包含置顶作品🎬 四个真实场景
场景一:内容团队追博主。运营要沉淀某博主 2024 全年的发布,用于选题参考。
link: - https://www.douyin.com/user/YOUR_SEC_UID start_time: "2024-01-01" # 按作品发布时间筛选 end_time: "2024-12-31" number: post: 200 folderstyle: true # 每个作品单独建子目录 json: true # 保留元数据供后续分析输出物就是作者/post/2024-xx-xx_标题_aweme_id/下成组的 mp4、封面和 data.json,直接按时间线翻就能看出创作节奏。
场景二:订阅式周更存档。每周固定跑一遍,只拉新作品。increase.post: true保证已下过的全部跳过,redownload_missing_files: true保证有人手删过文件时能自动补齐。想反过来强制覆盖当前筛选范围内的作品,把increase.post改成false就行,不用动数据库。
场景三:趋势研究挖词。研究员不下载视频,只要数据结构化结果:
# 导出热搜榜前 30 条与关键词搜索结果(均为 JSONL) python run.py --hot-board 30 -p ./Downloaded python run.py --search "猫咪" --search-max 100 -p ./Downloaded每行一条 JSON,含标题、标签和作品 ID,丢进 pandas 或 DuckDB 就能做词频和话题聚类。
场景四:嵌进内部系统。工程师把下载能力包成服务,内部系统只发 HTTP 请求。命令在"接入更大的系统"一节给全,这里只说画像:任务由别的系统触发、状态要可轮询、结果要落固定目录,REST 模式就比每次 spawn 一个 CLI 进程干净得多。
🗂️ 产出物长什么样
folderstyle: true时的默认目录结构,日期前缀取的是作品发布时间create_time(缺失时回退下载当天并记告警):
Downloaded/ ├── download_manifest.jsonl # 每行一条的下载清单,append-only ├── dy_downloader.db # SQLite 历史记录(database: true 时) └── 作者名/ └── post/ └── 2024-02-07_作品标题_aweme_id/ ├── 2024-02-07_作品标题_aweme_id.mp4 # 主视频 ├── 2024-02-07_作品标题_aweme_id_cover.jpg # 封面 ├── 2024-02-07_作品标题_aweme_id_music.mp3 # 背景音乐 ├── 2024-02-07_作品标题_aweme_id_avatar.jpg # 作者头像 ├── 2024-02-07_作品标题_aweme_id_data.json # 原始元数据 └── 2024-02-07_作品标题_aweme_id_comments.json # 评论(若开启)模板里的日期为什么重要:文件按"内容发生的时间"归档而不是"你下载的时间",重复跑任务不会打乱时间线;{id}强制保留在模板里,是为了让去重正则能从文件名反推出作品 ID——这也是为什么配置注释反复强调模板必须含{id}。
命名模板共 15 个变量,常用的几个:
| 变量 | 含义 | 变量 | 含义 |
|---|---|---|---|
{date} | 发布日期 YYYY-MM-DD | {title} | 作品标题 |
{id} | 作品 ID(必含) | {author} | 作者昵称 |
{author_id} | 作者 sec_uid | {type}/{mode} | 媒体类型 / 下载模式 |
{year} {month} {day} | 日期拆分 | {timestamp} | 发布时间戳 |
🩹 出问题了先看这儿
主页作品只出来 20 条左右。现象:翻页停在第一页,日志有受限痕迹。根因:抖音对 CLI 的翻页接口有风控,纯 API 拿不全。处理:保持browser_fallback.enabled: true且headless: false,浏览器弹出来时人工完成验证,并把max_scrolls: 240、idle_rounds: 8这类参数调大。再强调一次,Cookie 重跑和反复重试解决不了这个问题,这是门禁不是登录态。
Cookie 失效。现象:请求开始大量报错或要求登录。处理:重跑python -m tools.cookie_fetcher --config config.yml,一分钟解决;每月例行做一次更省事。
下载文件不完整、大小为 0。现象:目录里有空 mp4。根因:网络中断后残留了空文件。处理:保持retry_times: 3起步、redownload_missing_files: true,下载器会检查 Content-Length,空文件在下次增量任务里自动补下。
API 频繁 429 或超时。现象:失败率高、日志大量限流字样。处理:把thread降到 3,retry_times提到 5,配合限速器的固定间隔,宁可慢不要触发风控。
原画下载每条都慢。现象:开了video_quality: original后整体吞吐下降。根因:每个作品多一次 10 秒超时的原片探测请求。处理:接受探测开销,或者改用1080p之类的固定档位跳过探测。
以下是四个核心参数的默认值与调优范围:
| 参数 | 默认值 | 推荐范围 | 调大后果 |
|---|---|---|---|
thread | 5 | 3-8 | 触发限流概率上升 |
retry_times | 3 | 3-5 | 单条失败耗时变长 |
video_quality | highest | 按需 | original每作品多一次探测 |
| 限速间隔 | 0.5s + 抖动 | 勿低于默认 | 风控命中率上升 |
🔌 接入更大的系统
REST 模式一行命令起服务,任务提交后轮询状态即可:
# 启动 REST API 服务(需先装 fastapi 和 uvicorn) pip install fastapi uvicorn python run.py --serve --serve-port 8000# 提交下载任务,返回 job_id 后轮询 /api/v1/jobs/{job_id} curl -X POST http://127.0.0.1:8000/api/v1/download \ -H 'Content-Type: application/json' \ -d '{"url": "https://www.douyin.com/user/YOUR_SEC_UID"}'接口一共四个,全部在 server/app.py 里:
| 方法 | 路径 | 用途 |
|---|---|---|
| POST | /api/v1/download | 提交下载任务 |
| GET | /api/v1/jobs/{job_id} | 查询单个任务状态与结果 |
| GET | /api/v1/jobs | 列出最近任务 |
| GET | /api/v1/health | 健康检查 |
服务侧两个参数值得设:server.max_jobs: 500限制内存里的任务数,job_ttl_seconds: 86400控制完成态任务保留 24 小时。要容器化直接docker build -t douyin-downloader .,仓库根目录的 Dockerfile 就是为此准备的。不走 HTTP 的话,--search/--hot-board导出的 JSONL 也能当最小集成点——一个 shell 脚本加 cron 就是一条离线数据管道。
📋 长期维护清单
- 每月重跑一次 Cookie 抓取命令,别等失效了再动
- 定期
VACUUM或备份dy_downloader.db,它只涨不缩 - 跑久了的
download_manifest.jsonl手动归档一份,它是 append-only 的 - 跟进版本更新,风控门禁在变,兜底策略也在跟着变
- 盯一下下载目录的磁盘占用,原画档体积比转码档大数倍
内容仅建议用于个人存档与学习,注意版权与隐私边界,下载量别大到给平台服务器造成压力。
🚀 项目往哪走
- 给 like、mix、music 等模式补齐浏览器兜底,覆盖更多被门禁的接口(PROJECT_SUMMARY.md 的后续建议里已明确列出)
- manifest 加轮转归档策略,解决长期运行场景的文件膨胀
- 数据库查询 CLI:按作者、日期、标签检索已下载作品
- 桌面端 Douzy 的插件化方向:按平台拆分工作台,部分高级批量能力走插件激活
✅ 收尾
核心就三样:磁盘加 SQLite 双重去重让重复跑任务零成本、限速加抖动的并发模型让批量下载稳得住、append-only 的 manifest 让每一次下载可审计。下一步就干三件事:git clone装依赖、跑通 Cookie 抓取、用最小配置下一个真实博主主页。跑通之后再回来调video_quality和thread,你已经比大多数人理解这个工具了。
【免费下载链接】douyin-downloaderA practical Douyin downloader for both single-item and profile batch downloads, with progress display, retries, SQLite deduplication, and browser fallback support. 抖音批量下载工具,去水印,支持视频、图集、合集、音乐(原声)。项目地址: https://gitcode.com/GitHub_Trending/do/douyin-downloader
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考