ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

【Python爬虫案例】抖音视频下载 + X-Bogus 参数 JS 逆向:从抓包到复现的完整链路

【Python爬虫案例】抖音视频下载 + X-Bogus 参数 JS 逆向:从抓包到复现的完整链路 1. 抖音网页端视频下载为什么会卡在 X-Bogus抖音网页端的视频列表接口aweme/v1/web/aweme/post/不是拿到 URL 就能直接请求的。你在浏览器里打开一个博主主页翻两页Network 面板里能看到请求参数里带了一个X-Bogus长度固定 28 位每次刷新都不一样。少了它服务端直接返回空数据或者风控页随便填一个同样拿不到aweme_list。这个参数就是抖音前端 JS 在发请求前现算出来的签名。它跟 URL 里的 query 字符串、User-Agent、时间戳等都有关系所以你不能写死一个值复用必须把生成逻辑还原出来在 Python 里每次请求前动态算。这篇要解决的就是这条链路怎么在浏览器里定位到 X-Bogus 的生成入口怎么把混淆过的 JS 抠出来在 Node 环境跑通怎么用 Python 的execjs调用它最后拼出完整请求把视频真实地址拿到并下载。适合已经会写基础 requests 爬虫、想搞明白 JS 逆向签名这一环的人。全程只讲技术链路请求频率自己控制别拿去批量刷。我试过直接抄网上现成的 X-Bogus 算法版本一变就废所以更推荐你把「定位入口 → 抠 JS → 本地复现」这套方法学会换参数也能自己跟。2. 抓包定位从 XHR 筛选到加密函数入口2.1 筛选目标接口打开目标博主主页F12 进 Network勾选 XHR然后在过滤框输入post。翻页时会出现形如https://www.douyin.com/aweme/v1/web/aweme/post/?device_platformwebappaid6383...点开这个请求看 Payload/Query String Parameters里面有一堆device_platform、aid、sec_user_id、max_cursor、msToken最后一个就是X-Bogus。对比两次翻页请求你会发现只有X-Bogus、max_cursor、msToken在变其余基本固定。max_cursor是翻页游标msToken是另一个风控参数本篇先聚焦 X-Bogus。2.2 用启动器Initiator找调用栈直接在 Sources 里全局搜X-Bogus往往搜不到因为代码被混淆了字符串是拼接出来的。更靠谱的是用 Initiator在 Network 里点开那个 post 请求切到 Initiator 标签能看到一条调用栈。点最上面一层会跳到发起 ajax 的位置。这里通常是一个被混淆的XMLHttpRequest封装X-Bogus在发送前已经被塞进 URL 了。如果调用栈太深用 XHR 断点更直接Sources → XHR/fetch Breakpoints → 添加URL 填aweme/v1/web/aweme/post。刷新页面请求发出前会断下此时在 Call Stack 里逐层往上翻找到计算签名的那一层。2.3 日志断点确认生成结果断下来之后在可疑的那行右键 → Add logpoint输入console.log(_0x2458f0[apply](_0xc26b5e, _0x1f1790))继续执行Console 里会打印出一串 28 位字符串形如DFSzswVOljJANtOstup5PBt/pL3I跟请求里的 X-Bogus 一致说明找对了函数。输出太多时用条件断点因为 X-Bogus 长度固定 28_0x2458f0[apply](_0xc26b5e, _0x1f1790).length 28这样只在真正生成签名时停下。顺着_0x2458f0往上找它的定义最终会定位到一个加密函数入参是拼好的 query 字符串出参就是 X-Bogus。3. 把混淆 JS 抠出来在本地跑通3.1 复制核心 JS 文件定位到加密函数后看它来自哪个文件通常是webmssdk.es5.js这类。把这个文件的全部代码复制到一个新建的douyin.js里。直接node douyin.js会报一堆错因为它是给浏览器环境写的缺window、document、navigator。3.2 补齐浏览器环境在douyin.js顶部补上这些window global; document {}; document.addEventListener function () {}; navigator { userAgent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36 };再执行报错会少很多。差什么补什么常见还有location、screen按报错逐个补空对象或简单实现即可。3.3 暴露生成函数找到之前那个加密函数的位置在文件末尾把它挂到全局方便 Python 调用window.xiaoc function (params) { return _0x5a8f25(params); // 换成你实际定位到的函数名 };注意这里的userAgent必须和后面 Python 请求头里的 UA 完全一致否则算出来的签名服务端不认。4. Python 调用 JS 并拼出完整请求4.1 用 execjs 调用先装依赖pip install PyExecJS requests调用代码import execjs with open(douyin.js, encodingutf-8) as f: js_data f.read() js_compile execjs.compile(js_data) params device_platformwebappaid6383channelchannel_pc_web... xb_data js_compile.call(window.xiaoc, params) print(xb_data) # 应输出 28 位字符串如果这里报ReferenceError回到第 3 步继续补环境如果输出长度不是 28检查传入的params是否和浏览器里完全一致顺序、编码都不能变。4.2 请求头模板headers { User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36, Referer: https://www.douyin.com/user/MS4wLjABAAAAF0zlK2_5qCr5Lqy6zLNMH8W146aOetdfKSX95jYXwi8, Cookie: 你的Cookie }UA 必须和 JS 里写的一致。Cookie 从浏览器复制包含msToken、ttwid等缺失会触发风控。4.3 拼接 URL 并发请求import requests url https://www.douyin.com/aweme/v1/web/aweme/post/? params device_platformwebappaid6383channelchannel_pc_websec_user_idMS4wLjABAAAAF0zlK2_5qCr5Lqy6zLNMH8W146aOetdfKSX95jYXwi8max_cursor0count18... xb_data js_compile.call(window.xiaoc, params) full_url url params X-Bogus xb_data response requests.get(urlfull_url, headersheaders) print(response.json())params里的字段顺序要和浏览器抓到的保持一致X-Bogus是对整个 query 串算的顺序变了签名就错。5. 验证签名有效性并下载视频5.1 判断签名是否生效请求返回后看 JSON 里有没有aweme_list。有且非空说明 X-Bogus 有效返回{status_code: 8}或空列表多半是签名错、Cookie 过期或 UA 不匹配。先用一个真实视频链接跑通单次请求再考虑翻页。5.2 提取视频地址douyin_videos response.json().get(aweme_list, []) for v in douyin_videos: print(v[video][play_addr][url_list][0])play_addr.url_list里通常有多个备用地址取第一个即可。5.3 下载到本地import os os.makedirs(./DouYin, exist_okTrue) for v in douyin_videos: title v[desc].replace(/, _) video_url v[video][play_addr][url_list][0] res requests.get(video_url, headersheaders) try: with open(f./DouYin/{title}.mp4, wb) as f: f.write(res.content) print(f视频 {title} 下载完成) except Exception as e: print(f视频 {title} 下载出错 {e})标题里可能有/等非法字符先替换掉再当文件名。6. 本篇常见报错排查execjs 报ReferenceError: window is not defineddouyin.js顶部没补window global或者补的位置在引用之后。放到文件最前面。签名长度不是 28传入的params和浏览器不一致重点检查字段顺序、是否漏了msToken、URL 编码是否被二次处理。返回空aweme_listCookie 过期、UA 与 JS 不一致、X-Bogus算错三者之一。先用浏览器复制的最新 Cookie 重试。max_cursor翻页拿不到新数据把上一次响应里的max_cursor传给下一次请求同时重新算 X-Bogus不能复用。Node 执行报document is not defined补document {}和document.addEventListener function(){}按报错继续补location、screen。请求被重定向到验证页请求频率过高触发风控降低频率、加随机间隔别用多线程猛刷。排障和接入细节可以对照官方接入文档https://taotoken.net/api API Keys 在 https://taotoken.net/api-keys 管理。想先验证模型返回结构用模型对话页面 https://taotoken.net/chat 试跑长期做编码或 Agent 任务看 Coding Plan https://taotoken.net/coding-plan 。翻页批量下载时把max_cursor循环更新、每次重算签名再配合time.sleep控制节奏基本就能稳定拿到一个博主的全部视频列表。评论接口是另一套参数思路一样先抓包定位签名再复现。
返回列表