用 Python 抓网页视频,网页本质就两件事:找到视频文件的视频真实地址,然后把它下载下来。网页听起来简单,视频但实际操作中,网页90% 的视频时间都花在第一步上。
打开浏览器开发者工具,网页切到 Network 面板,视频刷新页面,网页筛选 Media 类型。视频如果运气好,网页你会直接看到一个 .mp4 或 .m3u8 的视频请求。右键复制链接,网页丢给 requests 就能下。视频但更多时候,网页你看到的是一个个几秒钟的 .ts 碎片文件,或者一个 .m3u8 索引文件——这说明视频被切片了。
m3u8 是绕不过去的坎
国内大部分视频网站都用 HLS 协议分发内容,就是把完整视频切成无数个小片段,通过 m3u8 播放列表来组织。你直接下载 m3u8 文件本身没有意义,它只是个文本索引。真正的视频数据藏在那些 .ts 文件里。
处理方式分两种情况。如果 m3u8 里列的是完整 ts 地址,直接遍历下载再合并就行。但很多网站会对 ts 文件做 AES-128 加密,m3u8 里会带一个
#EXT-X-KEY标签,里面包含密钥的 URI。你得先把密钥下下来,再用它解密每个 ts 片段。import requests from Crypto.Cipher import AES # 拿密钥 key = requests.get(key_url).content # 解密单个 ts cipher = AES.new(key, AES.MODE_CBC, iv=key) decrypted = cipher.decrypt(ts_data)注意 IV 的问题。有些站点把 IV 直接写在 m3u8 里,有些用密钥本身当 IV,还有些干脆不写——不写的时候默认用序列号。这块容易踩坑,我第一次搞的时候卡了一下午,最后发现是 IV 搞错了。
requests 不一定够用
有些站点的 ts 请求带 Referer 或 Cookie 校验。你单独拿链接去下,返回 403。这时候得把浏览器里的请求头完整复制过来,尤其是 Referer 和 User-Agent。我习惯用 requests.Session() 保持会话,先把主页请求一遍拿到 Cookie,再去下 ts。
还有一种情况是视频地址在 JS 里动态生成。页面源码里搜不到 .m3u8,但 Network 面板里能看到。这说明地址是前端算出来的。你可以直接在浏览器里断点调试,找到生成逻辑,然后用 Python 复现。懒得逆向的话,selenium 或 playwright 也能解决问题——让浏览器自己跑,你只管拦截请求。
from playwright.sync_api import sync_playwright with sync_playwright() as p: browser = p.chromium.launch() page = browser.new_page() page.on("request", lambda req: print(req.url) if ".m3u8" in req.url else None) page.goto("https://example.com/video/123") page.wait_for_timeout(5000)这招笨但有效。性能差些,可胜在稳定,不用跟混淆代码斗智斗勇。
合并与转码
ts 片段下完后,直接用 ffmpeg 合并最省事:
ffmpeg -i "concat:1.ts|2.ts|3.ts" -c copy output.mp4但片段多了写 concat 列表很烦。更常见的做法是生成一个 filelist.txt,每行写
file 'xxx.ts',然后ffmpeg -f concat -i filelist.txt -c copy output.mp4。如果 ts 本身是加密的,得先解密再合并,顺序别搞反。偶尔会遇到音视频分离的情况——视频流和音频流分别放在不同的 m3u8 里。那就得下两套,最后用 ffmpeg 合并:
ffmpeg -i video.mp4 -i audio.mp4 -c copy final.mp4。几个实际经验
并发下载能快很多,但别开太大。我一般用 8-16 个线程,再高容易被封 IP。加个 0.1 秒的延迟,模拟人类操作。另外记得给每个请求设 timeout,不然某个 ts 卡住会拖死整个脚本。
还有,别拿这技术去下付费内容。技术上能做到和应该去做是两码事。自己录个网课、下个公开讲座,没人管你。批量爬商业平台的付费视频,那就是另一回事了。
最后说个观察:现在越来越多的站点转向 DRM 加密,Widevine 那种。碰到这种,Python 基本束手无策,得靠录屏。所以趁现在还能爬,把该下的公开资源下了吧。
顶: 37679踩: 5






评论专区