尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Python网页视频下载全攻略:从直链到m3u8分片合并

Python网页视频下载全攻略:从直链到m3u8分片合并 简介面向网页视频下载需求的Python脚本工具包适合编程初学者、爬虫爱好者以及希望快速保存网页视频的普通用户。脚本基于requests等库实现网页请求与解析可定位网页中的视频源并支持M3U8流媒体地址获取与分段下载帮助用户绕过网页无法直接下载的限制。资源包共4个文件均为.py脚本整体仅2KB体积小巧、逻辑精简对应视频页跳转、页面解析、M3U8链接提取、视频持久化保存等不同环节读者可参考脚本结构快速理解视频下载的完整流程。已有351人学习下载。借助这份小工具既能获得可直接使用的下载脚本也能学习Python网络请求、HTML结构分析与m3u8协议处理等实用技能若配合自身网页环境稍作调整还可扩展到更多视频网站。总体而言这是一套兼具工具属性与学习价值的轻量级代码包。 说个很实在的场景网上刷到一个挺喜欢的视频想存到本地慢慢看但下载按钮找不到、浏览器插件今天能用明天失效、录屏出来的文件又大又模糊。折腾到最后很多人都会冒出同一个念头——能不能用Python自己写一个下载器这个想法没问题而且Python确实是干这件事最顺手的工具。这篇内容不绕弯子直接把我常用的Python网页视频下载方案拆开讲从工具选型、环境准备到手写代码抓取真实视频地址再到处理m3u8分片下载合并一次把整条链路说清楚。不管你是刚装好Python还没写过几行代码的新手还是已经写过爬虫、想做批量下载的进阶选手都能在这里面找到可以立刻上手的方案。文章里所有方法我都实测过核心就两件事一是搞清楚视频真正的地址藏在哪里二是掌握一套能应对大部分网站的分片下载与合并流程。1. 为什么用Python搞定网页视频下载先说一个很多人容易搞混的点网页视频下载难点从来不在“下载”本身而在于“定位”。你眼睛看到的是一个播放器浏览器背后干的却是“先拉一个清单、再按清单拿文件、最后拼起来播放”的三步操作。Python的优势就是能把这三步完全拆开、逐步控制并且用脚本自动化执行。1.1 你真正需要的能力是什么下载网页视频本质上只需要四样东西视频文件或分片文件的真实URL、正确的请求头伪装成浏览器访问、稳定的下载逻辑超时重试、断点续传、以及合并分片的处理能力。这四样东西对应到Python里分别就是URL分析能力、requests或httpx库、循环和异常处理、ffmpeg工具。听起来不复杂但每一样都有不少坑尤其是URL分析和请求头伪造这两步90%的下载失败都栽在这里。你看到的播放器地址往往是一个临时生成的动态地址几分钟后就会过期直接复制粘贴到浏览器里大概率打不开更别提用下载工具去抓了。1.2 常见下载方案的短板市面上有很多现成工具比如浏览器插件、IDM、各种在线解析网站我也用过不少。它们的问题很一致能用的时候很爽不能用了就很抓狂。插件类工具本质上是在拦截浏览器发出的网络请求只要网站改一下播放器协议或者接口规则插件就失灵而且你得等作者更新适配。在线解析网站就更不稳定上传链接等半天解析出来的清晰度还被压缩过有些站点甚至会在解析后的视频里插广告。IDM这类下载管理器虽然对直链视频靠谱但遇到m3u8这种分片协议要么不支持要么需要额外配置。Python方案的好处是代码在自己手里出问题可以自己排查、自己改。网站改协议你就改代码逻辑接口变了你就重新抓包分析。主动权完全在自己手上不依赖任何第三方工具的更新节奏。1.3 Python方案的核心优势再往深一层说Python做这件事的独特价值在于两点批量化和定制化。批量化很好理解写一个循环把几十个视频链接丢进去自动下载、自动重命名、自动按分类存储彻底解放双手。定制化就更有意思了——你可以只下载某个清晰度的视频可以自动跳过已经下载过的文件可以设置下载时段避开网络高峰甚至可以结合定时任务让脚本每天早上自动抓取你追更的剧集。这些能力是任何现成下载工具都给不了的。所以我的建议是现成工具能解决需求就用现成工具省事一旦遇到现成工具搞不定的场景就是你该拿起Python的时候。这篇文章后面讲的内容就是你拿起Python之后的完整路线图。2. 环境准备与工具选型动手之前先把环境搞好把工具选对。这个环节花15分钟后面能省下好几个小时的折腾时间。2.1 Python安装与基础环境配置如果你电脑上还没装Python先装Python。Windows用户直接去官网下载安装包安装时务必勾选“Add Python to PATH”这个选项——我见过太多新手卡在“python不是内部或外部命令”这个报错上基本都是因为漏勾了这一步。装完之后打开命令行Windows是CMD或PowerShellmacOS是终端先验证一下安装结果python --version能正常输出版本号就说明安装成功。接着升级pip并安装本章后面会用到的依赖库python -m pip install --upgrade pip pip install requests yt-dlp这里装了两个东西requests用于手写请求代码yt-dlp作为高可靠性的备用下载引擎。后面第三部分会分别讲它们的用法先装好放着。另外强烈建议顺手把ffmpeg也装上——这是视频分片合并的关键工具Windows用户去官网下载解压后把bin目录加到系统环境变量Path里macOS用户直接用brew install ffmpeg最省事。验证方式是在命令行输入ffmpeg -version能输出信息就代表配置完成。2.2 三大主流下载方案对比环境准备好之后我们把市面上最常用的Python视频下载方案放到一起对比方便你按场景选择。方案核心库/工具原理难度适用场景yt-dlp一键下载yt-dlp内置大量网站解析规则低主流视频平台、快速下载requests手动解析requests 正则/字符串处理从HTML中提取视频地址中自定义需求、学习原理m3u8分片下载合并requests ffmpeg拉取索引文件、并行下载分片、合并较高流媒体协议、长视频有个细节值得强调yt-dlp不是万能的。它依赖社区维护的站点规则小众网站或者刚改版的网站它一样会失败。但作为第一道方案它的性价比极高——你只需要两行代码就能完成一次完整下载。requests方案虽然麻烦但胜在完全可控。m3u8方案则是处理流媒体类网站的核心技能。我把这三个方案都放在后面的实战部分你可以按需取用。2.3 理解网页视频的真实结构在写代码之前还有一个认知需要建立网页视频分两种主流形态。第一种是“直链视频”也叫伪流媒体。这种最省事页面里直接存在一个mp4或webm格式的文件地址。你打开浏览器开发者工具按F12切到Network面板刷新页面后筛选Media类型就能看到这类文件。它的特点是地址栏里会直接出现视频文件后缀名。第二种是“分片流媒体”也就是m3u8协议。这是目前视频网站的主流做法——视频先被切成几秒一段的小文件再用一个m3u8索引文件记录所有分片的顺序和地址。播放器拿到m3u8文件后按顺序逐个加载分片实现无缝播放。这种方案的好处是方便服务器做码率自适应用户看什么清晰度就加载对应的那组分片。代价是下载逻辑复杂了不少要先拿m3u8索引再挨个下载几十上百个ts分片最后还要按顺序合并成一个完整的视频文件。理解这两种结构之后后面的代码就都是在完成“定位地址、下载文件、按需处理”这三步操作。接下来进入实战。3. 实战从一键命令到手工代码这一部分是整篇文章的干货核心我会按从易到难的顺序把三条下载路线完整走一遍。每条路线都会附上可直接运行的代码并补充必要的参数说明。3.1 方案一yt-dlp零代码快速下载如果你的目标是“快速、省事、支持网站多”直接用yt-dlp。它是一个命令行工具同时也提供了Python API。装好之后最简单的用法是这样yt-dlp 视频页面地址它会自动提取页面里的视频信息选择默认清晰度进行下载。想要指定清晰度和格式可以加参数yt-dlp -f bestvideobestaudio/best --merge-output-format mp4 视频页面地址解释一下bestvideobestaudio表示分别下载最好的视频流和音频流再用ffmpeg合并成一个文件/best表示如果合并失败就退而求其次下载最好的单一文件--merge-output-format mp4指定最终输出格式为mp4。如果你是在Python代码里调用可以这样写import yt_dlp url 视频页面地址 ydl_opts { format: bestvideobestaudio/best, merge_output_format: mp4, outtmpl: %(title)s.%(ext)s, # 输出文件名模板 } with yt_dlp.YoutubeDL(ydl_opts) as ydl: ydl.download([url])这里的outtmpl是输出模板%(title)s会自动替换成视频标题。码率、播放列表下载、字幕下载等高级功能也都是通过这个ydl_opts字典来配置的。注意yt-dlp对部分站点可能下载失败这是正常现象。此时不要死磕我一般会抓取出错信息里的关键报错比如“Unsupported URL”然后直接用方案二手动解析。3.2 方案二用requests手动解析直链视频接下来是手工方案。以直链视频为例我们用requests加正则表达式来提取并下载。先确认目标视频是直链F12打开开发者工具刷新页面Network面板里筛选Media如果能看到mp4或webm文件就是直链。然后在页面源码里找到这段地址。下面这段代码模拟的是“从HTML里提取第一个mp4地址并下载”的通用流程import re import requests def download_video(url, output_namevideo.mp4): headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36, Referer: url, # 很多网站校验Referer } # 1. 拉取页面HTML resp requests.get(url, headersheaders, timeout10) resp.encoding utf-8 html resp.text # 2. 提取mp4地址这里只是一个最简正则实际需按页面结构调整 matches re.findall(r(https?://[^\]\.mp4), html) if not matches: print(未找到视频地址) return video_url matches[0] # 3. 下载视频文件 with requests.get(video_url, headersheaders, streamTrue) as r: r.raise_for_status() with open(output_name, wb) as f: for chunk in r.iter_content(chunk_size8192): if chunk: f.write(chunk) print(f下载完成{output_name}) if __name__ __main__: download_video(https://example.com/video_page)几个关键点说一下。第一headers里的User-Agent和Referer不是摆设。很多网站的服务器会检查这两个字段如果你的请求看起来不像浏览器发出的它会直接拒绝最常见的表现就是返回403错误。第二正则表达式只是最简单的提取手段。实际页面里视频地址不一定直接出现在HTML里可能藏在JavaScript变量中或者通过Ajax接口异步加载。这时候就需要F12去看Network面板里的XHR请求找到真正返回视频地址的那个接口再用requests去请求这个接口拿数据。判断逻辑是优先找返回JSON的接口解析其中的字段比死磕正则要靠谱得多。第三下载大文件时一定要用streamTrue的方式配合iter_content按块写入。如果直接resp.content一次性读进内存遇到几个GB的高清视频内存分分钟爆掉。3.3 方案三处理m3u8分片并合并最后是重头戏m3u8分片视频的下载。这也是当前主流视频网站最常用的协议。首先找到m3u8地址F12 → Network面板 → 筛选Media/XHR → 刷新页面 → 找到.m3u8后缀的请求。拿到了m3u8地址后整个流程分三步。第一步是读取m3u8索引文件理解它的结构。一个典型的m3u8文件长这样#EXTM3U #EXT-X-VERSION:3 #EXT-X-TARGETDURATION:10 #EXT-X-MEDIA-SEQUENCE:0 #EXTINF:10.0, https://example.com/segments/segment_0.ts #EXTINF:10.0, https://example.com/segments/segment_1.ts #EXTINF:10.0, https://example.com/segments/segment_2.ts #EXT-X-ENDLIST#EXTINF后面的数字表示该分片的播放时长秒接下来一行是分片的URL。有些m3u8文件里的分片地址是相对路径需要手动拼接成完整的URL。第二步是下载所有分片。为了提高速度我会用多线程并行下载import os import requests from concurrent.futures import ThreadPoolExecutor def download_segment(args): seg_url, file_path args headers {User-Agent: Mozilla/5.0} for attempt in range(3): # 失败重试3次 try: resp requests.get(seg_url, headersheaders, timeout15) if resp.status_code 200: with open(file_path, wb) as f: f.write(resp.content) return True except Exception: continue return False # 假设segments是一个[(地址, 本地文件名), ...]列表 tasks [(seg_url, os.path.join(segments_dir, fseg_{i}.ts)) for i, seg_url in enumerate(segments)] with ThreadPoolExecutor(max_workers8) as executor: results list(executor.map(download_segment, tasks)) print(f成功下载 {sum(results)}/{len(results)} 个分片)线程数max_workers建议设置在8到16之间。开太少下载速度提不上来开太多容易被服务器限流反而导致大量分片下载失败。我在实测中8线程是最稳妥的平衡点。第三步是合并分片。这里就必须用到ffmpeg了。先用文本文件列出所有分片路径然后让ffmpeg按顺序合并# 在segments_dir目录下生成filelist.txt每行格式file seg_0.ts ffmpeg -f concat -safe 0 -i filelist.txt -c copy output.mp4-c copy表示不做重新编码直接复制流复制数据速度极快且画质无损。需要注意所有分片必须是同一种编码格式如果某些分片参数不一致ffmpeg会报错。这时可以去掉-c copy让它重新编码但速度会慢很多。整个m3u8流程走完之后你手里的output.mp4就是完整可播放的视频文件。4. 常见问题与排查技巧代码写出来是一回事跑通又是另一回事。下面这几个问题是我在实际操作中踩过、也帮别人排查过的高频坑整理出来给你避雷。4.1 下载403与超时多半是请求头没伪装好下载时报403 Forbidden90%的原因是请求头里的User-Agent或Referer没有设置或者设置得不完整。服务器检测到请求不像浏览器发出的就直接拒绝了。排查思路打开F12找到视频地址对应的那条请求右键 → Copy as cURL能看到浏览器发出的完整请求头。把你缺的关键字段都补到代码里基本就能解决。还有一个常见情况是“请求超时”如果视频服务器对并发请求数量有限制多线程下载时大量请求同时到达很容易触发限流。解决方案就是降低线程数且在代码里增加重试逻辑我在前面方案三的代码里就写了3次重试实际效果很明显。4.2 分片合并失败检查分片完整性和编码一致性用ffmpeg合并分片时报错先分两步排查。第一步确认所有分片都完整下载了——对比m3u8文件里的分片数量和本地实际文件数量是否一致差一个都不行。第二步用ffprobe工具检查分片的编码信息ffprobe seg_0.ts查看输出里的Video和Audio编码格式。如果所有分片格式一致问题基本出在部分分片文件损坏上重新下载那些文件就好。如果分片格式不一致比如有的是H.264有的是H.265那就只能去掉-c copy参数让ffmpeg统一重新编码。4.3 下载完只有画面没有声音这个问题非常典型。很多网站会把视频流和音频流拆成两个独立的文件尤其是高清视频。如果你下载的文件只有画面没有声音说明你只拿到了视频流没拿音频流。解决办法有两个一是用yt-dlp时加bestvideobestaudio参数让它自动下载两部分再合并二是手动在Network面板里同时找到视频流和音频流的地址分别下载后用ffmpeg把音轨合并进去ffmpeg -i video.mp4 -i audio.m4a -c copy output.mp44.4 版权与合规提醒技术能力本身是中性的能用来下载公开视频也能用来搬运别人辛苦创作的内容。这里必须说清楚本文介绍的方法适用于下载自己拥有版权的内容、允许下载的视频或者用于学习研究目的。未经授权下载并传播他人版权视频可能涉及侵权请务必遵守相关法律法规和平台服务条款。做技术的人更应该在享受技术便利的同时守住分寸。最后再分享一个实用技巧按照这套流程实际操作过几次之后我自己的习惯是遇到一个新的视频下载需求先用yt-dlp跑一遍能成功就直接用失败就F12抓包判断是直链还是m3u8再决定走哪条手工路线。这套“先快后精”的策略帮我省下了大量时间。另外下载下来的文件命名也建议一开始就规范化比如用“日期_标题_清晰度.mp4”这个格式。批量下载的时候文件名混乱会让你后期整理时欲哭无泪。我就是早期吃过这个亏后来在代码里统一加了date前缀现在本地视频库一眼就能扫完。这种小细节初看不值一提用久了才知道有多香。本文还有配套的精品资源点击获取
返回列表