# 网页音视频下载破解原理 > 基于 `jiji262/douyin-downloader` (7.9k⭐) 源码拆解 > 总结通用下载原理,可用于任何网页的音视频/文件下载 > 归档时间:2026-06-10 --- ## 核心问题 网页下载音视频的**本质矛盾**: | 你的需求 | 网站的反制 | |---------|-----------| | 拿到媒体文件URL | 隐藏真实URL,使用加密签名 | | 下载完整内容 | 分片加密、防盗链、CORS | | 批量自动化 | 验证码、频率限制、User-Agent检测 | | 长期稳定 | 签名算法不断更新 | 抖音的防护体系是典型的**多层反爬**,拆解它的攻防逻辑,可以举一反三。 --- ## 一、抖音防护体系全景 ``` ┌──────────────────┐ │ 反爬第一层 │ │ IP检测+UA检测 │ └────────┬─────────┘ │ 通过 ▼ ┌──────────────────┐ │ 反爬第二层 │ │ X-Bogus签名 │ │ (旧版RC4混淆) │ └────────┬─────────┘ │ 被发现/破解 ▼ ┌──────────────────┐ │ 反爬第三层 │ │ A_Bogus签名 │ │ (SM3国密哈希+ │ │ 浏览器指纹) │ └────────┬─────────┘ │ 通过 ▼ ┌──────────────────┐ │ API返回数据 │ │ aweme_detail │ │ └─ play_addr │ │ └─ url_list │ │ ↓ 还有一层 │ │ 需要带签名+ │ │ 特定header重定向│ └────────┬─────────┘ ▼ ┌──────────────────┐ │ 真实媒体服务器 │ │ 返回MP4/MP3流 │ └──────────────────┘ ``` --- ## 二、核心机制拆解 ### 2.1 第一层:基础防护 **网站的检测:** ```python User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) # 必须是真实浏览器 Referer: https://www.douyin.com/ # 必须从本站跳转 Cookie: sessionid=xxx; ttwid=xxx # 必须有登录态 ``` **破解:** 模拟真实浏览器的请求头,提供合法的Cookie。 ### 2.2 第二层:X-Bogus 签名(旧版) > 位置:`utils/xbogus.py`(约200行) **原理:** ``` 输入:URL参数 + User-Agent 过程: 1. 对UA做RC4类混淆(密钥 \x00\x01\x0c) 2. 对URL参数做MD5 3. 用自定义Base64字符表编码 4. 插入混淆数组 输出:URL参数上追加 X-Bogus=xxx ``` **关键代码:** ```python _ua_key = b"\x00\x01\x0c" # UA加密密钥 _character = "Dkdpgh4ZKsQB80/Mfvw36XI1R25-WUAlEi7NLboqYTOPuzmFjJnryx9HVGcaStCe=" # 自定义Base64 ``` **破解难度:** ⭐⭐ 已经被广泛破解,抖音已升级到A_Bogus。 ### 2.3 第三层:A_Bogus 签名(当前版)⭐ > 位置:`utils/abogus.py`(约800行) > 这是抖音当前使用的主力签名算法 **全景流程:** ``` 输入:URL参数 + User-Agent + 浏览器指纹 + 请求头 ┌─────────────────────────────────────────────────────────┐ │ A_Bogus 生成流程 │ │ │ │ 1. 浏览器指纹生成 │ │ └─ 随机viewport尺寸(1024-1920 x 768-1080) │ │ └─ 窗口内外尺寸差异 │ │ └─ 平台标识(Win32/MacIntel) │ │ └─ 输出: "1536|742|1560|828|0|0|0|0|1536|742|..." │ │ │ │ 2. 加密请求体(array1) │ │ └─ URL参数 → 编码转换 → 插入混淆数组 │ │ │ │ 3. 加密UA(array3) │ │ └─ User-Agent → \x00\x01\x0e 密钥加密 │ │ │ │ 4. SM3哈希运算(国密标准) │ │ └─ 对加密后的请求体+请求头+UA做SM3哈希 │ │ └─ SM3 = 中国国家密码标准,类似SHA-256的替代品 │ │ └─ 输出32字节固定长度哈希值 │ │ │ │ 5. 自定义Base64编码 │ │ └─ character1: "Dkdpgh2ZmsQB80/MfvV36XI1R45-WU..." │ │ └─ character2: "ckdp1h4ZKsUB80/Mfvw36XIgR25+WQ..." │ │ └─ 双重编码,用两个不同的字符表 │ │ │ │ 6. 排序混淆 │ │ └─ sort_index: [18,20,52,26,30,34,58,38,...] │ │ └─ 对编码结果按固定索引重排 │ │ │ │ 7. 追加到URL │ │ └─ 原始URL + "&a_bogus=" + 签名结果 │ └─────────────────────────────────────────────────────────┘ ``` **破解难度:** ⭐⭐⭐⭐ - 需要完整实现SM3哈希(Python的`gmssl`库) - 需要模拟浏览器指纹 - 需要理解混淆数组的排列逻辑 - 自定义Base64字符表不是标准RFC **为什么纯Python能实现:** 签名算法是完全确定的——输入相同,输出必相同。没有用到浏览器特有的能力(如WebGL、Canvas指纹),所以可以在服务器端用纯Python模拟。 ### 2.4 第四层:空响应检测 抖音API的一种特殊反制:**HTTP 200 但返回空内容**。 ```python # api_client.py 中的处理 if response.status == 200: body = await response.read() if not body: # 空200是反爬信号,重试+新签名 logger.warning("Empty 200 response — anti-bot, retrying") ``` **破解:** 检测到空响应后,重新生成签名重试。 ### 2.5 第五层:媒体URL的防盗链 API返回的`play_addr.url_list`里包含媒体URL,但直接访问可能被拦截。 ```python # 下载器中的重定向处理 # 用curl -L 自动跟随302重定向 # 需要带正确的Referer和Cookie headers = { "User-Agent": "...", "Referer": "https://www.douyin.com/", # 有些还需要 Range header 做分片下载 } ``` --- ## 三、通用下载方法论 从抖音的攻防中,可以总结出**破解任何网页下载的通用方法论**: ### 3.1 信息收集阶段 ``` 1. 打开浏览器DevTools → Network面板 2. 过滤媒体类型(XHR/JS/Media) 3. 找出API请求 → 分析URL参数 4. 找出媒体请求 → 看URL模式 5. 关注 Request Headers / Response Headers ``` ### 3.2 签名分析 ``` URL参数中有可疑参数? → x-bogus, a-bogus, _signature, token, nonce, ts │ 是否是时间戳? → ts, _t, timestamp (可模拟) │ 是否是哈希? → MD5/SHA1/SHA256/SM3 (需复现算法) │ 是否有自定义编码? → 自定义Base64字符表 (逆向JS) │ 是否依赖浏览器API? → WebGL/Canvas指纹 (需实浏览器) ``` ### 3.3 常见签名类型及破解难度 | 类型 | 例子 | 难度 | 破解方式 | |------|------|------|---------| | 时间戳+MD5 | `&ts=123456&sign=md5(url+key)` | ⭐ | 复现拼接逻辑 | | HMAC | `&sign=hmac(key, params)` | ⭐⭐ | 找密钥,复现算法 | | JS混淆 | eval+各种变形 | ⭐⭐⭐ | 用Node.js执行或JSDebugger | | **A_Bogus类** | 国密+指纹+自定义编码 | ⭐⭐⭐⭐ | 纯Python复现 | | Canvas指纹 | 需浏览器渲染 | ⭐⭐⭐⭐⭐ | 无头浏览器 | | WebGL指纹 | 需GPU支持 | ⭐⭐⭐⭐⭐ | 无头浏览器+GPU | ### 3.4 工具选择策略 ``` 分析结果 → 选择方案 │ ├─ 签名可在Python复现 → 纯代码下载(推荐,最快最稳) │ ├─ 签名需要JS执行 → Node.js + puppeteer 或 CDP浏览器 │ ├─ 需要浏览器完整环境 → CDP浏览器 + Playwright │ └─ 以上都不行 → 录制屏幕+OCR(最后手段) ``` ### 3.5 完整的攻防工具箱 | 技术 | 适用场景 | 工具/库 | |------|---------|--------| | 纯HTTP请求 | 无签名或签名可复现 | `requests`, `aiohttp`, `curl` | | 自定义签名算法 | A_Bogus类 | 逆向JS,用Python复现 | | Node.js执行 | JS混淆签名 | `execjs`, `pyexecjs` | | 无头浏览器 | Canvas/WebGL指纹 | `playwright`, `selenium` | | CDP协议控制 | 需要真实浏览器 | Chrome DevTools Protocol | | FFmpeg | 音视频提取/转码 | `ffmpeg -i video.mp4 -q:a 0 -map a audio.mp3` | | yt-dlp | 已知站点下载 | 支持1000+网站 | | 代理/IP池 | 频率限制 | SOCKS5, 代理轮换 | --- ## 四、抖音下载的完整技术栈(参考实现) ``` 工具:jiji262/douyin-downloader (Python) 技术栈: ├─ aiohttp → 异步HTTP客户端(高性能) ├─ gmssl → SM3国密哈希(A_Bogus核心) ├─ asyncio → 异步并发(批量下载) ├─ aiofiles → 异步文件IO ├─ imageio-ffmpeg → FFmpeg绑定(音频提取) └─ rich → CLI界面(进度条) 核心流程(简化): 1. CookieManager.get_cookies() → 获取Cookie 2. DouyinAPIClient._build_abogus_url() → 生成A_Bogus签名 3. DouyinAPIClient._request_json() → 调用API 4. URLParser.extract_media_urls() → 提取媒体URL 5. Downloader.download_file() → 下载+重试 6. audio_extraction.extract() → 音频分离 ``` --- ## 五、这个案例给了我们什么启发 ### 5.1 思路层面 1. **逆向工程不是破解,是学习** — 开源项目把签名算法写成了纯Python,我们可以直接借鉴 2. **多重fallback** — A_Bogus不行换X_Bogus,API不行换浏览器回退,设计上考虑了容错 3. **封装成熟工具** — 与其自己造轮子,不如先找GitHub上高星项目看原理 ### 5.2 技术层面 1. **签名算法的本质是可计算** — 只要不依赖浏览器特有的API,就能在服务器端复现 2. **Cookie是命门** — 登录态Cookie是最值钱的信息,要管理好 3. **自定义Base64是常见混淆手段** — 看到非标准的Base64字符表,就去找它的来源 ### 5.3 可复用的经验 | 场景 | 抖音方案 | 通用化 | |------|---------|--------| | 任何需要签名的网站 | A_Bogus (SM3+指纹) | 分析签名逻辑 → 纯代码复现 | | 需要登录态的API | Cookie从CDP提取 | 通用Cookie管理策略 | | 媒体URL有防盗链 | 带Referer/Cookie重试 | 请求头尽可能模拟浏览器 | | API返回空响应 | 重试+新签名 | 反爬触发时换参数重试 | | 需要提取音频 | FFmpeg分离 | 通用音视频处理方案 | --- ## 六、实操清单 当你要下载一个网站的音视频时: ``` [ ] 1. 打开DevTools → Network → 找到媒体请求 [ ] 2. 分析URL参数,找出签名相关参数 [ ] 3. 搜索GitHub看有没有现成项目 (xxx-downloader) [ ] 4. 如果有 → 阅读源码理解签名算法 [ ] 5. 如果没有 → 从JS中逆向签名逻辑 [ ] 6. 用Python/Node.js复现签名 [ ] 7. 用CDP提取Cookie(如果需要登录态) [ ] 8. 测试API调用 → 拿到真实媒体URL [ ] 9. 用curl/requests下载 + 断点续传 [ ] 10. 用FFmpeg提取/转码音频 ```