发布时间:2026/9/7 6:23:58
Python爬虫实战:B站高清视频下载与音画合并完整方案 简介面向Python爬虫初学者的实战入门示例演示如何批量下载哔哩哔哩小视频并在控制台实时显示每个文件的下载进度。核心逻辑围绕分页请求展开脚本循环遍历10页排行榜JSON数据从中提取视频标题与直链地址再调用requests模块以流式方式写入本地video目录标题中的非法字符会被自动替换为空避免文件保存失败。同时每次下载完成后随机等待3-6秒再发起下一次请求体现基础的反爬虫访问节奏控制。压缩包内仅包含1个py源文件大小约2KB代码量虽然不大但完整覆盖了网络请求、JSON解析、文件写入、字符串清洗与时间调度等常见知识点适合入门者逐行阅读并改造。目前已有495人学习查看对希望快速掌握网页数据抓取与文件下载流程的爬虫学习者来说是一份可直接运行、便于扩展的迷你参考脚本。 干过视频下载的朋友都知道B站网页端看视频是一码事想把高清资源稳稳当当存到本地是另一码事。之前我也试过各种在线解析网站和浏览器插件不是要会员就是限速遇到分P视频更是麻烦。后来索性自己用Python写了一个带实时进度显示的B站视频下载爬虫把从视频页提取数据到音画合并的完整流程跑通了一遍顺便把进度条也做进去了。这篇文章就把这套方案的思路、关键细节和踩坑记录完整拆出来给有同样需求的朋友做个参考。1. 整体设计与方案选择1.1 为什么选接口解析而不是网页HTML解析B站视频页面是典型的动态渲染前端视频的真实地址藏在JavaScript异步请求里直接抓HTML源码只能拿到一堆脚本标签和初始化数据拿不到真正的视频流地址。早期很多爬虫靠正则从window.__playinfo__里抠数据但B站页面改版频繁这种硬解析方式说废就废。我这边选择直接模拟B站客户端调用官方接口通过视频页的HTML拿到aid稿件ID和cid分P ID再请求播放信息接口获取视频流地址。接口虽然是公开的但必须带上正常的请求头伪装成用户浏览器访问否则容易被拦截或者直接返回风控错误。提示目前B站的播放信息接口返回的是DASH格式的音画分离流视频轨和音频轨分开存放需要分别下载后用工具合并。这是平台为了节省带宽和防盗链做的设计下载器必须适配这种格式。1.2 技术选型requests加标准库就够对比过scrapy和aiohttp等方案后我最终选了requests加标准库的组合。B站下载本质是“拿接口数据再依次下载几个文件”并发要求不高requests的同步流式下载配合tqdm进度条已经足够好用不必引入过于复杂的异步框架。此外音画合并环节用了ffmpeg通过子进程调用命令行完成。requests负责网络请求ffmpeg负责本地文件处理各司其职代码结构非常清晰也方便日后维护。依赖清单如下requests处理所有HTTP请求和流式下载tqdm实现下载进度条的可视化re、json数据提取与解析subprocess调用ffmpeg合并音视频os文件路径与命名处理安装命令就一条pip install requests tqdm另外提前下载好ffmpeg并配置到系统环境变量里后面合并音视频的时候要用。2. 核心机制拆解B站视频流是怎么组织的2.1 从视频页到cid的获取过程每个B站视频都有唯一的bvid形如BV1xx411c7mD通过bvid可以构造出视频页地址。用requests请求这个页面后在HTML源码里能找到一个包含视频信息的全局对象。有两种提取方式第一种是正则匹配aid:\d这种方式去抠优点是代码简单缺点是页面一旦调整字段格式就容易失效。第二种是用json解析嵌入的数据结构相对稳健一些。我实际用的方法是匹配cid:\d和aid:\d两个字段。需要注意的是如果视频是多P合集HTML初始数据里通常包含所有分P的cid列表但直接解析会比较繁琐更稳妥的做法是额外请求分P列表接口拿到每个分P对应的cid再逐一下载。2.2 DASH流与音画分离机制B站的高清视频使用的是DASH自适应流媒体技术会把视频画面和音频轨拆成独立的文件流。播放的时候由播放器同时拉取两条流然后同步播放但下载下来就是两个文件一个纯画面多半是.m4s格式一个纯音频同样.m4s格式。拿到这两个文件之后必须使用ffmpeg把它们合成为一个完整的视频文件。这就是为什么很多初次接触的人会疑惑明明一个视频怎么解析出来一堆链接其实视频轨和音频轨各对应一个链接缺一不可。单纯下载视频轨得到的文件没有声音只下载音频轨又看不到画面。好在接口返回的JSON数据里已经把视频轨和音频轨分好了类通常视频轨道在dash.video列表音頻轨道在dash.audio列表。我们只需要分别取最高画质和最高音质的URL进行下载即可。2.3 请求头伪装和Referer校验B站对视频下载请求的校验有两道硬门槛第一道是User-Agent。必须伪装成主流浏览器的UA直接裸用Python默认UA会被识别为爬虫大概率返回-412状态码请求被拦截。第二道是Referer。下载视频流时请求头里必须带Referer: https://www.bilibili.com/否则服务器会拒绝返回完整的视频流数据。这一点是B站防盗链的核心逻辑很多新人在这一步翻车。我实际使用的请求头模板如下headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36, Referer: https://www.bilibili.com/, }3. 实操过程完整实现一个带进度条的下载器3.1 获取video与audio流的地址以BV1xx411c7mD为例第一步是请求视频页面获取aid和cid。我写了这样一个函数import re import requests def get_page_info(bvid): url fhttps://www.bilibili.com/video/{bvid} headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 } resp requests.get(url, headersheaders) resp.encoding utf-8 html resp.text aid re.search(raid:(\d), html).group(1) cid re.search(rcid:(\d), html).group(1) return aid, cid这里有个细节正则匹配的时候页面源码里可能出现多个匹配项但第一个aid和第一个cid通常就是当前页对应的主视频实际测试下来基本稳定。拿到这两个关键参数后接着请求播放信息接口。接口地址是api_url fhttps://api.bilibili.com/x/player/playurl?bvid{bvid}cid{cid}fnval16fnval16这个参数很关键它告诉服务器我要返回DASH格式的数据也就是音视频分离的URL列表。如果不带这个参数可能只能拿到flv格式的低清流或者拿不到完整的音视频解析结果。接口返回的JSON里包含dash.video和dash.audio两个数组。视频数组里每个元素都有id字段表示清晰度比如64代表720P80代表1080P112代表1080P高码率。音频数组里通常有不同码率的音軌优先选bandwidth最大的那个即可。选择最高画质的逻辑非常简单video_list data[dash][video] video_info video_list[0] # 默认第一个就是最高画质 for item in video_list: if item[id] video_info[id]: video_info item audio_list data[dash][audio] audio_info max(audio_list, keylambda x: x[bandwidth])3.2 流式下载与进度显示拿到URL之后进入实际下载环节。这里比较关键的一点是必须用streamTrue参数让requests以流式方式接收数据否则一次性加载整个文件到内存里遇到几个GB大视频时内存直接爆掉。进度条我用tqdm实现先通过响应头里的Content-Length字段拿到文件总大小再按块迭代写入本地文件。一个可复用的下载函数长这样from tqdm import tqdm def download_file(url, filepath, headers): resp requests.get(url, headersheaders, streamTrue) total_size int(resp.headers.get(Content-Length, 0)) chunk_size 1024 * 1024 # 1MB with open(filepath, wb) as f, tqdm( totaltotal_size, unitB, unit_scaleTrue, descfilepath.split(/)[-1], ncols100 ) as bar: for chunk in resp.iter_content(chunk_sizechunk_size): f.write(chunk) bar.update(len(chunk))iter_content是流式下载的核心方法每次拉取1MB数据写入文件tqdm根据写入字节数实时更新进度条。整个过程非常直观终端里能看到文件名、下载速度、剩余时间体验很接近专业的下载工具。下载完成后目录下会产生两个临时文件命名我习惯用video_temp.m4s和audio_temp.m4s方便合并脚本统一识别。3.3 音画合并与文件清理最后一步是用ffmpeg把两条流合成一个完整视频。命令行参数不复杂ffmpeg -i video_temp.m4s -i audio_temp.m4s -c copy output.mp4-c copy表示直接复制流不重新编码速度非常快一个30分钟的视频通常几秒到十几秒就能完成画质也零损失。由于我是在Python里调用写成子进程执行import subprocess def merge_video_audio(video_path, audio_path, output_path): cmd [ ffmpeg, -y, -i, video_path, -i, audio_path, -c, copy, output_path ] subprocess.run(cmd, checkTrue) os.remove(video_path) os.remove(audio_path)-y参数是让ffmpeg覆盖同名文件时不弹确认提示方便集成到脚本里自动执行。合并完成后再把两个临时文件删掉整个流程就闭环了。4. 常见问题与排查技巧实录4.1 请求被拦截返回-412状态码这是最常见的问题。排查思路很明确先看看请求头里的UA和Referer是不是带齐了。如果带了还是被拦截有一个很有效的补救措施——给requests会话添加一个Cookie从浏览器里复制任意一个B站登录态的SESSDATA字段buvid3也可以。即便不是VIP账号带上Cookie后风控概率会大幅下降。4.2 up主设置了禁止下载部分up主在投稿时会开启“禁止下载”选项这种情况下接口返回的视频流URL是带水印的版本即使下载下来画面上也会有明显的B站水印和up主ID。另外还有一种情况是充电专属视频非充电用户无法获取播放地址。这不是爬虫代码能绕过的限制属于平台规则层面。我的处理方式是在代码里增加一个检测步骤请求播放接口后判断返回的URL里是否包含mcdn.bilivideo.cn或者upg-bc字样如果带这些特征就提示用户该视频可能受下载限制而不是闷头下载一个带水印的版本。4.3 下载到一半网络中断视频文件大的时候网络波动难免。requests的流式下载如果中途断掉已写入的本地文件并不完整重新下载只能从头再来。我在实际使用中做了两个优化第一个是给真实下载函数增加超时重试机制。第二个是结合tqdm进度条显示的已下载大小对特别大的文件可以手动中断后只下载缺失区段。这个比较复杂日常使用中做重试就够。重试的简化写法如下for attempt in range(3): try: download_file(url, filepath, headers) break except Exception as e: print(f第{attempt1}次下载失败: {e}) time.sleep(3)4.4 ffmpeg合并时报错“Unknown encoder”这个多半是ffmpeg安装版本太老或者没有正确配置。建议去官网下载完整版而不是精简版Windows用户下载后把bin目录路径添加到系统环境变量Path里。验证安装是否成功的方法很简单命令行执行ffmpeg -version能正常输出版本信息就说明配置OK。5. 使用效果与实际体验整套脚本跑下来当一个普通1080P视频约30分钟大小在500MB到1GB之间下载时视频轨和音频轨是并行串行下载的两段加起来大概1分钟到2分钟左右快速取决于网速。之后ffmpeg合并阶段由于是直接复制流通常不到5秒就能出片。片头片尾、弹幕这些都不需要特殊处理下载的就是纯正的正片内容。如果想连弹幕一起保存B站有独立的弹幕接口按cid就能取到XML格式的弹幕这个以后再单独写一篇展开。我个人用这套脚本最大的感受是B站的下载限制比想象中少只要用正规接口并带上合规的请求头大部分公开视频都能顺利下载。关键在于理解它的接口设计逻辑而不是跟页面结构硬刚。学会通过接口拿数据的思路之后其他视频平台的下载器也差不多是同一个套路只是参数和格式细节不同。希望这篇内容能给想自己动手做下载工具的朋友提供一些真实可参考的经验。本文还有配套的精品资源点击获取

