视频地址藏在 JS 里?彻底搞懂通用流地址提取的思路与实战
下载器最核心的能力,就是找到真实视频地址。早些年视频多在页面里直接写个 <video src="...">,现在几乎都藏起来了:m3u8/mpd 地址在 JS 变量里、在异步接口返回的 JSON 里、甚至被加密混淆。这篇文章不针对某个具体网站(那会过期),而是把"怎么把流地址抠出来"的通用方法论讲透:从最简单的看源码,到无头浏览器、中间人代理、JS 反混淆。
一、流地址通常躲在哪些地方
- HTML 源码:
<video src="...">或<source>标签; - 内联 JS 变量:
var playInfo = { url: "..." }; - 异步接口:页面加载后 JS 再请求一个 API 拿播放地址;
- 加密/混淆:地址被 base64、AES 或自定义算法处理;
- m3u8 套 m3u8:主 playlist 指向真实切片地址;
- WebSocket / WebTransport:不走普通 HTTP 请求。
二、方法一:先看源码 + 抓包(最低成本)
两板斧:
- 右键"查看网页源代码",搜
m3u8、mpd、.mp4、.ts、playUrl、videoUrl; - 打开开发者工具 → Network,过滤
m3u8/mpd/ts/mpd,往往直接能看到请求。
很多站点其实没那么复杂,抓包就能拿到真实地址。记得带上正常的 User-Agent 和必要 Referer,否则页面可能返回空壳。
三、方法二:正则从 JS 文本里抠
地址在 JS 变量里时,把页面或 JS 文件拉下来用正则:
import re, requests
html = requests.get(url, headers={'User-Agent': 'Mozilla/5.0'}, timeout=15).text
# 匹配常见模式
for pat in [r'"(https?://[^"\' ]+\.m3u8[^"\' ]*)"',
r'url["\': ]+(https?://[^"\']+\.mp4)',
r'playUrl["\': ]+"([^"]+)"']:
m = re.search(pat, html)
if m:
print('found:', m.group(1))
break
注意要带 UA,且很多站点用 JSON 明文传,搜 m3u8 几乎必中。
四、方法三:无头浏览器执行 JS
当地址是 JS 动态生成、或需要登录态触发请求时,静态抓源码没用。用 Playwright/Puppeteer 真正跑一遍页面,让它把请求发出来我们再拦:
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
b = p.chromium.launch(headless=True)
pg = b.new_page()
pg.on("request", lambda r: print(r.url) if ('.m3u8' in r.url or '.mp4' in r.url) else None)
pg.goto(url)
pg.wait_for_timeout(6000) # 等页面触发请求
b.close()
页面一发起 m3u8/mp4 请求,我们就捕获到了真实地址。还能用 pg.context.add_cookies(...) 带登录态。
五、方法四:中间人代理(mitmproxy)
对付强反爬/加密最彻底:让流量过 mitmproxy,直接看明文请求和响应:
from mitmproxy import http
def request(flow: http.HTTPFlow):
u = flow.request.url
if '.m3u8' in u or '.mp4' in u or 'play' in u:
print('[REQ]', u)
def response(flow: http.HTTPFlow):
# 看响应体里有没有地址
if 'application/json' in flow.response.headers.get('content-type',''):
print('[RES]', flow.response.text[:500])
启动 mitmproxy -s script.py,浏览器挂上代理(装好 mitm 证书解密 HTTPS),所有请求一览无余。配合解密 HTTPS 还能看到加密前的参数。
六、解密混淆地址
拿到的是加密串时思路:
- 在 JS 里搜相关变量名/函数名(如
decrypt、parseUrl),定位解密函数; - 用 Node 把那段解密函数跑起来,把密文喂进去;
- 或在 Playwright 里
page.evaluate直接调用页面自带的解密函数拿明文。
七、m3u8 嵌套处理
主 m3u8 经常只是指向清晰度列表(又一层 m3u8)。提取时要递归跟随,选目标清晰度那一层,再下切片。
八、方法对比
| 方法 | 难度 | 适用 |
|---|---|---|
| 看源码/抓包 | 低 | 地址明文 |
| 正则提取 | 低 | 在 HTML/JS 文本里 |
| 无头浏览器 | 中 | 动态生成/需登录 |
| 中间人代理 | 高 | 强反爬/加密 |
九、常见翻车现场
| 现象 | 根因 | 解法 |
|---|---|---|
| 抓包啥都没有 | 走 WebSocket/非 HTTP | 看 WS 帧或换协议分析 |
| 拿到 m3u8 下不动 | 需带 Referer/Cookie | 下载时一并传上(见 referer/cookie 用法) |
| 地址是加密串 | JS 混淆 | Node 跑解密函数 |
| 播放 403 | 防盗链 | 加正确 Referer/Origin |
| 频繁变地址 | 签名/时效性 Token | 提取后立即用,别缓存 |
十、常见问题
Q:为什么有时抓到的是 .mpd 不是 m3u8? 那是 DASH 格式,用 dash 下载器(如 yt-dlp 自带)或 ffmpeg 直接读 mpd。
Q:网站经常改版怎么办? 没有一劳永逸,把提取逻辑做成可配置的"规则",哪变了改哪条规则,比写死健壮。
Q:这样做合规吗? 提取自己有权限观看的内容、做个人离线备份通常没问题;批量抓取收费/版权内容分发则另当别论,务必评估法律风险。
小结
流地址提取是"分层递进"的活:先试最便宜的看源码/抓包,不行上正则,再不行无头浏览器,最硬核上 mitmproxy。核心是理解地址藏在哪一层,然后用对应工具把它逼出来。