网页视频下载这事,看着简单,真做起来全是坑。很多朋友一上来就按F12,在Network里翻半天,以为能找到一个现成MP4地址,结果发现整个页面加载了几百个请求,媒体文件反而像捉迷藏一样找不到。这篇是这个系列的第3篇案例,我拿一个比较有代表性的场景展开:网页里通过m3u8分段加载的视频,怎么完整下载到本地,包括抓地址、合并切片、绕防盗链、处理加密和音视频分离。适合已经会用一点开发者工具、但经常被“403”“奇怪的ts文件”劝退的朋友。看完之后,八成常见的网页视频下载需求你都能自己搞定。
1. 这个案例和前面两篇有什么不一样
1.1 为什么不能右键另存为
早期很多视频网站为了省事,直接把MP4文件放在网页里,浏览器拿到地址后,右键另存或者从开发者工具找到MP4路径就能下载,这也是网上大部分“网页视频下载教程”的适用范围。但现在主流站点基本都换了流媒体播放:视频不再是一个完整文件,而是被切成几秒一段的小视频块,播放器播完一段再去拉下一段,所以你在Network里看到的往往是一堆不断跳出来的小文件,而不是一个MP4。
这个过程有点像自助餐,不是一次性给你端一桌菜,而是后厨按顺序一道道上,你只能吃到当前这道,想打包全集就得拿到那张“菜单”。流媒体里的菜单就叫m3u8,它是一份纯文本清单,记录着每个小视频块的地址和播放顺序。只要把这份菜单抓到,再按顺序把所有分片合在一起,就能还原出完整视频。这个案例的核心思路就四个字:先拿菜单,再拼文件。
1.2 m3u8到底是个什么东西
m3u8的严格叫法是HLS播放列表,最早是苹果提出的HTTP Live Streaming标准,现在基本成了网页点播和直播的通用格式。它本质上是个UTF-8文本文件,你可以用记事本打开。一份最简单的点播m3u8长这样:
#EXTM3U #EXT-X-VERSION:3 #EXT-X-TARGETDURATION:10 #EXTINF:10.000000, segment_0.ts #EXTINF:10.000000, segment_1.ts #EXT-X-ENDLIST以#开头的是指令,#EXTINF后面跟着分片时长和文件名,播放器就按这个顺序把segment_0.ts、segment_1.ts依次拉出来播放。如果文件里的地址是相对路径,需要跟你刚才拿到的m3u8地址做拼接。你还会看到#EXT-X-KEY这样的指令,那是告诉播放器“后面的分片被加密了,用这个密钥解”,这个后面详细讲。
还有一个判断点:#EXT-X-ENDLIST出现,基本说明这是点播视频,也就是已经录制好、不会变长的内容,适合下载;如果始终没有ENDLIST,那多半是直播流,这种方法只能拉到一个时间窗口的画面,意义不大。所以拿到m3u8先别急着下载,花十秒看一眼内容,后面能省很多事。
1.3 动手前先准备好这几样工具
做这个案例我习惯用到三样东西:浏览器开发者工具、FFmpeg、还有一个能发自定义请求的命令行工具。浏览器开发者工具用来抓地址,FFmpeg用来下载和合并,curl或者wget用来排查请求头。FFmpeg是命令行工具,功能很强大,相当于视频界的瑞士军刀。Windows用户可以去官网下载解压包,把bin目录配置到环境变量;macOS可以用brew install ffmpeg,Linux发行版一般也有官方源,apt install ffmpeg或者yum install ffmpeg都行。
装好后打开终端敲一句 ffmpeg -version,能正常输出版本号就算成了。这类工具不需要学得很深,你会用 -i 指定输入、-c copy 直接复制流、-t 剪时长,基本就够应付大多数网页视频下载场景了。实在不想装命令行,也可以找现成的m3u8下载器,但我还是建议至少把FFmpeg跑通,因为你后面排查问题、转格式、混流都会用到它,一劳永逸。
2. 第一步:把视频地址从浏览器里揪出来
2.1 抓包的三个关键动作
用开发者工具找媒体地址,流程可以固定成三步:打开Network面板、刷新页面开始播放、盯住Media和Fetch/XHR两个过滤条件。具体操作是先按F12打开开发者工具,切到Network标签,勾选Preserve log(保留日志),然后按Ctrl+F5强制刷新页面,让所有网络请求重新记录一遍。这时候再点播放,你会在列表里看到一堆segment开头、以ts结尾的小文件,恭喜,那就是视频分片。
在过滤框输入m3u8,如果网站用HLS播放,一般能直接看到列表项;如果没看到,再看Fetch/XHR里有没有类似playlist、index.m3u8的请求。有些网站把视频地址包在blob:https://一堆字符里,src看起来是blob地址,这种直接复制到下载工具里是没用的,因为blob内容只存在浏览器内存中。这种情况要往回追,看是哪个XHR请求返回了m3u8或者MPD,真正能用的地址藏在那个响应里。
如果没有看到任何媒体相关请求,先确认是不是缓存问题,清一次缓存再刷新;如果视频来自第三方iframe,比如某些平台的播放器是嵌套进来的,可能请求记录在另一个文档里,可以切到frame选择器去看。经验上,90%的网页视频都能在Network里找到答案,找不到更多是过滤条件没调对。
简单提一句,有些视频页面里有个
2.2 打开m3u8之后怎么看
找到m3u8地址后,新开一个标签页直接打开,通常要么下载一个文本文件,要么浏览器直接展示文本内容。先别管那些堆成山的分片地址,重点看几个指令。第一,如果内容里只有一行子播放列表地址,比如一个叫index.m3u8的入口文件指向了quality_720p.m3u8,那说明这是多码率主列表,还需要再打开子列表才是真正要下载的清单。第二,看有没有#EXT-X-KEY,有的话说明分片是加密的,后面需要带上密钥信息。第三,看是点播还是直播,有没有#EXT-X-ENDLIST,这个刚才说过了。
可能有人问,地址拿到了直接复制到下载软件里不就行了吗?如果是公开地址,确实可以,但很多网站会给地址加上防盗链。你用浏览器访问没问题,因为浏览器自动带上了正确的Referer、User-Agent、Cookie。脱离浏览器环境直接请求,服务器一看来源不对,直接回一个403。所以只找到地址不算完,还要把请求头一起带走,这一步是这个案例里最实战的部分。
2.3 顺手处理快进、全屏暂停、禁止拖动
调试视频的时候有几个小技巧很实用。想在网页里快速定位视频内容,别再手动拖进度条了,直接在Console执行:
document.querySelector('video').currentTime += 60;这句代码会把当前播放位置往后跳60秒,想跳多久改数字就行。想倍速播放就执行 video.playbackRate = 2。这种操作对下载也有用,比如你想确认视频中间某个片段是否正常加载,可以先把进度跳过去再看Network请求。至于“网页视频被其他全屏后暂停播放”,多半是播放器监听了fullscreenchange事件主动调用了暂停,或者全屏切换导致焦点变化,在Console里临时给video对象挂一个play回调可以缓解。
有网站会禁止拖动进度条,本质上是监听dragstart之类的鼠标事件然后阻止默认行为。你可以在Console里临时把这些事件处理函数置空,或者直接用播放器自带的键盘快捷键。不过要记住,这些操作都只是前端临时改动,刷新页面就恢复。它们解决的是“看视频不方便”的问题,而不是“下载视频”的问题,别指望靠这点操作绕过所有限制。
3. 第二步:用FFmpeg把切片拼成完整视频
3.1 一条命令搞定基本下载
拿到m3u8地址并且确认没有网络限制之后,最简单的下载命令是:
ffmpeg -i "https://example.com/path/index.m3u8" -c copy output.mp4FFmpeg会自动读取m3u8列表,把里面的所有ts分片下载下来,然后按顺序封装成output.mp4。-c copy的意思是视频和音频流都不重新编码,直接拷贝进新容器。这么做的最大好处是速度快,几个小时的长视频,拷贝模式下几分钟到十几分钟就能完成,而且画质无损。如果-c copy失败,比如输出格式不支持某些编码,才考虑去掉-c copy,用libx264重新编码,但转码非常耗时,属于最后手段。
我在实际操作中基本都会把视频地址用英文双引号包起来,因为很多m3u8地址里带&和?参数,不加引号命令行会拆出问题。分片数量特别多的时候,FFmpeg可能会因为某个切片超时卡住,可以加上网络超时参数,比如-rw_timeout 10000000表示网络读取超时上限10秒。如果还是经常断,优先考虑网络原因,降低并发、多试几次,或者找一个更稳定的网络环境。有些m3u8清单里分片数量能到几百上千个,FFmpeg默认是串行下载,耐心等就是。
3.2 防盗链处理:把浏览器的请求头还回去
第3篇案例要重点强调的事来了:大多数下载失败不是命令写错,而是没有把请求头补全。浏览器访问一串m3u8时,服务器会校验来源页面、UA、登录Cookie。命令行工具访问的时候,这些信息全都没有,于是服务器回了403。解决的思路很简单,把浏览器里那个m3u8请求的请求头原样搬过来。
最省事的做法是在Network里找到那个m3u8请求,右键选择Copy as cURL,这样会复制出一条完整curl命令,里面带好了所有请求头。你可以在终端里先跑一下这条curl,正常能拿到内容,再把里面的-H参数转成FFmpeg能用的形式。比如:
ffmpeg -headers "Referer: https://example.com/page/123" -headers "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)" -i "https://example.com/path/index.m3u8" -c copy output.mp4如果网站需要登录,还得把Cookie加进去,同样是复制浏览器里的值。这里有个细节:很多平台给m3u8和分片地址签了时效token,地址后面会带一串expire和sign参数,这种链接可能过几分钟就失效。所以最佳节奏是抓到一个可用地址后,马上复制请求头,立刻开始下载,别等研究完再去,不然又是一堆新的403。真遇到失效,重新刷新页面再抓一次就好。
3.3 加密切片和音视频分离怎么办
m3u8里有#EXT-X-KEY指令,说明分片是AES-128加密的。FFmpeg对这个情况的处理比较智能,它看到KEY里的URI后会自动去请求密钥文件,然后用密钥解密每个分片。前提是密钥文件地址能正常访问。如果密钥请求也403,那还是防盗链问题,通常FFmpeg在请求密钥时会沿用m3u8的请求头设置,所以携带Referer和User-Agent后一般就能解决。万一遇到个别站点对密钥请求单独校验,可以先把密钥文件通过浏览器下载下来,再用其他工具手动解密,不过这种情况很少见,先不用学那么深。
另一个更常见的问题是音视频分离。很多自适应码率的站点,会把视频画面和音频分成两个不同的m3u8,播放器分别拉取再同步播放。一个典型现象就是FFmpeg下载完,文件播放只有画面没有声音。解决办法是在Network里多找一条涉及audio、sound、m4s之类关键词的m3u8,分别下载成两个文件,再用FFmpeg合并:
ffmpeg -i video.mp4 -i audio.m4a -c copy combined.mp4判断有没有音视频分离的方法很简单:看m3u8主列表内容,如果里面包含多行带RESOLUTION和CODECS属性的条目,并且某些行里音频相关信息和视频不同,那就是多路流。这种时候与其硬用一条命令下载,不如先把视频流和音频流分开抓,再合并,成功率会高很多。
4. 常见问题与排错实录
4.1 最常见的四类报错对照
这个案例做多了,常见错误就那么几类,整理成表格:
| 报错现象 | 大概率原因 | 解决思路 |
|---|---|---|
| 403 Forbidden | 防盗链或签名过期 | 补全Referer、User-Agent、Cookie,重新抓取新地址 |
| 404 Not Found | 地址拼接错误或切片过期 | 去m3u8里核对相对路径,刷新页面重新抓包 |
| 解析m3u8失败/不支持标签 | m3u8格式特殊或文件损坏 | 换一个下载器,或用文本打开检查是否完整 |
| 连接超时/中途卡住 | 网络不稳或分片并发过高 | 加超时参数、减少并发、重新执行下载 |
403是出现频率最高的一种,你只要记住它不代表“不能下载”,只代表你的请求不被认可,把浏览器的请求头原样带过去,多数情况下就能通过。404里有个容易被忽略的点:m3u8文件里写的分片路径是相对路径,你自己拼URL时一旦拼错,FFmpeg就会找不到文件。好消息是大多数FFmpeg版本会自动拼接相对路径,但如果你手动改过地址,就要注意。连接超时在高码率视频里也常见,分段太多加上网络波动,就会下到一半卡住。我的习惯是先重试一次,如果还卡,就用专门的下载器或者降低视频清晰度,别跟一条地址死磕。
4.2 下载完打不开、没有声音、音画不同步
下载完成的文件如果打不开,先看看文件后缀和实际封装格式是否匹配。FFmpeg输出时如果写的是MP4,但源流里有某些不被支持的编码,文件播放可能会有问题。遇到这种情况,我一般会把输出格式改成MKV再试:
ffmpeg -i input.ts -c copy output.mkvMKV的兼容性很好,对各类编码流基本都能装。如果播放器还是不认,再考虑用ffprobe看一下编码信息,ffprobe是FFmpeg自带的检测工具,执行ffprobe -show_streams output.mkv就能看到画面和音频用的什么编码。没有声音的问题刚才说了,优先去查音视频是否分离。音画不同步多发生在下载过程中网络抖动导致某些分片损坏,稳妥的办法是删除文件重新下载,不要在残缺文件上反复折腾。也有极少数情况是切片本身时间戳混乱,用ffmpeg -fflags +genpts可以重新生成时间戳,但效果不保证。
4.3 右键被禁、控制台被卡怎么破
有些网站为了防小白,会禁止右键,甚至打开F12后不断进入debugger暂停。这些基本都是前端小把戏。右键禁用不影响F12快捷键,你一样可以打开开发者工具。遇到debugger反复暂停,在Sources面板里按Ctrl+F8停用所有断点,就能正常操作。如果你发现Network里明明在播放视频却看不到媒体请求,优先怀疑缓存,开一个无痕窗口再抓,往往就有结果了。
还有一种情况是视频用iframe嵌套,请求记录不在主文档里。开发者工具里有一个frame选择下拉框,切换到视频所在的那个frame,Network记录就会显示对应子文档的请求。这个点卡住了不少新手,我特意在这里写出来,希望你能少绕点路。记住一个原则:所有前端限制都只能增加操作成本,不能真正保护视频,因为播放视频终究要把数据传到浏览器端。真正的下载限制靠的是服务端鉴权和加密,这也是我们要去看请求头和m3u8内容的原因。
5. 进阶玩法:批量下载和自动化
5.1 用脚本批量下载整季课程
如果只是下一两个视频,手动操作就够了。但你可能会遇到一个系列有几十集的情况,这时候逐个复制地址再执行命令,效率太低了。如果分页URL或者m3u8地址有规律,可以直接写一个循环脚本。比如某个课程的地址规律是/course/lesson01/index.m3u8到/course/lesson20/index.m3u8,那么在bash里这样写:
#!/bin/bash base="https://example.com/course/lesson" for i in $(seq -w 1 20) do ffmpeg -headers "Referer: https://example.com" \ -i "${base}${i}/index.m3u8" \ -c copy "lesson_${i}.mp4" doneseq -w会把数字补成01、02这样的两位格式,方便对齐文件名。跑之前一定要先单集验证一遍,确认请求头、地址规律都没错,再开批量。批量下载时建议在循环里加一句sleep 3,避免短时间请求太频繁被站点限流。这行里有一句话叫“下载五分钟,封IP两小时”,虽然这个案例一般不会到封IP的程度,但尊重对方服务器的压力总没错。
5.2 Python辅助脚本怎么个思路
有些场景用bash不太合适,比如地址需要通过Post请求接口获取、需要先登录拿token、或者文件名要从页面标题里提取,这时候用Python脚本更顺手。核心逻辑不复杂:用requests请求接口拿到m3u8地址,再交给FFmpeg子进程下载,或者自己解析m3u8后逐个下载分片再合并。直接贴一段可跑的代码没意义,因为不同平台的接口完全不一样,但思路是一致的:
1. 模拟登录或者直接复用浏览器Cookie 2. 解析出视频页面里的m3u8接口地址 3. 请求接口拿到最终m3u8 4. 调用FFmpeg下载并合并复用浏览器Cookie的方式很简单,从开发者工具里任意请求的请求头里复制Cookie字段,粘贴到脚本的headers里,就能在脚本里模拟登录态。这个做法对付需要登录验证的站点时非常管用。整个脚本体量通常在一百行以内,比纯手动操作省心得多。当然,如果你对命令行更熟,直接复制curl命令也能达到同样效果。工具只是手段,核心是对请求流程的理解。
6. 处理案例时我的几个固定习惯
我自己做这类下载案例的时间越长,越觉得最重要的不是记命令,而是会看协议。m3u8看得懂、请求头补得齐、加密和音视频分离遇到时能有应对方向,任何网页视频到手里都只是时间问题。我自己的固定习惯是:先花十秒看m3u8内容,再决定下一步,而不是拿到链接就盲跑命令;遇到403先补头,遇到怪异文件先查分片列表,基本能解决九成问题。
最后再分享一个小技巧:下载前先想清楚自己是不是真的有权限获取这个内容。工具本身是中立的,但用在哪、怎么用,决定了很多事情。学习自用是一码事,拿别人的版权内容去传播或者商用就是另一码事。把技术用在合法合规的事上,你才能安心地一直玩下去。