相关新闻

2026/9/7 6:23:58

硬件工程师面试20问:从电路基础到电源PCB全解析

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

2026/9/7 6:23:58

Python网格处理实现晶格体表面自定义浮雕的完整流程

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

2026/9/7 6:23:58

Jaspersoft Studio 6.21.3实用指南:安装、汉化与中文字体配置

简介:这是一份Jaspersoft Studio 6.21.3报表设计器的完整发行版资源,主要面向企业级Java开发人员、数据分析人员以及JasperReports库的使用者,解决报表模板制作、数据源连接、打印导出等环节的环境搭建与依赖管理问题。资源包共包含1762个文件…

2026/9/7 7:24:00

荣归之刻神都王PVE强度测评:从定位替换看阵容升级

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

2026/9/7 7:24:00

项目生命周期管理:从lain42.top案例看技术项目专业关闭流程

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

2026/9/7 7:24:00

Swin-Transformer源码级审计:从窗口注意力到工程化落地选型

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

2026/9/7 7:24:00

自动驾驶仿真测试场景设计:概念、方法与工程实践

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

2026/9/7 7:24:00

无人值守后台服务:幂等性、重试与可观测性的稳定设计

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

2026/9/7 7:19:00

事业单位面试九类题型底层逻辑与答题框架全解析

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

