M3U8批量下载:从索引解析到解密合并的实战指南
简介这是一款面向流媒体下载场景的 M3U8 批量视频下载工具主要解决 m3u8 播放地址解析、真实链接获取以及视频转码播放问题适合需要批量保存网络课程、直播回放或多集视频的用户也适用于内容创作者、运维人员整理视频素材工具本身绿色小巧无需复杂安装即可使用。资源包为 ZIP 格式压缩包共 13 个文件大小 23.6MB其中可执行程序用于主程序和辅助下载组件JSON 与 CONF 配置项用于解析规则和下载参数LOG 日志记录请求与错误信息DAT、SESSION 保存运行状态与会话数据整体结构简洁、便于按需调整。目前已有 3478 人学习/下载说明该工具在真实下载场景中有一定使用基础。下载后可获得可直接运行的客户端及内置转码与下载能力支持批量拉取 m3u8 流并合并转码为常见视频格式同时借助请求日志和错误日志可快速定位解析失败、超时等异常便于日常排障和二次开发。1. M3U8批量视频流下载先弄懂切片索引再决定怎么下给某视频平台的几百节网课做离线归档时浏览器 F12 里全是 .m3u8 流。M3U8 本身不是一个视频文件而是 HLS 协议下的播放列表真正的内容被切成几秒一个的 .ts 切片索引文件里记录着切片地址、时长、加密方式等信息。单文件手动下载还能忍几百个视频、每个几十个切片靠手工 curl 等于给自己找罪受。M3U8 批量视频流下载要解决的核心就三件事解析索引、并发拉切片、按序解密合并。这篇文章会把工具选型、自写脚本的参数细节以及加密、防盗链、断流这几个高频坑都摊开讲。适合做视频素材归档、爬虫工程和流媒体测试的开发者新手可以照着跑熟手直接跳去第 4 章看坑。2. M3U8 流媒体到底存了什么切片索引与多线程下载原理2.1 m3u8 文件的真实结构VOD 与直播的差别一个典型的点播 m3u8 长这样#EXTM3U #EXT-X-VERSION:3 #EXT-X-TARGETDURATION:10 #EXT-X-MEDIA-SEQUENCE:0 #EXT-X-KEY:METHODAES-128,URI/keys/key.php?tokenabc123 #EXTINF:10.0, segment000.ts #EXTINF:10.0, segment001.ts #EXT-X-ENDLIST逐行拆解#EXTM3U 是文件头声明这是 m3u8#EXT-X-VERSION 是协议版本VOD 流一般是 3 或 4#EXT-X-TARGETDURATION 表示每个切片的最大时长这里是 10 秒#EXT-X-MEDIA-SEQUENCE 是起始序号也是 AES 加密时默认 IV 的计算基准#EXT-X-KEY 声明了加密方法和密钥地址#EXTINF 后面的数字是切片时长下一行是切片文件地址可能是相对路径最后一行 #EXT-X-ENDLIST 表示这是点播流可以完整下载。直播流没有 #EXT-X-ENDLIST索引末尾会不断追加新切片所以不能按点播方式一口气下完。判断手里的资源能不能批量下载第一件事就是看 m3u8 最后有没有这个标签。还有一点容易被忽略切片地址经常是相对路径比如只写了 segment000.ts实际完整地址需要和 m3u8 的 URL 做 urljoin 拼接。下载前把切片地址全部展开成绝对 URL能省掉后面一大半 404 问题。2.2 工具选型一行命令、批量专用还是自己写脚本面对一个 m3u8 链接常见有三条路直接用 ffmpeg 一条命令拉完、用批量下载器处理多文件场景、自己写 Python 脚本定制流程。三条路适合的场景完全不同先看对比方案适合场景优点缺点ffmpeg 单文件临时下一个视频命令简洁无需开发批量要写循环加密和重试支持弱批量下载器多文件、带加密或防盗链内置并发、重试和密钥处理参数黑匣子定制逻辑受限自写脚本对接内部系统、特殊校验完全可控能定位每个失败点开发维护成本高ffmpeg 命令本身很简单ffmpeg -i https://example.com/video/index.m3u8 -c copy -bsf:a aac_adtstoasc output.mp4-c copy 表示不转码直接复制流速度快aac_adtstoasc 是处理 ts 流里 ADTS 音频头到 mp4 ASC 格式的常见修复不加这个参数部分播放器里会出现有画面没声音的问题。这条命令对单个视频足够但批量下载几百个文件时循环里一旦某个切片请求失败ffmpeg 默认直接退出不会自动重试。批量下载器在并发、重试、密钥处理上做了封装但参数繁琐遇到特殊签名校验时反而难排查。我一般建议验证流程用 ffmpeg正式批处理用脚本脚本里把每一步都打日志翻车时能顺着日志定位。2.3 多线程批量下载为什么必须上并发单线程下载慢瓶颈不在带宽而在网络 I/O 等待。每个 ts 请求都要经历 TCP 握手、发送请求、等待响应、读取内容这四步几十个切片串行执行光是握手延迟就吃掉大量时间。多线程本质是让等待时间重叠把 I/O 等待期让给其他请求吞吐量能提升好几倍但它不提高单线程的下载速度也不能突破服务端的带宽限制。线程池大小一般取 CPU 核数的 4 到 8 倍我习惯从一个保守值开始试比如 8 个线程观察服务端返回状态再往上调。并发太高会触发服务端的限流出现大批 503 或连接被重置。另外每个下载请求要带上独立的请求头User-Agent 和 Referer 都要模拟真实浏览器有些服务端会校验这两个字段缺失直接拒绝连接。批量场景下把请求头和重试逻辑封装成独立函数比在循环里散着写更容易维护。3. 手写批量下载脚本解析索引、并发拉片、解密合并3.1 解析 m3u8 索引逐行读比正则更稳解析 m3u8 时很多人第一反应是写正则匹配。实际上 m3u8 的标签格式有变体比如 fMP4 流里有 #EXT-X-MAP 标签部分播放器会在 #EXTINF 前面插入其他标签正则一把梭容易抓空。更稳的方式是逐行循环读def parse_m3u8(url, raw_text): lines [line.strip() for line in raw_text.splitlines() if line.strip()] ts_list [] key_info {method: None, uri: None, iv: None} idx 0 while idx len(lines): line lines[idx] if line.startswith(#EXT-X-KEY): for part in line.split(,)[1:]: k, v part.split(, 1) v v.strip() if k METHOD: key_info[method] v elif k URI: key_info[uri] v elif k IV: key_info[iv] v elif line.startswith(#EXTINF): if idx 1 len(lines): seg lines[idx 1].strip() if not seg.startswith(#): ts_list.append(seg) idx 1 return ts_list, key_info这段代码的逻辑逐行扫描遇到 #EXT-X-KEY 就拆出加密方法和密钥地址遇到 #EXTINF 就取下一行作为切片地址加入列表。返回的 key_info 里有 method、uri、iv 三个字段method 为 NONE 时表示不需要解密AES-128 时则需要单独处理密钥。密钥 URI 是相对路径的情况很常见后面要拼接完整地址。这里有个容易踩的细节#EXT-X-KEY 行里的属性用逗号分隔但属性值本身可能带引号需要去掉引号再存。IV 字段可能不写不写时要用 #EXT-X-MEDIA-SEQUENCE 的值生成初始向量这个放到解密环节处理。3.2 多线程下载 ts 切片重试、临时文件与断点续传切片下载是整个流程里最容易出问题的环节网络抖动、服务端限流、连接被重置都可能让某个 ts 下载不完整。下载函数要同时处理重试和文件校验import requests from concurrent.futures import ThreadPoolExecutor, as_completed from pathlib import Path def fetch_ts(ts_url, save_path, headers, retries3): for attempt in range(retries): try: resp requests.get(ts_url, headersheaders, timeout15) if resp.status_code 200 and len(resp.content) 0: tmp_path save_path.with_suffix(.tmp) tmp_path.write_bytes(resp.content) tmp_path.rename(save_path) return True except requests.RequestException: pass return False def run_download(ts_list, base_url, out_dir, headers, workers8): out_dir Path(out_dir) out_dir.mkdir(parentsTrue, exist_okTrue) task_map {} with ThreadPoolExecutor(max_workersworkers) as pool: for idx, ts in enumerate(ts_list): ts_url ts if ts.startswith(http) else base_url ts save_path out_dir / f{idx:05d}.ts task_map[pool.submit(fetch_ts, ts_url, save_path, headers)] save_path for future in as_completed(task_map): if not future.result(): print(failed:, task_map[future])fetch_ts 的核心设计先把数据写到 .tmp 临时文件下载完整后再重命名成正式文件。这样做的好处是即使某个请求中途失败也不会留下一个半截文件混进合并阶段。每次请求带 timeout15 秒防止某个切片挂起拖住整个线程池重试 3 次对偶发断流够用但如果服务端持续限流重试次数再多也没意义。run_download 里的编号用 00000、00001 这种五位数格式保证按字典序排序时切片顺序不会乱。ts 地址可能是相对路径所以判断它是否以 http 开头不是就拼 base_url。headers 里要带 Referer 和 User-Agent防盗链的资源缺了这两个字段基本必 403。3.3 AES-128 解密与 ts 合并分步校验再封装ts 切片下载完如果是 AES-128 加密直接合并会得到一堆花屏数据必须先解密再合并。解密代码from Crypto.Cipher import AES from pathlib import Path def decrypt_ts(src_ts, out_ts, key_bytes, iv_bytes): cipher AES.new(key_bytes, AES.MODE_CBC, iv_bytes) data src_ts.read_bytes() decrypted cipher.decrypt(data) pad_len decrypted[-1] if 1 pad_len 16: decrypted decrypted[:-pad_len] out_ts.write_bytes(decrypted)AES-128-CBC 解密后需要手动去掉 PKCS7 填充。TS 分片的填充规则是最后一个字节表示填充长度解密后直接按这个值截断。key_bytes 可以从密钥 URI 单独请求获取iv_bytes 优先用 m3u8 里 #EXT-X-KEY 的 IV 字段如果没写就用 #EXT-X-MEDIA-SEQUENCE 的值转成大端序 16 字节作为 IV。解密完合并cat $(ls *.ts | sort) merged.ts ffmpeg -i merged.ts -c copy -bsf:a aac_adtstoasc output.mp4逻辑说明第一步用 cat 把所有解密后的 ts 按序号顺序拼成一个完整 ts第二步用 ffmpeg 转封装成 mp4aac_adtstoasc 是对音频流的封装修复。注意如果 m3u8 里有 #EXT-X-MAP 标签常见于 fMP4 流cat 直接拼接会花屏这种流不能手动合并要保留原始 m3u8 文件直接让 ffmpeg 读取 m3u8 链接做转封装。做脚本的时候要先探测是否存在这个标签再决定走哪条合并路径。4. 避坑加密、防盗链、断流与并发参数4.1 加密和防盗链花屏和 403 是两条线坑 1解密后依旧花屏。现象ts 全部下载成功合并出的 mp4 能播放但画面有大量马赛克或绿屏。原因m3u8 里写了 AES-128 加密但脚本只下载了切片没处理密钥或者密钥 URI 是相对路径没拼对导致用错误密钥解密。解决先打印 key_info 确认 method 和 uri密钥地址要用 m3u8 的 URL 拼接成完整地址IV 没显式给时用 MEDIA-SEQUENCE 生成初始向量。我在这里耗过半小时后来把 KEY 行单独打印出来才发现 uri 是相对路径。坑 2ts 全部返回 403。现象下载日志里每个请求都是 403 Forbidden。原因服务端校验 Referer、User-Agent 或 Cookie请求头不完整直接拒绝。解决从浏览器复制完整的 Referer 和 User-Agent 填进 headers有的站还要 Cookie 字段一并带上。先用 curl -I 测试单个 ts 链接能否通通了再跑批量。遇到校验 Sec-Fetch-Dest 这类浏览器专用请求头的站点requests 模拟不了就直接放弃硬刚看服务端有没有生成签名 URL 的接口。4.2 断流与校验HTTP 200 不代表文件完整坑 3每个 ts 都返回 200合并后播放到某个时间点卡死。现象下载过程没有报错但视频播到中间某处一直转圈。原因服务端限速或连接被重置requests 在响应未读完时断开HTTP 状态码仍是 200但 Content-Length 对不上文件被截断。解决下载后比对本地的实际字节数和响应头里的 Content-Length不一致就重试。服务端不返回 Content-Length 时抽几个相邻切片比对大小突然小一个数量级的基本就是坏的。坑 4合并后音画不同步。现象视频能放但声音和画面逐渐错位越往后越明显。原因某个坏 ts 混进了合并流程时间轴从那里开始错位。解决合并前逐片校验文件非空、解密后长度是 16 的倍数合并后跑一遍 ffprobe 检查时长ffprobe -v error -show_entries formatduration -of defaultnoprint_wrappers1 output.mp4拿这个时长和 m3u8 里所有 EXTINF 时长之和做对比偏差超过 2 秒基本就是有坏切片混进去了。4.3 并发与超时参数不是玄学坑 5并发一高就 503 或直接封 IP。现象8 线程跑得好好的调到 32 线程前 20 个切片正常后面全部 503 Service Unavailable。原因服务端按 IP 限速或限制最大连接数短时间大量请求触发限流。解决从 8 线程起步出现连续 503 就退到 4 线程观察重试日志如果重试 3 次仍失败说明不是偶发断流是触发限流了得降并发而不是加重试。另一个隐蔽问题是 DNS 解析抖动间歇性解析失败导致某些切片 connect 不上表现为随机几个 ts 报错。我一般给 Session 挂自定义 DNS 解析或者切换到系统 DNS 再跑一次错误率能明显降下来。超时设置也要单独测timeout 设太短会误杀慢速切片设太长会让线程池卡死15 秒对大多数 VOD 流是合适的起点。5. 进阶给批量下载加上校验、增量与定时任务5.1 下载后的三步自检批量任务跑完不代表结束要做一个快速自检判断结果是否可信。三步走第一ts 切片数量要和 m3u8 里 EXTINF 的数量一致多一个少一个都要查第二用 ffprobe 读取合并后的 mp4 时长和索引文件中的理论时长对比第三抽查文件的几个关键位置确认不是首尾正常、中间全坏。ts_count$(grep -c #EXTINF index.m3u8) mp4_duration$(ffprobe -v error -show_entries formatduration -of defaultnoprint_wrappers1 output.mp4) echo ts_count$ts_count, duration$mp4_duration这段脚本逻辑从原始 m3u8 里数出 EXTINF 标签的个数就是理论切片数再读 mp4 的实际时长。两者对不上时去日志里找失败记录重下对应编号的切片不用全量重跑。5.2 增量下载与定时任务批量下载经常要定期更新比如每日归档自动更新课程列表。增量思路不难每次跑之前先检查目标目录里有没有上次生成的 mp4 或 done 标记文件存在就跳过如果资源有新增切片m3u8 的 MEDIA-SEQUENCE 会往后推脚本解析时按序号做差集只下新增的部分。定时任务我用 cron 挂一个 shell 脚本注意加锁防止任务重叠0 2 * * * flock -n /tmp/hls_dl.lock /opt/hls_download/run.sh /var/log/hls_dl.log 21flock 的作用是抢锁如果上一次任务还没跑完这次定时触发就直接跳过避免两个任务同时写同一个目录导致文件混乱。日志落盘到固定路径第二天早上看日志里有没有 failed 行就能判断昨晚任务是不是正常。那次加密和防盗链同时出现的批量任务里我连续翻车两次一次坏在密钥相对路径一次坏在并发太高被限流。从那以后我每次批量前都强制走一遍三件套索引预检打印 key_info 和 ts 总数、密钥预检单独解密一个 ts 验证、重试参数确认 8 线程 / 15 秒超时 / 3 次重试再复杂的资源也能一次跑通。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

