ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Python+ffmpeg批量下载智慧教育平台m3u8视频实战

Python+ffmpeg批量下载智慧教育平台m3u8视频实战 国家中小学智慧教育平台上的课程资源质量确实不错不少老师、家长都想把上面的视频存到本地方便离线播放或者在网络不稳定的教室里使用。但平台本身没有提供下载按钮手动一节课一节课去录屏效率低得让人抓狂。最近我在 GitHub 上翻到一个叫 smartedu-download 的项目配合 Python 和 ffmpeg 这套组合拳基本能做到把课程视频批量拉下来。这篇就聊聊我是怎么把这套流程跑通的包括环境怎么搭、m3u8 这种流媒体格式到底怎么回事、N_m3u8DL-CLI 和 ffmpeg 各自扮演什么角色以及我在实际操作中踩过的那些坑。不管你是刚接触 Python 的新手还是已经用过 ffmpeg 的老手应该都能从里面找到点有用的东西。1. 先搞清楚要下载的到底是什么东西很多人一上来就急着找工具、敲命令结果卡在第一步根本不知道自己面对的是什么格式的资源。国家中小学智慧教育平台上的视频绝大多数不是那种点一下就能右键另存为的 mp4 文件而是基于 HLS 协议的流媒体。这个区别很关键直接决定了你后面要用什么工具、走什么路线。1.1 HLS 与 m3u8为什么不能直接右键保存HLS 全称是 HTTP Live Streaming翻译过来就是基于 HTTP 的实时流传输协议。它的核心思路是把一整段视频切成无数个几秒到十几秒的小片段每个片段是一个独立的 ts 文件然后用一个索引文件把这些片段的地址、时长、码率等信息串起来。这个索引文件就是 m3u8。打个比方传统的 mp4 就像一本装订好的书你直接拿走就行而 HLS 视频像是一套连载漫画每一话单独发行m3u8 就是那份目录告诉你第一话在哪、第二话在哪、总共多少话。浏览器播放的时候是先读目录再按顺序把每一话拉下来拼着播。所以你右键保存最多只能存到当前正在播的那一小段或者干脆什么都存不到。这就解释了为什么必须用专门的工具去解析 m3u8、按顺序下载所有 ts 片段、最后再合并成一个完整文件。手动做这件事几乎不可能因为一个四十分钟的课程可能有几百个 ts 片段地址还带各种参数。1.2 平台资源的几种常见形态在实际操作中我发现平台上的资源大致分三类处理方式各不相同。第一类是纯视频课程就是上面说的 HLS 流这是最主要也最值得下载的部分。第二类是课件、教案这类文档通常是 PDF 或者图片这类反而好办直接下载就行。第三类是带有交互的页面比如一些实验演示、动画课件这类资源往往依赖平台的前端逻辑下载下来意义不大也不在本文讨论范围内。我们重点盯住第一类。判断一个页面是不是 HLS 视频有个很简单的办法打开浏览器的开发者工具切到网络面板筛选 m3u8 或者 ts然后刷新页面开始播放。如果能看到一堆 ts 请求和一个 m3u8 请求那基本就确认了。这个排查动作看着简单但它是后面所有操作的前提我建议你养成先确认格式再动手的习惯能省掉大量无用功。1.3 为什么选择本地化保存而不是在线观看有人可能会问既然在线能看为什么非要下载这个问题我在实际使用中体会很深。首先是网络问题很多学校的教室网络并不稳定上课到一半卡住是常有的事提前下载好就完全不受影响。其次是复用问题同一节课可能要在不同班级讲好几遍每次都要重新加载、重新缓冲时间都浪费在等待上。再者是留存问题平台上的资源偶尔会调整今天能看的明天不一定还在重要的课程提前存一份心里踏实。当然下载下来的资源请务必遵守相关的使用规范仅用于个人教学和学习场景不要进行二次传播或者商业用途这一点是底线。2. 工具链选型Python、ffmpeg 和 N_m3u8DL-CLI 各干什么活确定了要处理的是 HLS 流之后接下来就是选工具。市面上的方案不少我最终锁定的是 Python 做调度、N_m3u8DL-CLI 做主力下载、ffmpeg 做兜底和转码这套组合。下面说说为什么这么选以及每个工具到底承担什么职责。2.1 N_m3u8DL-CLI专治 m3u8 的下载利器N_m3u8DL-CLI 是一个用 C# 写的命令行工具专门用来下载 m3u8 视频。它的优势非常明显支持多线程下载速度比单线程快好几倍能自动处理 AES-128 加密的流很多平台的视频片段是加密的普通下载器拿到的是乱码它能自动解密支持断点续传下载到一半断了不用从头再来还能自动合并 ts 片段并调用 ffmpeg 转成 mp4。我对比过几个同类工具N_m3u8DL-CLI 在稳定性和功能完整度上是最省心的。它的命令格式大概是这样的N_m3u8DL-CLI.exe https://example.com/video.m3u8 --workDir download --saveName lesson01 --enableDelAfterDone这里的--workDir指定工作目录--saveName指定保存的文件名--enableDelAfterDone表示下载完成后删除中间的 ts 片段只保留最终的 mp4。这几个参数是我用得最多的建议先拿这几个跑通再去看它更高级的选项。需要提醒的是这个工具依赖 .NET 运行环境Windows 上一般没问题如果你在 Linux 或者 macOS 上用可能需要通过 mono 或者找对应的跨平台版本这一点后面会细说。2.2 ffmpeg不只是转码更是合并和修复的多面手ffmpeg 的大名不用多介绍音视频处理领域的瑞士军刀。在这套流程里它主要干三件事。第一件是合并。当 N_m3u8DL-CLI 因为某些原因没能自动合并或者你手头已经有一堆 ts 片段和一个 m3u8 文件时可以用 ffmpeg 来合并ffmpeg -i index.m3u8 -c copy -bsf:a aac_adtstoasc output.mp4这条命令的意思是读取 m3u8 索引直接复制音视频流-c copy表示不重新编码速度快、无损然后输出成 mp4。-bsf:a aac_adtstoasc这个参数是处理 AAC 音频的HLS 里的 AAC 是 ADTS 格式mp4 里需要的是 ASC 格式不加这个参数有时候音频会出问题。第二件是转码。有些下载下来的视频码率很高文件特别大或者格式不兼容某些播放器这时候可以用 ffmpeg 压缩ffmpeg -i input.mp4 -c:v libx264 -crf 23 -preset medium -c:a aac -b:a 128k output.mp4-crf 23是质量参数数值越小质量越高文件越大23 是个比较均衡的值-preset medium控制编码速度越慢压缩率越高。第三件是修复。偶尔会遇到下载下来的文件时间轴不对、音画不同步的情况ffmpeg 也能派上用场。可以说有了 ffmpeg 在手视频处理的大部分疑难杂症都有解。2.3 Python把零散操作串成自动化流水线单次下载用命令行就够了但如果你要下载一整个学期的课程几十上百个视频一个个手动敲命令显然不现实。这时候 Python 的价值就体现出来了。Python 在这套流程里的角色是调度员和胶水。它可以做这几件事批量读取课程列表、自动提取每个视频的 m3u8 地址、循环调用 N_m3u8DL-CLI 下载、下载完成后统一用 ffmpeg 处理、最后整理文件命名和目录结构。整个流程跑通之后你只需要维护一个课程清单剩下的交给脚本就行。我自己的做法是用 Python 的subprocess模块来调用外部命令用os和pathlib来管理文件路径用requests来处理网络请求。这套组合不需要多高深的编程技巧会基本的 Python 语法就能写。2.4 三者协作的完整链路把三个工具串起来看整个链路是这样的Python 脚本负责发现和调度拿到 m3u8 地址后交给 N_m3u8DL-CLI 去下载和解密下载过程中 N_m3u8DL-CLI 内部会调用 ffmpeg 完成合并如果遇到特殊情况Python 再单独调用 ffmpeg 做后处理。这个分工的好处是各司其职每个工具都干自己最擅长的事。N_m3u8DL-CLI 专注下载ffmpeg 专注处理Python 专注编排。你不需要自己写解密逻辑也不需要自己实现 ts 合并站在这些成熟工具的肩膀上效率高得多。3. 环境搭建从零把工具装到位工具选好了接下来是动手装。这一步看着简单但新手最容易在这里卡住尤其是 Python 环境配置和 ffmpeg 的路径问题。我按顺序把每一步说清楚。3.1 Python 安装与环境变量配置Windows 上装 Python去官网下载安装包安装的时候务必勾选 Add Python to PATH 这个选项。我见过太多人装完 Python 在命令行敲python提示找不到命令就是因为没勾这个。如果已经装完了才发现没勾也不用重装手动把 Python 的安装目录和 Scripts 目录加到系统环境变量 Path 里就行。装完之后验证一下打开命令行输入python --version pip --version能正常输出版本号就说明装好了。如果python不行但py可以说明是 Windows 的别名问题用py代替python也行。对于需要管理多个项目的情况我建议用虚拟环境避免不同项目的依赖互相打架python -m venv venv venv\Scripts\activate激活之后命令行前面会出现(venv)的标识这时候装的包都只在这个环境里生效干净利落。3.2 ffmpeg 的下载与路径设置ffmpeg 官方不提供安装程序只提供编译好的压缩包。去官网的下载页面Windows 用户选 Windows builds下载那个 essentials 版本的 zip 包。解压之后你会看到一个 bin 目录里面有 ffmpeg.exe、ffprobe.exe、ffplay.exe 三个可执行文件。关键一步是把 bin 目录的完整路径加到系统环境变量 Path 里。加完之后重新打开一个命令行窗口注意必须是新开的旧窗口不会刷新环境变量输入ffmpeg -version能打印出版本信息就成功了。如果提示找不到命令八成是路径没加对或者加完没重开命令行。这里有个细节值得说N_m3u8DL-CLI 在合并阶段会去找 ffmpeg如果你的 ffmpeg 没配好环境变量它可能合并失败。所以 ffmpeg 的路径配置是整套流程里最不能省的一步。3.3 N_m3u8DL-CLI 的获取与依赖N_m3u8DL-CLI 在 GitHub 上有发布页下载最新的 release 压缩包解压就能用是个绿色工具。它需要 .NET Framework 4.7.2 或者更高版本Windows 10 和 11 一般自带老一点的系统可能需要手动装一下。解压之后建议把它放到一个固定的目录比如D:\tools\N_m3u8DL-CLI然后把这个目录也加到环境变量 Path 里这样在任何地方都能直接调用。如果不加环境变量就得每次写完整路径麻烦。验证一下N_m3u8DL-CLI.exe --version能输出版本号就说明可用。如果报缺少 dll 之类的错误多半是 .NET 版本不够去装个新版运行库就好。3.4 用 pip 装齐 Python 依赖Python 这边需要装的包不多主要是requests用来发网络请求tqdm用来显示进度条lxml或者beautifulsoup4用来解析页面如果需要从页面提取信息的话pip install requests tqdm beautifulsoup4 lxml如果下载速度慢可以换国内镜像源pip install requests tqdm -i https://pypi.tuna.tsinghua.edu.cn/simple装完之后建议写个简单的测试脚本确认各个工具都能正常调用别等到正式下载的时候才发现某个环节没配好。3.5 环境验证跑一个最小可用示例环境搭好之后别急着上批量脚本先拿一个视频做单点测试。找一个 m3u8 地址用 N_m3u8DL-CLI 跑一遍N_m3u8DL-CLI.exe 你的m3u8地址 --workDir test --saveName test01观察输出看它能不能正常解析、下载、合并。如果这一步能跑通说明环境没问题后面的事情就好办了。如果卡住根据报错信息逐个排查是网络问题、地址问题还是工具依赖问题。我个人的经验是环境搭建阶段多花半小时把每个工具单独验证一遍比后面下载到一半出问题再回头查要划算得多。4. 实战把下载流程跑起来环境就绪现在进入正题。这一节我把从拿到课程页面到最终得到 mp4 文件的完整过程拆开讲包括怎么找 m3u8 地址、怎么组织批量下载、怎么处理下载后的文件。4.1 定位 m3u8 地址的几种方法找 m3u8 地址是整件事的起点。最直接的方法是用浏览器的开发者工具。打开课程播放页面按 F12 打开开发者工具切到 Network 面板在筛选框里输入 m3u8然后刷新页面或者点击播放。正常情况下你会看到一个或多个 m3u8 请求点进去看 Response里面就是索引内容。有时候会遇到多个 m3u8比如一个主索引master playlist和若干子索引media playlist。主索引里列出的是不同清晰度的子索引地址子索引里才是真正的 ts 片段列表。这种情况下你应该把主索引地址交给下载工具让它自己去选清晰度或者手动挑一个子索引地址下载指定清晰度。还有一种情况是地址被加密或者带了时效性参数直接复制出来过一会儿就失效了。遇到这种要么尽快下载要么在脚本里模拟请求动态获取。后者涉及的分析工作比较多新手可以先从简单的开始。4.2 单文件下载的完整命令拆解拿到地址之后单文件下载就是一条命令的事。我把常用参数拆开讲N_m3u8DL-CLI.exe https://example.com/course.m3u8 ^ --workDir D:\downloads\lesson01 ^ --saveName 第一课 ^ --enableDelAfterDone ^ --threadCount 16 ^ --retryCount 5--threadCount 16是并发线程数默认好像是 16网络好的话可以适当调高但别太夸张太高反而容易被限速。--retryCount 5是失败重试次数网络不稳定的时候很有用。--enableDelAfterDone前面说过下载完删掉中间文件。命令里的^是 Windows 命令行的换行符Linux 和 macOS 用\。如果你嫌一行太长可以拆成多行写可读性好一些。4.3 批量下载的 Python 脚本骨架单个视频下载会了批量就是加个循环。下面是我常用的脚本骨架你可以根据自己的需求改import subprocess import os from pathlib import Path # 课程清单实际使用中可以从文件读取 courses [ {name: 第一课, url: https://example.com/lesson01.m3u8}, {name: 第二课, url: https://example.com/lesson02.m3u8}, ] base_dir Path(D:/downloads/smartedu) base_dir.mkdir(parentsTrue, exist_okTrue) for course in courses: work_dir base_dir / course[name] work_dir.mkdir(exist_okTrue) cmd [ N_m3u8DL-CLI.exe, course[url], --workDir, str(work_dir), --saveName, course[name], --enableDelAfterDone, --threadCount, 16, --retryCount, 5 ] print(f开始下载{course[name]}) result subprocess.run(cmd, capture_outputTrue, textTrue) if result.returncode 0: print(f下载成功{course[name]}) else: print(f下载失败{course[name]}) print(result.stderr)这个骨架的核心就是subprocess.run它把外部命令当成一个函数来调用返回码为 0 表示成功。你可以在此基础上加日志、加异常处理、加失败重试。4.4 下载后的文件整理与命名规范下载下来的文件如果命名乱七八糟后期找起来很痛苦。我建议在脚本里就把命名规范定好比如用 年级-学科-课名 这样的格式目录按学期或者单元分。这样下载完直接就是一个结构清晰的资源库不用再花时间整理。另外下载完成后可以顺手用 ffprobe 检查一下文件完整性ffprobe -v error -show_entries formatduration -of defaultnoprint_wrappers1:nokey1 output.mp4能输出时长就说明文件基本正常。如果输出为空或者报错说明文件可能损坏需要重新下载。4.5 处理加密流与特殊参数有些视频的 ts 片段是 AES-128 加密的m3u8 里会有一个#EXT-X-KEY标签指向密钥地址。N_m3u8DL-CLI 能自动处理这种情况你不需要手动解密。但如果密钥地址需要特定的请求头或者 cookie就可能失败。遇到这种情况可以在命令里加上请求头参数N_m3u8DL-CLI.exe url --headers Referer: https://example.com|User-Agent: Mozilla/5.0多个请求头用|分隔。这个技巧在处理一些有防盗链的地址时特别有用。我遇到过好几次下载失败加上 Referer 之后就正常了原因就是服务器校验了来源。5. 踩坑记录那些让我折腾半天的意外情况工具用起来顺的时候很顺但一旦出问题排查起来可能很费时间。这一节我把实际遇到过的几个典型问题整理出来包括现象、原因和解决办法希望能帮你少走弯路。5.1 ffmpeg 找不到导致合并失败这是最常见的问题。现象是 N_m3u8DL-CLI 下载 ts 片段都正常但到了合并阶段报错提示找不到 ffmpeg 或者合并失败。原因基本就是 ffmpeg 没装或者没配环境变量。解决办法前面说过把 ffmpeg 的 bin 目录加到 Path 里然后重开命令行。这里有个容易忽略的点如果你是在 IDE 里跑 Python 脚本IDE 可能用的是它自己启动时的环境变量你新加了 Path 之后需要重启 IDE 才生效。我就因为这个折腾过一次明明命令行里 ffmpeg 能用脚本里就是找不到最后发现是 IDE 没重启。还有一种情况是 ffmpeg 装了但版本太老某些参数不支持。建议用比较新的版本官方 essentials 包就行。5.2 下载到一半中断与断点续传网络不稳定的时候下载到一半断了是常事。N_m3u8DL-CLI 支持断点续传重新运行同样的命令它会检测到已经下载的片段跳过它们继续下。但前提是工作目录和文件名跟上次一致所以别中途改参数。如果断点续传也失败可能是中间文件损坏了。这时候把工作目录里的临时文件清掉重新下载。我一般会在脚本里加个判断如果某个视频下载失败超过三次就跳过它继续下一个最后统一处理失败的避免一个卡住拖累全部。5.3 音画不同步与时间轴修复偶尔会遇到下载下来的视频音画不同步声音比画面快或者慢一点。这种情况多半是 ts 片段的时间戳有问题或者合并的时候处理不当。修复的思路是用 ffmpeg 重新封装有时候重新封装就能解决ffmpeg -i input.mp4 -c copy -avoid_negative_ts make_zero output.mp4-avoid_negative_ts make_zero这个参数专门处理时间戳问题把负的时间戳归零。如果重新封装不行就得重新编码音频或者视频那就比较费时间了。我的经验是大部分音画不同步问题重新封装就能搞定真正需要重编码的情况很少。5.4 文件名乱码与特殊字符处理课程名称里经常有中文、括号、空格这些字符处理不好就会乱码或者导致命令执行失败。Windows 命令行对中文的支持时好时坏Python 调用的时候编码问题也容易出。我的做法是在 Python 脚本里统一用 UTF-8 编码调用 subprocess 的时候显式指定编码result subprocess.run(cmd, capture_outputTrue, textTrue, encodingutf-8)文件名里的特殊字符比如:、?、*这些 Windows 不允许的字符提前替换掉。空格的话用引号把路径包起来就行。这些细节看着小但真出问题的时候很烦人。5.5 平台地址变化与脚本维护平台的资源地址不是一成不变的可能过一段时间就调整了。如果你的脚本是硬编码地址那每次变化都得改代码。更好的做法是把地址和配置分离用一个单独的配置文件或者数据文件存课程清单脚本只负责读取和执行。另外如果平台调整了页面结构从页面自动提取地址的逻辑可能失效。这时候要么手动更新地址要么重新分析页面。我一般会定期检查一下脚本还能不能用别等到要用了才发现跑不通。6. 进阶玩法让整套流程更顺手基础流程跑通之后可以做一些优化让整套东西用起来更舒服。这一节分享几个我自己在用的进阶技巧。6.1 用配置文件管理课程清单把课程清单从代码里抽出来放到一个 JSON 或者 YAML 文件里{ courses: [ {name: 三年级数学-第一单元, url: https://example.com/math01.m3u8}, {name: 三年级数学-第二单元, url: https://example.com/math02.m3u8} ] }Python 脚本读取这个文件遍历下载。这样要增删课程只需要改配置文件不用动代码也不容易出错。6.2 下载进度可视化与日志记录批量下载的时候有个进度显示心里踏实。可以用 tqdm 做个简单的进度条或者把每个视频的下载状态写进日志文件。日志里记录开始时间、结束时间、成功失败、文件大小这些信息出问题的时候方便追溯。import logging logging.basicConfig( filenamedownload.log, levellogging.INFO, format%(asctime)s - %(levelname)s - %(message)s ) logging.info(f开始下载{course[name]})日志这东西平时看着没用真出问题的时候就是救命稻草。6.3 下载后自动转码压缩如果下载的视频码率很高文件很大可以用 ffmpeg 批量压缩。写个循环把目录下所有 mp4 过一遍for %f in (*.mp4) do ffmpeg -i %f -c:v libx264 -crf 23 -preset medium -c:a aac -b:a 128k compressed_%f压缩会损失一点画质但对于课堂播放来说crf 23 基本看不出差别文件能小一半以上。如果存储空间紧张这个操作很值得做。6.4 定时任务与增量下载如果课程是持续更新的可以设置定时任务定期检查有没有新课程有的话自动下载。Windows 上用任务计划程序Linux 上用 cron。配合增量下载逻辑记录已下载的课程跳过重复的就能实现半自动化的资源库维护。这个玩法适合长期使用前期多花点时间搭好后面基本不用管。6.5 跨平台使用的注意事项上面讲的流程主要基于 Windows因为 N_m3u8DL-CLI 是 .NET 工具。如果你用 Linux 或者 macOS有几个选择一是用 mono 运行 .NET 程序二是找跨平台的替代工具三是直接用 ffmpeg 下载 m3u8。ffmpeg 本身就能下载 m3u8ffmpeg -i https://example.com/video.m3u8 -c copy output.mp4这条命令在 Linux 和 macOS 上都能用缺点是单线程速度慢一些而且对加密流的支持不如 N_m3u8DL-CLI 完善。如果只是偶尔下载几个视频ffmpeg 足够了如果要批量下载大量加密视频还是建议在 Windows 环境下用 N_m3u8DL-CLI。我在 Linux 上试过用 ffmpeg 直接下载简单场景没问题但遇到加密流就抓瞎了。所以跨平台的话得根据实际需求权衡。7. 一些实际使用中的体会这套流程我用了一段时间下载了不少课程资源整体感受是工具链成熟跑通之后很省心但前期环境搭建和问题排查确实需要点耐心。最容易出问题的环节是环境配置尤其是 ffmpeg 的路径和 Python 的编码。这两个地方搞定之后后面基本就是顺水推舟。我建议新手不要一上来就追求全自动先把单个视频下载跑通再逐步加批量、加日志、加压缩一步步来每步都验证过再往下走。另外下载下来的资源一定要管理好。我见过有人下载了几百个视频文件名全是乱码最后自己都找不到哪个是哪个。花点时间在命名和目录结构上后期省的是自己的时间。最后再分享一个小技巧如果某个视频反复下载失败别死磕先跳过把能下的都下完最后再集中处理失败的。有时候是临时的网络问题过一会儿再试就好了。批量任务最忌讳的就是卡在一个点上不动效率全耗在等待上了。
返回列表