2026/9/7 0:47:43

超人会飞不算本事:系统稳定依赖清晰规则与边界设计

开头先不绕弯子。“#斯坦李吐槽dc 所以超人是无缘无故会飞的嘛哈哈哈哈哈哈哈锤哥真是技术人才啊!#雷神 #复联”这类调侃式短标题,第一波冲击力在于它把两个宇宙的角色塞进同一个吐槽箱里,但细想一下就能发现,它真正碰到的根本不是…

2026/9/7 0:14:19

超人VS蜘蛛侠:拆解超级IP的影响力与传播方法论

把“蜘蛛侠 vs 超人”放在 CSDN 上聊,可能很多人第一反应是走错片场了。但如果把这两个角色看成“两个持续运营了 80 多年的文化产品”,你会发现,这场比较本质上是两个不同 IP 策略的长期结果对比:超人赢在定义了整个超级英雄题材…

2026/9/7 0:14:17

基于CNN的调制信号识别:MATLAB实现时频图分类实战

简介:本资源是一套面向通信工程与信号处理方向学习者、研究者的深度学习实践方案,聚焦调制信号自动检测与识别这一典型无线通信任务,解决传统方法依赖人工特征、低信噪比下性能下降等痛点。压缩包共12个文件(10.73MB)&…

2026/9/7 0:03:36