稀土储氢合金的工作原理:从镧镍合金到镁基体系的稀土调控

稀土储氢合金的工作原理:从镧镍合金到镁基体系的稀土调控

氢气是清洁能源,但储存和运输一直是个难题。气态氢需要高压,液态氢需要极低温。有没有一种材料,能像海绵吸水一样把氢气“吸住”,需要时再释放出来?有,这就是储氢合金。而在储氢合金中,稀土元素…

2026/10/17 12:12:40 阅读更多 →
【kubernetes】kubeadm部署k8s集群

【kubernetes】kubeadm部署k8s集群

一、初始化系统一、环境初始化//所有节点,关闭防火墙规则,关闭selinux,关闭swap交换 systemctl stop firewalld systemctl disable firewalld setenforce 0 sed -i s/SELINUXenforcing/SELINUXdisabled/ /etc/selinux/config iptables -F &am…

2026/10/16 9:27:24 阅读更多 →
技术趋同时代,代码之外如何保留个人风格?

技术趋同时代,代码之外如何保留个人风格?

最近在整理这一期周刊素材的时候,我注意到一个有点微妙的现象:身边几个做开发的朋友,聊起技术栈来几乎一个模子——前端框架选型一致、接口设计风格趋同、甚至连“业余爱好”都开始撞车。有人调侃说,现在简历拿出来,把…

