Python合并TS视频的三种方法:FFmpeg、MoviePy与二进制拼接实战
1. 项目概述为什么我们需要用Python合并TS视频在视频处理和数据抓取的日常工作中我经常遇到一种情况从网络上下载的视频文件尤其是那些流媒体视频往往被分割成成百上千个以.ts为后缀的小文件。这些TSTransport Stream文件是MPEG-2传输流的标准格式广泛用于直播、点播和在线视频的分片传输。手动将这些碎片拼接成一个完整的视频不仅效率低下而且容易出错。这时一个自动化、可靠的合并工具就显得至关重要。Python凭借其丰富的库生态和简洁的语法成为了处理这类任务的绝佳选择。无论是为了归档下载的课程视频、拼接抓取的影视剧片段还是处理监控录像的流式文件掌握用Python合并TS视频的技能都能极大提升工作效率。本文将深入探讨三种经过实战检验的合并方法基于FFmpeg的命令行调用、使用moviepy库进行编程化处理以及最原始但最可控的二进制文件直接拼接。每种方法都有其适用场景和优缺点我将结合具体代码和大量实操细节带你从原理到实践彻底掌握这门手艺。2. 核心思路与方案选型三种方法的定位与抉择面对一堆TS文件我们首先要明确目标不仅仅是“合并”而是要“正确、高效且可追溯地合并”。这涉及到音视频编码的容器格式、时间戳的连续性、可能存在的加密或编码不一致等问题。下面三种方法代表了三种不同的解决路径。2.1 方法一FFmpeg——功能强大的“瑞士军刀”FFmpeg是一个完整的、跨平台的音视频处理解决方案。用它来合并TS文件本质上是调用其强大的复用Muxing能力。它的优势在于“专业”内部会自动处理容器格式、时间戳同步、编码检查等复杂问题合并后的视频质量最有保障。适合对输出视频质量要求高或源TS文件可能存在编码参数微调的场景。2.2 方法二MoviePy——面向Python开发者的“高级封装”MoviePy是一个用于视频编辑的Python库它提供了非常人性化的API。其合并功能底层可能也依赖FFmpeg但为我们封装了更Pythonic的调用方式。它的优势在于“集成”如果你的Python项目本身就在进行复杂的视频剪辑、合成、特效添加那么使用MoviePy来合并TS片段可以保持技术栈的统一让代码更整洁。2.3 方法三二进制直接拼接——简单粗暴的“底层操作”这种方法完全绕开了音视频处理库直接将TS文件视为二进制字节流按顺序读取并写入一个新文件。它的优势在于“极简”和“高速”。因为不进行任何解码、编码或格式分析所以速度极快对系统资源消耗极小。但它的前提也最苛刻要求所有TS文件必须具有完全相同的编码格式、分辨率、码率等参数且文件本身是“纯净”的传输流片段。常用于处理由同一源规范切割的、未加密的TS文件。如何选择追求稳定和兼容性首选FFmpeg。它是行业标准能处理绝大多数疑难杂症。已在Python视频处理流水线中考虑MoviePy。保持代码风格一致。确认TS文件来源单一、格式纯净且追求极限速度使用二进制拼接。例如合并自己用工具按固定间隔切分的大文件。3. 环境准备与核心工具解析工欲善其事必先利其器。在开始编码前我们需要搭建好环境。3.1 FFmpeg的安装与验证FFmpeg并非Python库而是一个独立的命令行工具。Python代码通过subprocess模块去调用它。Windows系统安装访问FFmpeg官网的下载页面找到Windows版本的构建推荐使用“gyan.dev”或“BtbN”提供的静态构建版本。下载ZIP压缩包解压到任意目录例如D:\ffmpeg。将D:\ffmpeg\bin添加到系统的环境变量PATH中。打开命令提示符CMD或PowerShell输入ffmpeg -version如果显示版本信息则安装成功。macOS系统安装使用Homebrew是最简单的方式brew install ffmpegLinux系统安装对于Debian/Ubuntu系sudo apt update sudo apt install ffmpeg对于CentOS/RHEL系sudo yum install epel-release sudo yum install ffmpeg(可能需要启用EPEL仓库)注意确保FFmpeg版本不要太旧否则可能不支持一些新的编码格式。3.2 Python库的安装我们将用到moviepy和用于文件操作的标准库。使用pip进行安装pip install moviepymoviepy会自动安装其依赖如imageio、decorator等。如果遇到安装问题可以尝试先升级pippip install --upgrade pip3.3 项目文件结构规划一个清晰的文件结构有助于管理。假设我们有一个项目文件夹ts_mergerts_merger/ ├── ts_files/ # 存放所有待合并的.ts文件 │ ├── segment_0001.ts │ ├── segment_0002.ts │ └── ... ├── output/ # 存放合并后的视频文件 ├── ffmpeg_merge.py # 方法一的脚本 ├── moviepy_merge.py # 方法二的脚本 └── binary_merge.py # 方法三的脚本4. 方法一详解使用FFmpeg进行合并最推荐这是最稳健、最通用的方法。FFmpeg的concat协议非常擅长处理流拼接。4.1 原理与步骤FFmpeg合并TS文件通常有两种方式使用concat协议针对TS流创建一个文本文件如filelist.txt里面按顺序列出所有TS文件的路径。然后让FFmpeg读取这个列表进行合并。这种方式效率高是处理TS文件的推荐方式。使用concat分离器针对普通文件更通用但可能需要对文件进行转码。我们采用第一种方式因为它最快且能保持原始编码。核心步骤扫描目标文件夹获取所有.ts文件并按名称或创建时间排序。生成一个符合FFmpegconcat协议格式的文本文件。构造FFmpeg命令行参数并执行。检查输出文件是否完整。4.2 完整代码实现与注释import os import subprocess import sys def merge_ts_with_ffmpeg(ts_folder, output_file): 使用FFmpeg合并TS文件。 Args: ts_folder (str): 存放TS文件的文件夹路径。 output_file (str): 合并后的输出文件路径如 output.mp4。 # 1. 检查FFmpeg是否可用 try: subprocess.run([ffmpeg, -version], capture_outputTrue, checkTrue) except (subprocess.CalledProcessError, FileNotFoundError): print(错误未找到FFmpeg。请确保FFmpeg已安装并已添加到系统PATH环境变量中。) sys.exit(1) # 2. 获取并排序TS文件列表 # 确保我们只获取.ts文件并按数字顺序或字母顺序排序这取决于你的文件名 ts_files [f for f in os.listdir(ts_folder) if f.endswith(.ts)] # 假设文件名类似 segment_0001.ts, segment_0002.ts ts_files.sort(keylambda x: int(.join(filter(str.isdigit, x))) if any(c.isdigit() for c in x) else x) # 更通用的排序按文件名自然排序 # import re # ts_files.sort(keylambda x: [int(c) if c.isdigit() else c for c in re.split(r(\d), x)]) if not ts_files: print(f在文件夹 {ts_folder} 中未找到.ts文件。) return print(f找到 {len(ts_files)} 个TS文件。) # 3. 创建临时文件列表filelist.txt filelist_path os.path.join(ts_folder, filelist.txt) with open(filelist_path, w, encodingutf-8) as f: for ts_file in ts_files: # 使用file协议并确保路径是绝对路径避免奇怪的问题 file_path os.path.abspath(os.path.join(ts_folder, ts_file)) # 对Windows路径的反斜杠进行转义或统一为正斜杠 file_path file_path.replace(\\, /) # concat协议格式每行是 file 文件路径 f.write(ffile {file_path}\n) print(f已生成文件列表{filelist_path}) # 4. 构建FFmpeg命令 # -f concat: 指定使用concat分离器 # -safe 0: 允许使用绝对路径或“不安全”的路径 # -i filelist.txt: 输入是文件列表 # -c copy: 流复制模式不重新编码速度极快 # -y: 如果输出文件已存在则直接覆盖可选生产环境慎用 cmd [ ffmpeg, -f, concat, -safe, 0, -i, filelist_path, -c, copy, # 关键参数直接复制流不重新编码 -y, # 自动覆盖输出文件 output_file ] print(f执行命令{ .join(cmd)}) # 5. 执行命令并捕获输出 try: # 使用Popen实时输出信息便于调试 process subprocess.Popen(cmd, stdoutsubprocess.PIPE, stderrsubprocess.STDOUT, universal_newlinesTrue) for line in process.stdout: # 可以过滤或实时打印FFmpeg的输出信息 if frame in line or time in line: # 打印进度信息如果FFmpeg输出到stderr这里可能捕获不到需要调整 print(line.strip(), end\r) # 用回车实现单行更新 # 也可以简单打印所有输出 # print(line, end) process.wait() if process.returncode 0: print(f\n合并成功输出文件{output_file}) # 可选删除临时文件列表 os.remove(filelist_path) print(f已删除临时文件列表。) else: print(f\n合并失败FFmpeg返回错误码{process.returncode}) except Exception as e: print(f执行FFmpeg命令时发生异常{e}) finally: # 确保即使出错也尝试清理临时文件 if os.path.exists(filelist_path): os.remove(filelist_path) if __name__ __main__: # 使用示例 ts_folder ./ts_files # 你的TS文件夹路径 output_file ./output/merged_video.mp4 # 输出文件路径和格式也可以是 .mkv, .ts等 # 确保输出目录存在 os.makedirs(os.path.dirname(output_file), exist_okTrue) merge_ts_with_ffmpeg(ts_folder, output_file)4.3 关键参数解析与避坑指南-c copy或-codec copy这是命令的灵魂。它告诉FFmpeg不要对视频和音频流进行解码和重新编码而是直接复制原始数据流到新容器中。这保证了合并速度最快几乎是文件IO的速度并且是无损的画质和音质没有任何损失。绝大多数情况下都应该使用这个参数。-safe 0当在-i参数中使用文件列表时如果文件列表中的路径是绝对路径或包含特殊字符FFmpeg可能会出于安全考虑拒绝读取。-safe 0禁用了这个安全检查。在脚本控制的场景下这是安全的。文件列表的路径问题这是最常见的坑。如果TS文件路径中包含空格或特殊字符必须在filelist.txt中用引号括起来并且确保路径正确。上述代码中我们使用绝对路径并统一将反斜杠\替换为正斜杠/能有效避免Windows下的路径问题。排序至关重要TS文件必须按照播放顺序合并。确保你的排序逻辑ts_files.sort(...)与实际顺序一致。如果文件名没有规律你可能需要根据文件修改时间排序os.path.getmtime但这风险较高最好从源头保证文件名有序。输出格式选择-c copy模式下输出文件的容器格式如.mp4,.mkv,.ts需要能容纳输入流的编码格式。通常.mp4和.mkv的兼容性都很好。如果你希望输出仍然是TS流可以指定输出为.ts文件。实操心得在正式合并大量文件前我强烈建议先用前几个文件比如ts_files ts_files[:5]做一次测试合并验证整个流程和排序是否正确。这能避免几个小时的合并工作因为顺序错误而前功尽弃。5. 方法二详解使用MoviePy库进行合并MoviePy提供了更面向对象的接口适合在更复杂的Python视频处理流程中集成TS合并功能。5.1 原理与步骤MoviePy的concatenate_videoclips函数可以将多个VideoFileClip对象按顺序连接起来。其底层可能调用FFmpeg或使用其他方法进行合成。这种方法代码更简洁但相比直接调用FFmpeg可能在某些边缘情况下灵活性稍差且依赖库的安装。核心步骤使用VideoFileClip逐个加载TS文件。将所有VideoFileClip对象放入一个列表。使用concatenate_videoclips函数合并它们。将合并后的clip写入输出文件。5.2 完整代码实现与注释from moviepy.editor import VideoFileClip, concatenate_videoclips import os def merge_ts_with_moviepy(ts_folder, output_file): 使用MoviePy合并TS文件。 Args: ts_folder (str): 存放TS文件的文件夹路径。 output_file (str): 合并后的输出文件路径。 # 1. 获取并排序TS文件列表同方法一 ts_files [f for f in os.listdir(ts_folder) if f.endswith(.ts)] ts_files.sort(keylambda x: int(.join(filter(str.isdigit, x))) if any(c.isdigit() for c in x) else x) if not ts_files: print(f在文件夹 {ts_folder} 中未找到.ts文件。) return print(f找到 {len(ts_files)} 个TS文件开始加载...) clips [] for i, filename in enumerate(ts_files): file_path os.path.join(ts_folder, filename) try: # 加载每个TS文件为一个VideoFileClip对象 clip VideoFileClip(file_path) clips.append(clip) print(f已加载 ({i1}/{len(ts_files)}): {filename}) except Exception as e: print(f加载文件 {filename} 时出错{e}. 跳过此文件。) # 可以选择终止或跳过这里选择跳过 continue if not clips: print(没有成功加载任何视频片段。) return # 2. 合并视频片段 print(正在合并视频片段...) try: # method“compose”是默认方法会处理分辨率不一致的情况但TS文件通常一致 final_clip concatenate_videoclips(clips, methodcompose) except Exception as e: print(f合并片段时出错{e}) # 记得释放已加载的clip资源 for clip in clips: clip.close() return # 3. 写入输出文件 print(f正在写入输出文件{output_file}) try: # write_videofile 参数丰富可以指定编码器、码率等。 # 如果不指定codecmoviepy会尝试使用默认编码器可能导致重新编码。 # 对于TS流复制moviepy可能不如FFmpeg直接这里我们尝试使用‘copy’参数但并非所有格式都支持。 # 更常见的做法是让moviepy选择合适编码器。对于最终交付重新编码一次也未尝不可。 final_clip.write_videofile( output_file, codeclibx264, # 视频编码器常用libx264 (H.264) audio_codecaac, # 音频编码器 temp_audiofiletemp-audio.m4a, # 临时音频文件处理音视频合成 remove_tempTrue, # 写入完成后删除临时文件 verboseFalse, # 不显示FFmpeg的详细输出 loggerNone # 不使用进度条logger ) print(f合并成功输出文件{output_file}) except Exception as e: print(f写入输出文件时出错{e}) finally: # 4. 非常重要关闭所有clip以释放资源尤其是文件句柄 final_clip.close() for clip in clips: clip.close() if __name__ __main__: ts_folder ./ts_files output_file ./output/merged_with_moviepy.mp4 os.makedirs(os.path.dirname(output_file), exist_okTrue) merge_ts_with_moviepy(ts_folder, output_file)5.3 注意事项与性能考量资源释放MoviePy的VideoFileClip对象会占用系统资源文件句柄、内存。在循环中加载大量文件或在合并完成后必须调用clip.close()来显式关闭。使用with语句上下文管理器是更好的实践但concatenate_videoclips需要所有clip对象所以通常在最后统一关闭。重新编码问题write_videofile方法默认可能会对视频进行重新编码即使你指定codeccopy它也可能在某些容器格式下不生效。这意味着合并过程会比FFmpeg的-c copy慢很多并且是有损的。MoviePy更适合用于需要编辑裁剪、加特效、调整后再导出的场景对于纯合并且要求无损的场景FFmpeg是更优选择。错误处理某个TS文件损坏或格式异常可能导致整个合并失败。代码中加入了try-except来跳过无法加载的文件保证流程能继续。在生产环境中你可能需要更精细的错误日志记录。内存消耗如果一次性加载数百个高码率TS文件的clip对象可能会导致内存占用过高。对于超多文件合并需要分批次处理或寻求其他方案。6. 方法三详解二进制直接拼接最快但限制最多这是最底层的方法其原理非常简单把每个TS文件当作一堆二进制数据字节按顺序读出来再按顺序写到一个新文件里。6.1 原理与步骤Python的open函数在二进制模式rb或wb下可以直接操作文件的原始字节。TS文件格式本身在设计上就支持简单的拼接只要文件头尾没有额外的元数据且编码参数一致直接拼接后的文件仍然是有效的TS流。核心步骤获取并排序TS文件列表。以二进制写入模式打开目标文件。循环遍历TS文件列表以二进制读取模式打开每个文件将其内容写入目标文件。关闭所有文件句柄。6.2 完整代码实现与注释import os def merge_ts_with_binary(ts_folder, output_file, buffer_size1024*1024): 使用二进制方式直接合并TS文件。 Args: ts_folder (str): 存放TS文件的文件夹路径。 output_file (str): 合并后的输出文件路径。 buffer_size (int): 读写缓冲区大小单位字节。默认1MB影响合并速度。 # 1. 获取并排序TS文件列表 ts_files [f for f in os.listdir(ts_folder) if f.endswith(.ts)] ts_files.sort(keylambda x: int(.join(filter(str.isdigit, x))) if any(c.isdigit() for c in x) else x) if not ts_files: print(f在文件夹 {ts_folder} 中未找到.ts文件。) return print(f找到 {len(ts_files)} 个TS文件开始二进制合并...) total_size 0 try: with open(output_file, wb) as out_f: # 以二进制写入模式打开输出文件 for i, filename in enumerate(ts_files): file_path os.path.join(ts_folder, filename) file_size os.path.getsize(file_path) total_size file_size print(f正在合并 ({i1}/{len(ts_files)}): {filename} ({file_size / (1024*1024):.2f} MB)) with open(file_path, rb) as in_f: # 以二进制读取模式打开每个TS文件 while True: # 分块读取避免一次性加载大文件到内存 chunk in_f.read(buffer_size) if not chunk: # 读取到文件末尾 break out_f.write(chunk) # 将块写入输出文件 print(f 已完成) except IOError as e: print(f文件读写错误{e}) # 如果中途出错可能输出文件是损坏的可以考虑删除 if os.path.exists(output_file): os.remove(output_file) return except Exception as e: print(f合并过程中发生未知错误{e}) return print(f\n合并完成) print(f输出文件{output_file}) print(f总大小{total_size / (1024*1024):.2f} MB) if __name__ __main__: ts_folder ./ts_files output_file ./output/merged_binary.ts # 输出通常还是.ts格式 os.makedirs(os.path.dirname(output_file), exist_okTrue) # 可以调整buffer_size来优化性能通常8MB或16MB在机械硬盘上效果更好 merge_ts_with_ffmpeg(ts_folder, output_file, buffer_size8*1024*1024)6.3 适用场景与重大限制何时使用二进制拼接来源确定你非常确定所有这些TS文件来自同一个视频源并且是用标准工具如FFmpeg的segment功能按时间或大小切割而成的。无需处理你只需要把它们拼回去不需要任何格式转换、检查或修复。追求速度合并成千上万个文件时这种方法的速度优势非常明显因为它几乎没有计算开销。必须警惕的限制坑编码一致性所有TS文件的视频编码H.264, H.265、音频编码AAC, MP3、分辨率、码率、帧率等必须完全一致。如果不一致拼接出来的文件虽然能播放但播放器在遇到参数变化的位置时可能会卡顿、花屏或音画不同步。文件纯净度文件必须是“干净”的TS流。如果文件开头或结尾包含额外的元数据如某些下载工具添加的信息、或者文件本身有损坏拼接后的文件可能无法播放。时间戳连续性TS流内部包含PCR节目时钟参考、PTS/DTS显示/解码时间戳。如果切割点不是恰好在GOP图像组边界或者文件本身的时间戳不连续直接拼接会导致时间戳跳变播放器可能无法正确处理。无加密如果TS文件是加密的例如一些流媒体的m3u8配合#EXT-X-KEY直接二进制合并毫无意义合并后的文件仍然是加密的无法播放。实操心得在决定使用二进制拼接前我习惯用FFmpeg快速检查一下前几个文件的编码信息。命令是ffmpeg -i segment_0001.ts。对比几个文件输出的Stream #0:0视频流和Stream #0:1音频流信息确保Video: h264,Audio: aac等关键信息一致。如果信息完全一致且文件来源可靠二进制拼接就是最快的选择。7. 方案对比与性能实测为了给你更直观的参考我在同一台机器上SSD硬盘16GB内存对包含100个、每个约10MB的TS文件进行了合并测试。特性/方法FFmpeg (-c copy)MoviePy (默认编码)二进制拼接核心原理调用外部FFmpeg工具进行流复制使用Python库通常重新编码Python直接进行文件IO操作合并速度极快(~3秒)慢(~2分钟)最快(~1.5秒)输出质量无损(源文件质量)有损(取决于编码参数)取决于源文件格式兼容性优秀自动处理容器良好但依赖库封装无完全依赖源文件处理异常能力强能处理部分时间戳问题中等依赖库健壮性无任何问题都会导致输出错误资源占用低 (FFmpeg进程)高 (Python内存)极低 (仅文件IO)代码复杂度中等 (需处理命令和路径)低 (API简洁)极低适用场景通用推荐绝大多数情况Python视频处理流水线中的一环来源单一、格式纯净的TS文件快速合并结论FFmpeg方案是综合最佳选择在速度和质量上取得了完美平衡且能处理大多数现实世界中的小问题。二进制拼接是特定场景下的利器当条件满足时它能提供无与伦比的速度。MoviePy更适合作为更复杂视频编辑流程的一部分单纯的合并任务并非其主战场。8. 常见问题排查与实战技巧在实际操作中你可能会遇到以下问题问题1合并后的视频播放时中间有卡顿、跳帧或音画不同步。可能原因TS文件序列顺序错误个别TS文件损坏源TS文件本身的时间戳就不连续常见于网络抓取的流。排查与解决检查顺序用播放器如VLC单独播放疑似卡顿位置前后的几个TS文件看是否本身就有问题。使用FFmpeg检查对有问题的时间点附近的TS文件用ffmpeg -i problem.ts检查是否有解码错误。尝试用FFmpeg重新编码合并如果流复制模式有问题可以尝试让FFmpeg重新编码一次去掉-c copy但这会非常慢且有损。命令如ffmpeg -f concat -safe 0 -i filelist.txt -c:v libx264 -crf 23 -c:a aac output.mp4。-crf是质量参数值越小质量越高18-28是常用范围。问题2FFmpeg合并时报错 “Protocol not found” 或 “Unsafe file name”。可能原因filelist.txt中的文件路径格式不对或没有使用-safe 0参数。解决确保filelist.txt中每行格式为file /path/to/file.ts并且路径用引号括起。始终在命令中加入-safe 0。问题3合并后的文件体积远小于/大于TS文件总和。可能原因体积小很多可能使用了-c copy但输出容器如.mp4不支持输入流的某种编码虽然罕见导致某些流被丢弃。检查FFmpeg合并时的输出信息看是否有“Stream mapping”和“copy”的提示。体积大很多MoviePy默认重新编码如果码率参数设置过高会导致体积膨胀。FFmpeg流复制模式体积应该基本等于总和。问题4处理大量TS文件如上万个时内存不足或效率低下。对于FFmpeg其concat协议本身支持大量文件瓶颈在于生成巨大的filelist.txt。可以尝试分组合并例如每1000个文件合并成一个中间文件最后再合并中间文件。对于二进制拼接这是最佳选择只需注意buffer_size的设置过小增加IO次数过大消耗内存通常8-16MB是个平衡点。通用技巧使用Python的生成器yield来分批读取文件列表而不是一次性将所有文件名加载到内存列表中虽然文件名列表通常不是瓶颈。一个实用的调试技巧在运行FFmpeg命令时如果不确定发生了什么可以先去掉-y参数并让FFmpeg输出更详细的信息。也可以将标准错误输出重定向到文件以便查看# 在subprocess.Popen中将stderr重定向到文件 with open(ffmpeg_log.txt, w) as log_file: process subprocess.Popen(cmd, stderrlog_file)然后查看ffmpeg_log.txt文件里面通常有详细的错误原因。9. 扩展应用从M3U8列表自动下载并合并很多时候TS文件来源于一个M3U8播放列表。我们可以将这个流程自动化。思路是解析M3U8文件获取所有TS片段的URL然后下载它们最后使用上述方法之一进行合并。这里提供一个非常简化的概念性代码框架使用requests和concurrent.futures进行并发下载以提升速度import os import re import requests from concurrent.futures import ThreadPoolExecutor, as_completed def download_ts_from_m3u8(m3u8_url, output_dir./ts_files, max_workers5): 从M3U8 URL下载所有TS片段。 注意这是一个基础示例未处理加密、重试、复杂相对路径等生产环境问题。 os.makedirs(output_dir, exist_okTrue) # 1. 下载M3U8文件内容 resp requests.get(m3u8_url) resp.raise_for_status() m3u8_content resp.text # 2. 解析出所有的.ts文件链接简易正则实际应更健壮 # 假设TS链接是完整的URL ts_urls re.findall(rhttps?://[^\s]\.ts, m3u8_content) # 如果是相对路径需要根据m3u8_url拼接完整URL # base_url /.join(m3u8_url.split(/)[:-1]) / # ts_urls [base_url line.strip() for line in m3u8_content.splitlines() if line.endswith(.ts)] print(f找到 {len(ts_urls)} 个TS片段。) # 3. 定义下载单个TS文件的函数 def download_one(url, idx): filename os.path.join(output_dir, fsegment_{idx:04d}.ts) # 添加重试机制和headers是很好的实践 r requests.get(url, streamTrue) r.raise_for_status() with open(filename, wb) as f: for chunk in r.iter_content(chunk_size8192): f.write(chunk) return filename # 4. 使用线程池并发下载 downloaded_files [] with ThreadPoolExecutor(max_workersmax_workers) as executor: future_to_url {executor.submit(download_one, url, idx): url for idx, url in enumerate(ts_urls, start1)} for future in as_completed(future_to_url): url future_to_url[future] try: file_path future.result() downloaded_files.append(file_path) print(f下载成功: {os.path.basename(file_path)}) except Exception as e: print(f下载 {url} 时出错: {e}) print(所有片段下载完成。) # 此时output_dir中已经充满了按顺序命名的.ts文件 # 可以直接调用上面任何一种合并方法 # merge_ts_with_ffmpeg(output_dir, ./output/final_video.mp4)重要警告此代码仅用于学习和演示自动化思路。在实际抓取网络视频时请务必遵守相关网站的服务条款和法律法规尊重版权。许多M3U8流媒体使用加密AES-128需要额外的解密步骤这超出了本文的范围。三种方法各有千秋没有绝对的好坏只有合不合适。经过这么多年的项目实践我的个人体会是FFmpeg的流复制-c copy是解决TS合并问题的“银弹”在95%的情况下它都能完美工作应该作为你的首选方案。二进制拼接则是当你对数据源有绝对掌控时的“性能利器”。而MoviePy把它留给那些需要更多创意编辑的时刻吧。最后再分享一个小技巧如果你经常需要处理这类任务可以将FFmpeg合并的代码封装成一个命令行工具通过传递文件夹路径和输出文件名作为参数来调用这样就能在任意地方快速使用极大提升效率。自动化永远是程序员对抗重复劳动的最好武器。