近期好多朋友问我同一个问题网上看到想收藏的视频浏览器自带的下载功能要么不给力要么只能看不能下到底怎么才能把视频弄到本地我给出的答案基本都是同一个——用Python和requests库自己写个下载脚本。这不是炫技而是因为它足够简单、跨平台、还几乎不需要什么额外依赖。Python本身就是写爬虫和数据采集最顺手的语言requests作为其中最常用的HTTP请求库处理视频下载这种“请求一个URL、把响应内容写入文件”的事属于典型的小马拉小车但拉得飞快。这篇文章我不打算只给一段能跑的代码就完事我会把整个流程掰开揉碎了讲清楚为什么用requests而不是浏览器直接下载、下载视频的真实原理是什么、代码怎么写才不容易踩内存爆炸的坑、以及你大概率会遇到的防盗链和请求头问题怎么破。不管你是刚接触Python三天的新手还是写了段时间脚本但没正经处理过文件流的开发者这篇都值得你花几分钟看完然后把它存进收藏夹。1. 从“右键另存为”到requests为什么非要写代码下载视频先说一个很多人没想明白的问题视频本质上就是一个大文件浏览器右键“视频另存为”不行吗为什么非要绕一圈用requests去下答案分两层。第一层很多网站的播放器用了blob加密流或m3u8切片右键根本拿不到真实地址。你以为是mp4实际播放器内部转成了几十个ts分片需要拼接这个后面细讲。第二层即使你拿到了视频直链浏览器下载也有几个痛点不支持断点续传中断就重来不便于批量下载无法自定义请求头比如携带Referer做防盗链校验。而requests完全不受这些限制——它就是一个“什么都不做”的HTTP客户端你想加什么头、想从哪个字节开始下载、想一次并发几个任务全部由代码说了算。对于正在学Python和爬虫的朋友来说视频下载还是很好的练手项目它把requests的流式响应、headers构造、文件I/O、进度条处理这些核心技能一次性全用到比单纯爬个静态网页有分量得多。我的建议是别把“下载视频”当成什么见不得人的灰产技能它和“用curl拉文件”本质一样都只是HTTP协议的标准操作。关键是搞清楚原理然后用在合理、合规、尊重版权的场景里比如下载自己上传的素材、授权范围内的公开课程、或CC协议视频。2. 拆开来看requests下载视频的底层逻辑响应流不是一次性吞进内存的很多第一次写视频下载脚本的人代码长这样import requests url https://example.com/video.mp4 resp requests.get(url) data resp.content # 一次性把整个文件读进内存 with open(video.mp4, wb) as f: f.write(data)这段代码能跑但只对几十MB的小文件适用。一旦视频上了500MB你就能亲眼见证电脑卡死、内存爆红、甚至进程被系统杀掉。原因很简单resp.content会把整个响应体保存在内存里相当于把一座仓库整个搬进一间房。正确做法是开启流式响应边下载边写盘resp requests.get(url, streamTrue)streamTrue的意义在于requests只有在访问resp.content或resp.iter_content()时才会读取响应体而iter_content(chunk_size1024)能把文件切成一块块拿内存占用始终保持在很低水平。用一个生活化的例子解释resp.content像是点了一整桌菜等服务员一次性全端上来桌子不够大就崩了iter_content则像后厨做一道上一道盘子永远不会满你随时可以停筷。所以在视频下载场景里正确姿势是import requests url https://example.com/video.mp4 headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 } resp requests.get(url, headersheaders, streamTrue) resp.raise_for_status() with open(video.mp4, wb) as f: for chunk in resp.iter_content(chunk_size1024 * 512): # 512KB一块 if chunk: f.write(chunk)这里if chunk是为了过滤掉一些keep-alive等场景下拉回来的空块避免无意义写盘。512KB是个比较均衡的块大小写入次数适中内存占用也不会超过几MB。3. 动手实操一个能直接用的视频下载类核心代码逐行拆解贴一段我平时常用的下载类核心代码不算长但是把该处理的细节都处理了import os import time import requests class VideoDownloader: def __init__(self, url, save_path, headersNone): self.url url self.save_path save_path self.headers headers or { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 } def get_file_size(self): resp requests.get(self.url, headersself.headers, streamTrue) return int(resp.headers.get(Content-Length, 0)) def download(self): # 先取下文件总大小为的是算进度条 total_size self.get_file_size() resp requests.get(self.url, headersself.headers, streamTrue) resp.raise_for_status() downloaded 0 with open(self.save_path, wb) as f: for chunk in resp.iter_content(chunk_size1024 * 512): if chunk: f.write(chunk) downloaded len(chunk) percent downloaded / total_size * 100 if total_size else 0 print(f\r已下载 {downloaded}/{total_size} ({percent:.1f}%), end) print() if __name__ __main__: d VideoDownloader( urlhttps://example.com/sample.mp4, save_pathsample.mp4, ) d.download()这段代码看起来平平无奇但有三个细节值得单独拿出来说。第一个细节是Content-Length。有些服务器在响应头里不返回这个字段或者返回的值是gzip压缩后的大小而非真实文件大小所以get_file_size()拿到的可能不准。对于这种情况我的处理是不要强依赖总大小算进度条可以改成显示“已下载XX MB”这种不依赖总量的展示。第二个细节是raise_for_status()。这一行会在服务器返回4xx或5xx时抛异常避免你下载到一个HTML错误页面还以为是成功。很多视频链接有时间戳或防盗链参数过期后返回403有这个检查你能第一时间发现。第三个细节是print带\r的回车符刷新进度。在命令行里这样写在同一行刷新进度不会刷屏。注意如果你在IDE里跑可能显示不正常这种情况就改成每下载1%打印一行或者干脆不打印。3.1 如果下载中断了怎么办先学会判断文件完整性下载到一半断网、被服务器踢下线、电脑睡眠……这些都可能导致文件损坏。所以下载完成后别急着收工先验证一下。最简单的方法是比对文件大小。如果你之前拿到了Content-Length下载完后用os.path.getsize()看一下对不上基本就废了。但网站返回的是chunked编码时Content-Length可能不存在这时候只能通过播放器打开试看或者用ffprobe检查ffprobe -v error -show_entries formatduration -of defaultnoprint_wrappers1:nokey1 video.mp4能输出时长说明文件基本完整报错则说明中间有缺失。这也是我为什么推荐在脚本里增加一个“已下载大小”的跟踪而不是闷头写完就拉倒。后面讲的断点续传核心也是围绕已下载的字节位置做文章。4. 为什么代码能跑却总下载失败请求头、防盗链和Cookie问题深挖如果你照着上面的代码跑大概率会遇到两类报错一类是启动即失败的403、404、429另一类是下载中途失败的ConnectionError、ReadTimeout。这些问题的根源多数不在requests用法本身而在请求头和服务器的博弈。4.1 403 Forbidden防盗链是怎么起作用的视频网站不像静态网页那样开放服务器会检查请求头里的Referer字段判断这次请求是不是从合法的页面过来的。如果你直接copy视频地址到requests里下不带Referer服务器直接一个403砸过来意思是“你是从哪儿来的”解决办法是在headers里加上headers { User-Agent: Mozilla/5.0 ..., Referer: https://video-site.com/watch/xxx, }这里的Referer应该填视频所在播放页的URL而不是视频文件URL。有些站还会校验Origin、Sec-Fetch-Dest等字段但大多数情况下UA加Referer就够用。4.2 420和429请求频率太快被限流下载单个视频不会触发但如果你写了个for循环批量下载连续快速请求几十个服务器就会开始拒绝你。429表示请求过多420表示“你的请求跟我服务器上的某个限流策略冲突了”。被限流之后的正确处理方式是等。用time.sleep加随机延时比如每次下载完随机等2到5秒。如果对方返回Retry-After头就按照这个头指定的秒数等。代码很简单import time import random time.sleep(random.uniform(2, 5))4.3 Cookie与登录态能在浏览器下载但脚本403的根源还有一种很常见的情况浏览器里能正常播放、能下载但requests一跑就403。这是因为你浏览器里带着登录后的Cookie而requests不带。解决办法在浏览器开发者工具里从Network面板复制请求头里完整的Cookie字符串粘到headers里。Cookie往往有有效期过期后再粘一次就行。4.4 Request header is too largeCookie粘太多会撑爆服务器热词里有“request header is too large”这个我实际遇到过整段复制浏览器请求头时不小心把几百KB的Cookie都粘进去了服务器直接拒绝处理。正常请求头总大小应该控制在几KB到十几KB级别。如果Cookie太长先只挑sessionid、auth_token这类关键项别一把梭全粘。经验是凡是浏览器请求头里带着几百个键值对的大概率是调试太久累积了大量测试Cookies清一下浏览器状态再复制就干净了。4.5 connection failed: error sending request 这类底层网络报错这两个报错本质上都是网络栈层面出了问题常见原因有三类目标服务器主动断开了连接多见于超大文件下载到一半被服务器超时踢出本地网络到目标服务器之间链路的MTU问题或者代理配置把请求踢了本机防火墙/杀毒软件拦截了requests建立的连接排查顺序先确认普通网页能打开同一个视频链接再确认项目里有没有配置代理os.environ里的HTTP_PROXY和HTTPS_PROXY最后看是不是TLS版本问题把requests升级到最新版准没错。5. 进阶断点续传、并发下载和m3u8切片这套组合拳才叫完整方案学会了基础下载之后你会发现实际场景永远比教程复杂。我整理了几种高频进阶需求也是我自己在项目里实际用过的方案。5.1 断点续传避免一个2GB文件从零开始重下原理是用HTTP的Range请求头告诉服务器“我从第N个字节开始给我”。代码核心逻辑如下def download_with_resume(url, save_path, headersNone): resume_pos 0 if os.path.exists(save_path): resume_pos os.path.getsize(save_path) headers headers or {} if resume_pos 0: headers[Range] fbytes{resume_pos}- resp requests.get(url, headersheaders, streamTrue) # 服务器返回206说明支持断点续传返回200说明不支持只能从头来 if resp.status_code 206: mode ab elif resp.status_code 200 and resume_pos 0: print(服务器不支持断点续传重新下载) mode wb resume_pos 0 else: mode wb with open(save_path, mode) as f: for chunk in resp.iter_content(chunk_size1024 * 512): if chunk: f.write(chunk)注意第二个分支不是所有服务器都支持Range请求如果它忽略你的Range头直接返回200和完整内容而你还在用 “ab” 模式会把新内容接到旧文件后面直接导致文件损坏。这也是我最想强调的写续传逻辑前一定先判断状态码和Content-Length不能想当然。5.2 并发分块下载把文件切成几段同时拉对于服务器支持Range且文件很大的场景可以用多个线程分段下载再合并。思路是先拿总大小分成N段每段用不同的Range头并发请求最后按offset写入同一个文件的对应位置。但一般情况下我不推荐为常规视频网站做分块并发。一是很多网站的CDN对违反顺序的分段请求有风控拦截二是单文件顺序下载速度通常已经跑满带宽。分块策略在下载大体积、高带宽瓶颈的场景比如内网服务器拉镜像、数据中心拉模型文件才有立竿见影的效果。5.3 m3u8切片下载需要“另起炉灶”的高频场景短视频平台、在线教育网站、直播回放最常走HLS协议媒体地址是.m3u8文件里面写的是几十甚至上千个.ts分片的地址。用requests下载视频时直接拿m3u8地址下只会得到一个文本文件不是视频。处理流程分三步请求m3u8文件用正则或专门的m3u8解析库推荐m3u8这个Python库解析出ts分片地址列表逐个下载ts分片保存到临时目录用ffmpeg合并所有ts为mp4命令是ffmpeg -f concat -safe 0 -i filelist.txt -c copy output.mp4如果只是想快速扒一个线上课程、直播回放用ffmpeg -i m3u8地址 -c copy output.mp4秒杀一切ffmpeg自己会处理下载和合并。只有当ffmpeg被服务器限流时才需要requests自己逐个ts分片下载并加上延时。6. 换个视角看问题为什么很多人下视频失败卡在“找不到真实地址”而非代码本身写了七八年爬虫我越来越觉得下载视频真正的门槛不是requests怎么写而是怎么找到那个真实的视频文件地址。这里给出几条通用思路。第一打开网页播放视频同时打开开发者工具里的Network面板筛选Media或Fetch/XHR。视频文件一般体积最大几MB到几百MB一眼能认出来。第二看到的是一个blob:开头的地址说明播放器用MediaSource Extensions把视频切成碎片喂给了浏览器真实地址藏起来了。这种情况下要么去源码里找.m3u8或.mpd要么考虑用抓包工具看实际请求的ts/m4s文件。第三很多视频站会套一层播放器鉴权真实地址带?auth_keyxxxexpiresxxx这类动态参数过期就失效。这种情况复制到requests里时注意参数里的特殊字符比如、/、不要被URL编码搞乱了。就算你技术很好也建议只在你自己有权限的范围内做下载。别人的付费课、未授权的电视剧、私密视频无论如何都不应该碰。一个稳妥的判断标准是这个视频是否允许离线观看有没有明显的版权声明拿不准就不下自己本地已购或在合法平台通过官方渠道下载才是最稳的。7. 本地调试时容易踩的坑IDE编码、临时文件、环境变量下载视频脚本在本地跑和在服务器上跑遇到的坑点不太一样。分享几个我踩过的7.1 Windows下文件名带特殊字符直接保存失败很多从m3u8解析出来的文件名会带?、*、/这些非法字符Windows下直接open(xxx?test.mp4, wb)会抛FileNotFoundError或恶意字符报错。给文件名做个清洗import re def safe_filename(name): return re.sub(r[\\/*?:|], _, name)7.2 控制台打印中文乱码Windows PowerShell或cmd默认不是UTF-8print中文路径或进度条时容易乱码。建议脚本开头加上sys.stdout.reconfigure(encodingutf-8)或者干脆进度里不打印中文只打英文和数字。7.3 下载到一半杀毒软件把文件隔离这个比较隐蔽表现是脚本正常走完文件也写着大小正常但打开播放器提示解码失败。大概率是杀毒软件实时防护把下载中的临时文件标记为可疑并隔离了。排查方式是去隔离区看一眼有没有同名文件。真遇到这个可以把下载目录加进白名单。7.4 connection failed和token exchange failed的排查链路这类报错我们做了一张通用排查表遇到直接按顺序查现象最可能原因操作connection failed: error sending request 出现在所有请求前本机代理环境变量有残留打印os.environ.get(HTTP_PROXY)、HTTPS_PROXY有值就del os.environ[...]或置空只发生在单个大文件下载中段服务器主动断连/连接空闲超时设置streamTrue把chunk_size调小如256KB降低单次读取耗时token exchange failed 出现在带登录态接口Cookie/session过期或临时token失效重新请求登录接口刷新Cookie/token确认headers里没有过期字段局域网内下载报错DNS解析或网关拦截ping目标域名确认连通换用IP或改DNS如设置系统DNS再试下载很慢但浏览器很快请求头缺少Accept-Encoding触发CDN未命中gzip/brotli压缩加Accept-Encoding: gzip, deflate, br注意要用bytes方式写入files文件8. 把代码包装成“能给别人用”的小工具值得用的几个优化如果你只想自己偶尔下一个视频上面代码已经够用。但如果你想把它做成命令行小工具给同事用、或打包成EXE下面几个优化可以大幅提升体验。8.1 断点续传到一半的文件分类这是我个人习惯。下载目录里总是有.part或.temp文件所以脚本里把未完成文件后缀改成.part下载完成后rename成目标文件名。这样即使中断了看到.part就知道能续传目录也清爽。part_path self.save_path .part # 下载过程中写入part_path # 完成后 os.rename(part_path, self.save_path)8.2 加入简单的命令行参数解析用argparse可以快速给脚本增加命令行入口比如python video_dl.py https://example.com/video.mp4 -o output.mp4 --referer https://example.com/watch/123这样不用每次改代码里的URL灵活很多。虽然argparse是Python标准库但注意它的parse_args在遇到非法参数时默认会调sys.exit(2)在被其他地方调用时这个行为往往不符合预期所以我一般把parse_args的调用放在if __name__ __main__里面避免作为模块导入时误触发退出。8.3 用tqdm做进度条如果不想自己写print进度直接用tqdm库from tqdm import tqdm with open(save_path, wb) as f: with tqdm(totaltotal_size, unitB, unit_scaleTrue) as pbar: for chunk in resp.iter_content(chunk_size1024 * 512): if chunk: f.write(chunk) pbar.update(len(chunk))tqdm在终端里显示效果非常专业还会自动计算剩余时间和下载速度实用性和颜值都比手写print高一个级别。前提是total参数尽量准确否则进度条会来回跳。9. 在Python学习路线中视频下载应该放在什么位置经常有刚入门的读者问我是不是应该先学爬虫框架Scrapy、再学数据分析然后才能碰视频下载完全不是。下载视频这个任务恰好踩在Python知识图谱里一个很舒服的位置它前置知识极少——只需要会安装requests、会打开终端跑脚本剩下的全是熟练度问题。它又能把requests、文件操作、异常处理、命令行这几块基础技能一次性串起来。所以我其实建议初学者把视频下载作为第一个“真正有实用性”的项目来做做完再决定要不要深入爬虫方向。顺带回应下热词里经常出现的“python安装教程”“python教程”“python入门”这几个搜索需求如果你连Python环境都没有优先去Python官网下载安装包安装时务必勾选“Add Python to PATH”这是新手最容易踩的第一个坑。装完后在命令行验证python --version能输出版本号就说明环境OK接下来就是pip install requests这一步了。环境配好之后一定记住这两个高频报错ModulenotFoundError: No module named requests说明requests没装或装到了别的Python环境用pip install requests重新装pip不是内部命令大概率是PATH没配好把Python安装目录和Scripts目录加进环境变量9.1 关于编辑器选型VSCode还是PyCharmVSCode配合Python插件或者PyCharm Community版哪个都行。初学者我更推荐VSCode因为启动快、配置轻、不会像PyCharm那样加载半天。但需要注意VSCode里选了错误的解释器会导致“明明安装了requests却提示找不到”—这种情况多半是右下角解释器没选对手动切到你安装的那个Python版本即可。10. 一点关于“合理使用”的提醒代码本身中立边界在于使用目的最后想认真聊几句因为它值得被说清楚。requests库本身只是“向服务器发送HTTP请求并接收响应”的工具用它下载视频和用浏览器下载视频在技术层面上没有区别。但在什么时候下载、下载什么内容、下载下来做什么用这些问题才是真正需要判断的。我给自己定的底线非常简单只下载自己拥有版权或授权的视频素材只下载明确允许离线观看的内容一些平台提供官方下载不以任何形式二次传播盗版不将批量下载能力用于倒卖、搬运等违法行为按照这个底线requests下载视频依然是个非常实用且有趣的技能。你完全可以拿它下载自己网盘里备份的课程、处理公司的宣传素材、备份自己发布的视频内容。技术是工具使用工具的人决定它的价值。希望这篇文章不仅帮你解决了下载问题也让你对HTTP请求、文件流、防盗链这些概念有了更深一层的理解。如果按这套思路把脚本跑通了我建议你做的第一件事是拿自己刚写的脚本下载一条短视频然后用ffprobe验证文件完整性。那种“自己写的工具真的能干活”的成就感是看多少教程都换不来的。
企业数字化 ERP 产品动态
相关推荐
2026年加密软件平台选型指南:从个人工具到企业级方案全解析 1. 加密软件平台到底在解决什么问题聊加密软件之前,得先把一个概念理清楚:加密软件不是单一功能的产品,它是一类工具的统称。有人用它保护移动硬盘里的设计图纸,有人用它给客户发合同附件,有人用它管理整个公司的文件外… · 2026/9/24 20:59:34
从聊天框到AI团队:LobeHub排班制Agent协作实战指南 得先交代一下背景。我第一次打开LobeHub的时候,心里想的其实很简单:这不过是一个更好看的AI聊天网页。多换个模型、多几个话题分类、UI更精致,也就这样了。但后来我不经意间把三个不同职责的Agent挂了进去,又加了一层轻量调度&… · 2026/9/24 20:59:28
虚拟化技术实战:从CPU硬件指令到云平台故障排查 1. 这不是概念课,是运维现场的实战笔记“云计算与虚拟化技术详解”——看到这个标题,很多人第一反应是教科书目录、培训PPT封面,或是某次技术分享会上主持人念出的议程条目。但在我过去十年跑过的27个数据中心、亲手部署过412台物理服务器、处… · 2026/9/24 20:59:16
腾讯数字人+大模型知识引擎:RAG驱动的智能交互落地全解析 最近一直在调研数字人和大模型结合落地的方案,腾讯数字人与大模型知识引擎这两个产品放在一起琢磨,信息量其实非常大。数字人负责“像人”,知识引擎负责“懂人”,两个能力叠在一起,才真正解决了一直以来虚拟客服、虚拟… · 2026/9/24 21:32:12
克拉美罗界在DOA估计中的工程实践:推导、Python实现与避坑指南 简介:阵列信号处理中,克拉美罗界(CRB)是参数估计误差的理论下界,源自费歇尔信息矩阵,为任何无偏估计器设定了方差下限。这份资源以克拉美罗界为核心,针对MUSIC与ESPRIT两种经典的空间谱估计算法… · 2026/9/24 21:32:12
大模型长尾知识问答实战:RAG混合检索与GraphRAG方案 1. 长尾问题为什么总是让大模型“一本正经地胡说”1.1 一个真实场景:冷门型号的引脚定义去年帮一个做硬件的朋友查一颗停产多年的电源管理芯片,型号冷门到在主流搜索引擎上只能翻出两份模糊的扫描版数据手册。我顺手把型号丢给某款通用大模型,… · 2026/9/24 21:32:05
AI测试开发转型指南:从手工测试到Agent评测的核心技能与实操路径 1. 从手工测试到AI测试开发:转型的底层逻辑1.1 为什么测试人现在必须关注AI测试开发这两年跟不少做测试的朋友聊天,发现一个很明显的分化:一部分人还在写Selenium脚本、维护接口自动化用例,每天跟元素定位和断言打交道;… · 2026/9/24 21:32:05
基于Lighthouse和Deepseek的QQ私人AI机器人搭建指南 你有没有过这种时刻:明明手机就在手边,却要先解锁、找浏览器、翻书签,才轮到AI聊天框跟你对话。我现在已经很少开网页版AI了,不是它不好用,而是我发现了一个更顺手的方式——直接在QQ里养一个私人AI,把它当… · 2026/9/24 21:32:05
基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程 简介:这是一套面向计算机、人工智能、自动化等专业学生与教师的毕业设计级项目资源,围绕YOLOv8实现渔船作业监控系统,可用于毕设、课程设计、大作业或项目立项演示。压缩包共97个文件,约24.21MB,以70个Python源码文件为… · 2026/9/24 0:00:13
1D-CNN时间序列建模实战:从Conv1d原理到工业落地 简介:面向时间序列数据建模的一维卷积神经网络完整实现,适合深度学习入门者及需要快速验证时序模型的研究者,能够从音频、文本、传感器或股价等序列中挖掘局部特征与时间依赖。压缩包体积很小,只有3KB,内含3个Python脚… · 2026/9/24 0:00:26
柔软的L:汉语语流中被忽视的舌肌张力控制 1. 这个“L”不是字母表里的L,而是舌尖上的L最近在几个方言群和语音教学社群里,反复看到有人发一句:“也说字母L:柔软的长舌”。初看以为是英语发音课笔记,点开才发现全是方言爱好者、播音系学生、语言康复师甚至戏曲演… · 2026/9/24 0:00:44