2026/10/16 9:26:22 阅读更多 →

最新新闻

qModel v1.4.2模型版本管理实战:创建、对比、切换与回滚全攻略

qModel v1.4.2模型版本管理实战:创建、对比、切换与回滚全攻略

做算法工程师这几年,我一直有一个执念:模型版本管理这件事,绝不能等到线上出事了才想起来补课。代码有Git管理,数据有数据仓库,偏偏模型文件常常散落在各个训练机的磁盘、网盘甚至U盘里,谁训的、用的什么数…

2026/10/17 12:12:42 阅读更多 →
AI应用编排实战:智能表单、流程编排与插件开发快速上手

AI应用编排实战:智能表单、流程编排与插件开发快速上手

最近一段时间,我大部分精力都泡在一个叫ModelEngine的AI应用编排平台上。说真的,第一次看到这个工具时,我第一反应是“又一个低代码平台”。但实际用了一个月、给某电商客户落地了一套售后反馈分析应用之后,我改变了看法。它真正解…

2026/10/17 12:12:42 阅读更多 →
Dify搭建搜索与文档阅读智能体:工具调用、提示词与避坑全指南

Dify搭建搜索与文档阅读智能体:工具调用、提示词与避坑全指南

我最近在Dify上搭了一个既能联网搜索、又能读本地文档的智能体,整个过程比我预想的顺利,但没有哪一步是"光靠直觉就能成"的。比如工具描述写得不清楚,模型就死活不调搜索接口;比如文档上传方式选错了,几十页…

