因为音视频地址由javascript动态生成,requests无法执行js,故拿不到真实流地址;需用playwright等工具模拟浏览器行为,还原加密逻辑、复用会话头与cookie,并严格控制请求节奏以绕过反爬。

为什么直接用 requests 获取不到音视频流地址?
因为目标页面的音视频地址通常由 JavaScript 动态生成,比如通过 fetch() 或 XMLHttpRequest 请求一个加密接口,再用密钥拼接出真实 URL。你用 requests.get() 拿到的 HTML 里根本没这个地址,只有一堆 div 和占位脚本。
常见错误现象:requests 返回的响应中搜不到 .m3u8、.mp4 或 /video/ 这类关键词;用浏览器开发者工具看 Network 面板却能看到清晰的流请求 —— 这说明服务端做了行为识别或时间戳校验。
- 必须还原 JS 中的加密逻辑(比如 AES 解密、时间戳签名、UA+Referer 组合校验)
- 不能只靠静态 HTML 解析,得模拟完整执行链:页面加载 → JS 初始化 → 接口调用 → 地址生成
- 很多站点会校验
sec-ch-ua、accept-language等现代请求头,缺一不可
用 Playwright 替代 Selenium 的关键原因
Selenium 启动慢、默认不支持拦截响应体、对 WebSocket 和 Fetch 监听支持弱,而音视频流常走 fetch 或 XHR,且需在请求发出前注入 headers 或修改参数。
Playwright 提供了更底层的路由控制能力,比如:
-
page.route()可拦截所有匹配 URL 的请求,并修改headers或直接返回伪造响应 -
page.on("response")能监听到每个响应,包括 200 的.m3u8内容,无需等待 DOM 渲染完成 - 内置 Chromium/Firefox/WebKit 三端,可快速切换验证是否被 UA 特征识别
示例片段(获取 m3u8 地址后立即断开):
page.on("response", lambda r: print(r.url) if ".m3u8" in r.url else None)
await page.goto("https://example.com/video/123")
# 不需要等页面“加载完成”,响应一发出就捕获
如何稳定提取并解密 m3u8 中的 AES 密钥?
很多平台把 KEY 的 URI 做成动态路径,比如带 timestamp、token、sign 参数,且该 URI 本身也校验 Referer 和自定义 header(如 X-Auth-Token)。直接用 requests 下载会 403。
- 必须复用 Playwright 当前上下文的 cookies + headers,尤其是
Cookie和Referer必须与触发 m3u8 请求时完全一致 - 密钥响应通常是二进制,不是 JSON,别用
r.json();要用r.content直接写入文件 - 有些 key 是 base64 编码后再 AES 加密过的,得先 base64 decode,再用 IV 和 key 解密 —— 注意 IV 是否从 m3u8 的
IV=0x...字段里取
容易踩的坑:m3u8 文件里 URI="key?ts=171..." 中的 ts 是秒级时间戳,超过 30 秒即失效,所以拿到后必须立刻请求,不能缓存或延迟。
下载 ts 分片时为什么频繁 403 或连接重置?
不是网络问题,是服务端做了分片级反爬:每个 .ts URL 带独立签名,且签名依赖上一个分片的下载时间、当前系统时间、甚至 TCP 连接复用状态。
- 必须用同一个
session(或 Playwright 的page实例)连续请求,不能换 client - 两个 ts 请求间隔建议控制在 0.3–0.8 秒之间,太快会被限速,太慢导致签名过期
- 某些平台要求
Rangeheader 或特定Accept类型,漏掉就会 406 - 不要并发下载分片 —— 多数服务端会根据 IP+User-Agent+时间窗口做 QPS 限制,单 IP 并发 >2 就可能触发熔断
真正稳定的方案不是“加速”,而是“拟人”:保持 session、控制节奏、复用 TCP 连接、严格同步 referer 和 cookie。加密逻辑和时间窗口一旦错一个参数,整条链就断。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