基于YOLOv8和PyQt5的麦穗稻穗检测识别系统设计与实现

这次我们来看一个把目标检测算法和桌面端工具结合得很典型的项目:基于 YOLOv8 PyQt5 的麦穗稻穗检测识别系统。这个项目本身不是新概念,但它的价值在于落地形态很完整。YOLOv8 负责核心的麦穗稻穗目标检测,PyQt5 负责提供可视化的桌面交互界…

2026/9/7 0:03:36

UL 1642锂电池安全标准全解析:测试项目、认证流程与避坑指南

简介:UL 1642是锂电池安全领域的重要规范,本中文版资源适合锂电池制造商、检测机构工程师及产品认证相关人员阅读,用于理解电池在设计与制造层面的安全要求、测试方法与合规要点。资源共1个PDF文件,压缩包大小834KB,便…

2026/9/7 0:03:36

BS EN 13814-1-2019游乐设施安全标准:设计与制造核心要点解析

简介:BS EN 13814-1:2019是英国采纳欧洲标准EN 13814-1:2019的正式版本,由BSI标准出版,重点规定游乐设施和游乐设备在设计与制造环节的安全准则,与BS EN 13814-2:2019、BS EN 13814-3:2019共同取代旧版BS EN 13814:2004。该标准面…

2026/9/6 11:40:10

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/6 19:33:50

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/6 10:19:40

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…