2026/10/17 12:12:42 阅读更多 →
MCP与LangGraph实战:把AI Agent脚本重构成生产级系统

MCP与LangGraph实战:把AI Agent脚本重构成生产级系统

做 AI 应用最难受的一个时刻,是脚本里的 demo 跑得风生水起,一推上生产就四处漏风。过去一年我接手好几套类似系统,骨架都差不多:一个 Python 脚本,把用户输入塞给大模型,模型说要调工具就 if-else 判断一下…

2026/10/17 12:12:42 阅读更多 →
Trezor硬件钱包取证:加密备份提取与压缩包解析实战

Trezor硬件钱包取证:加密备份提取与压缩包解析实战

简介:这份PDF文档聚焦硬件钱包数字取证这一细分领域,面向CTF-Misc方向的学习者、数字取证人员与安全研究人员,系统讲解如何从Trezor设备中提取加密备份压缩包。内容从Trezor硬件钱包的工作原理、存储格式与备份策略讲起,逐步深入到…

2026/10/17 12:12:42 阅读更多 →
artcraft:从创意到成品的执行方法论与流程控制

artcraft:从创意到成品的执行方法论与流程控制

1. 从“artcraft”这个名字说起:它到底想解决什么问题 第一次看到“artcraft”这个词,我脑子里蹦出来的不是某个具体工具,而是一种很朴素的组合感——art(艺术、审美、表达)加上 craft(手艺、工艺、打磨&am…

2026/10/17 12:11:42 阅读更多 →

日新闻

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

简介:《DeepSeek使用方法.pdf》是一份面向DeepSeek入门及进阶用户的完整使用指南,重点围绕V3与R1两款模型的能力差异与适用场景展开,帮助读者根据任务类型与成本选择合适的模型。资源共1个PDF文件,压缩包大小约311KB,篇…

2026/10/17 0:00:25 阅读更多 →
200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

简介:这是一份聚焦DeepSeek提示词优化的《200Deepseek润色指令》PDF文档,共收录两百余条可直接套用的润色指令,涵盖语法修正、风格统一、逻辑梳理、表达精炼等维度,并针对商务写作、学术论文、广告文案等场景给出差异化建议。文档…

2026/10/17 0:00:25 阅读更多 →
OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

简介:本资源是一份基于OpenCV的本科课程设计完整报告,面向计算机视觉初学者、图像处理学习者及高校通信/信息类专业学生,解决物体智能计数与检测结果结构化记录的实际问题。项目覆盖图像预处理、轮廓检测、边界框绘制、轨迹栏交互式参数调节、…

2026/10/17 0:00:25 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/16 21:35:51 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/17 1:44:59 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/16 15:04:24 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 21:54:38 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 3:49:43 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 14:23:36 阅读更多 →