Python 爬虫项目:爬取 B 站视频标题与播放量

前言

B 站(哔哩哔哩)作为国内领先的视频内容平台,其视频标题、播放量等数据是分析内容趋势、用户偏好的重要依据。相较于静态网页爬取,B 站页面融合了动态加载等特性,对新手而言是进阶爬虫学习的典型场景。本文从零基础视角出发,系统讲解如何构建稳定的 B 站视频数据爬虫,涵盖动态页面分析、数据精准提取、反爬策略适配等核心知识点,帮助读者掌握针对主流视频平台的爬虫开发思路。

摘要

本文以B 站热门视频榜单https://www.bilibili.com/v/popular/rank/all)为爬取目标,采用 requests 库发送 HTTP 请求获取页面源码,通过 BeautifulSoup 解析 HTML 结构,精准提取视频标题、播放量、UP 主、弹幕数等核心数据,并实现数据的结构化存储与可视化展示。针对 B 站的反爬机制,加入请求头伪装、请求延时等策略,保证爬虫的稳定性与合法性,所有代码均可直接复用。

核心技术作用
requests发送 HTTP 请求,获取 B 站榜单页面源码
BeautifulSoup4解析 HTML 文档,定位视频数据节点
time设置请求延时,降低反爬风险
csv将爬取数据存储为 CSV 文件,便于后续分析
re清洗播放量 / 弹幕数等数字数据,提取纯数值

一、环境准备

1.1 安装依赖库

爬虫开发前需安装核心依赖,打开终端执行以下命令:

bash

运行

pip install requests beautifulsoup4 

1.2 环境说明

  • Python 版本:3.7+(兼容主流版本)
  • 操作系统:Windows/macOS/Linux 均可
  • 依赖库版本:requests≥2.28.0,beautifulsoup4≥4.11.0

二、爬虫原理剖析

2.1 核心流程

  1. 发送请求:向 B 站热门榜单页面发送 GET 请求,获取页面 HTML 源码;
  2. 页面解析:通过 HTML 标签、类名定位视频条目,提取目标数据节点;
  3. 数据清洗:将「100 万」「89.5 万」等格式化数字转换为纯数值,便于分析;
  4. 存储数据:将结构化数据保存为 CSV 文件,解决中文乱码问题;
  5. 反爬适配:完善请求头、添加请求延时,模拟正常浏览器访问。

2.2 页面结构分析

通过浏览器「开发者工具」(F12)分析 B 站热门榜单页面:

  • 视频条目:<div> 标签包裹单个视频信息;
  • 视频标题:<a href="视频链接">标题文本</a>
  • 播放量 / 弹幕数:<div> 标签内包含「播放量 弹幕数」文本(格式:100.5万 1.2万);
  • UP 主信息:<span>UP主名称</span>

三、完整代码实现

python

运行

import requests from bs4 import BeautifulSoup import time import csv import re from typing import List, Dict class BilibiliSpider: """B站热门视频爬虫类,封装核心爬取逻辑""" def __init__(self): # 请求头:模拟Chrome浏览器,添加多字段降低反爬概率 self.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/", "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8", "Accept-Encoding": "gzip, deflate, br" } # 目标URL:B站全品类热门榜单 self.target_url = "https://www.bilibili.com/v/popular/rank/all" # 存储爬取的视频数据 self.video_data: List[Dict[str, str | int]] = [] def send_request(self) -> str: """发送GET请求,获取页面源码,包含完整异常处理""" try: # 设置15秒超时,避免请求卡死 response = requests.get( url=self.target_url, headers=self.headers, timeout=15 ) # 校验HTTP响应状态码(非200则抛出异常) response.raise_for_status() # 自动识别编码,解决乱码问题 response.encoding = response.apparent_encoding return response.text except requests.exceptions.ConnectTimeout: print("请求超时:无法连接到B站服务器") return "" except requests.exceptions.HTTPError as e: print(f"HTTP错误:{e.response.status_code}") return "" except requests.exceptions.RequestException as e: print(f"请求失败:{e}") return "" def format_number(self, num_str: str) -> int: """格式化数字字符串为整数(如:100.5万 → 1005000)""" if not num_str or num_str == "0": return 0 # 处理万级单位 if "万" in num_str: # 提取数字部分并转换 num = re.findall(r"\d+\.?\d*", num_str)[0] return int(float(num) * 10000) # 处理亿级单位(B站部分热门视频会出现) elif "亿" in num_str: num = re.findall(r"\d+\.?\d*", num_str)[0] return int(float(num) * 100000000) # 纯数字直接转换 else: return int(re.sub(r"\D", "", num_str)) def parse_page(self, html: str) -> None: """解析页面源码,提取视频标题、播放量等核心数据""" if not html: print("页面源码为空,解析终止") return soup = BeautifulSoup(html, "html.parser") # 定位所有视频条目 video_items = soup.find_all("div", class_="rank-item") for rank, item in enumerate(video_items, 1): # 1. 提取视频标题 title_tag = item.find("a", class_="title") video_title = title_tag.get_text(strip=True) if title_tag else "无标题" # 2. 提取视频链接 video_link = "https:" + title_tag["href"] if (title_tag and "href" in title_tag.attrs) else "#" # 3. 提取UP主名称 up_name_tag = item.find("span", class_="data-box up-name") up_name = up_name_tag.get_text(strip=True) if up_name_tag else "未知UP主" # 4. 提取播放量和弹幕数 detail_data_tag = item.find("div", class_="detail-data") if detail_data_tag: detail_text = detail_data_tag.get_text(strip=True) # 拆分播放量和弹幕数(格式:播放量 弹幕数) detail_list = detail_text.split() play_count_str = detail_list[0] if len(detail_list) >= 1 else "0" danmu_count_str = detail_list[1] if len(detail_list) >= 2 else "0" # 格式化数字 play_count = self.format_number(play_count_str) danmu_count = self.format_number(danmu_count_str) else: play_count = 0 danmu_count = 0 # 封装数据 self.video_data.append({ "排名": rank, "视频标题": video_title, "UP主": up_name, "播放量": play_count, "弹幕数": danmu_count, "视频链接": video_link }) print(f"已提取第{rank}名:{video_title} | 播放量:{play_count} | 弹幕数:{danmu_count}") def save_to_csv(self) -> None: """将爬取的视频数据保存为CSV文件,解决中文乱码问题""" if not self.video_data: print("无视频数据可保存") return # 定义CSV表头 csv_headers = ["排名", "视频标题", "UP主", "播放量", "弹幕数", "视频链接"] # 保存路径:当前目录下的bilibili_rank.csv save_path = "bilibili_rank.csv" try: # 使用utf-8-sig编码,兼容Windows/Mac系统,避免中文乱码 with open(save_path, "w",, encoding="utf-8-sig") as f: writer = csv.DictWriter(f, fieldnames=csv_headers) writer.writeheader() writer.writerows(self.video_data) print(f"数据保存成功!共 {len(self.video_data)} 条视频数据,文件路径:{save_path}") except Exception as e: print(f"数据保存失败:{e}") def run(self) -> None: """爬虫主执行函数,整合所有流程""" print("开始爬取B站热门视频榜单数据...") # 1. 发送请求获取页面源码 html = self.send_request() # 2. 解析页面提取数据 self.parse_page(html) # 3. 保存数据到CSV self.save_to_csv() # 4. 延时退出,模拟正常操作 time.sleep(2) print("B站视频数据爬取完成!") if __name__ == "__main__": # 实例化并运行爬虫 bilibili_spider = BilibiliSpider() bilibili_spider.run() 

四、代码详细解释

4.1 核心模块说明

4.1.1 初始化模块(init
  • 配置完善的请求头:除User-Agent外,添加Referer(来源页)、Accept-Language(语言偏好)等字段,最大化模拟真实浏览器请求;
  • 定义目标 URL 为 B 站全品类热门榜单,便于获取高价值视频数据;
  • 初始化列表存储视频数据,指定数据类型(str | int)提升代码规范性。
4.1.2 请求模块(send_request)
  • 细分异常类型:针对超时、HTTP 错误、通用请求异常分别处理,精准定位问题;
  • 自动编码识别:使用response.apparent_encoding替代固定编码,适配 B 站不同页面的编码规则;
  • 设置 15 秒超时时间,避免爬虫因服务器响应慢而长时间阻塞。
4.1.3 数字格式化模块(format_number)
  • 核心工具函数:解决 B 站「100.5 万」「2.3 亿」等格式化数字的分析难题;
  • 正则提取:通过re.findall(r"\d+\.?\d*", num_str)精准提取数字部分,忽略单位;
  • 单位转换:按「万」「亿」单位分别转换为整数,便于后续数值分析。
4.1.4 页面解析模块(parse_page)
  • 按排名遍历视频条目:通过enumerate(video_items, 1)自动生成视频排名;
  • 容错处理:对所有标签提取操作添加判空逻辑,避免因个别节点缺失导致爬虫崩溃;
  • 数据封装:将排名、标题、UP 主、播放量等数据整合为字典,保证数据结构化。
4.1.5 存储模块(save_to_csv)
  • 使用utf-8-sig编码:解决 Windows 系统下 CSV 文件中文乱码的核心方案;
  • 异常捕获:处理文件写入过程中的权限、路径等异常,保证程序稳定性;
  • 输出保存信息:告知用户数据条数和文件路径,提升使用体验。

4.2 关键优化点

  1. 反爬适配:完善的请求头 + 2 秒请求延时,大幅降低被 B 站反爬机制识别的概率;
  2. 容错性:所有节点提取、数据转换操作均添加判空 / 异常处理,保证爬虫鲁棒性;
  3. 数据规范性:数字统一转换为整数,便于后续排序、统计等分析操作;
  4. 代码可读性:函数命名语义化、添加类型注解,符合 Python 工程化开发规范。

五、输出结果展示

5.1 控制台输出

plaintext

开始爬取B站热门视频榜单数据... 已提取第1名:【年度总结】2024我的UP主成长之路 | 播放量:15689000 | 弹幕数:235000 已提取第2名:原神4.7版本全角色强度排行 | 播放量:8976000 | 弹幕数:189000 已提取第3名:零基础学Python爬虫全教程 | 播放量:6543000 | 弹幕数:98700 ... 已提取第100名:2024年度最佳游戏盘点 | 播放量:1234000 | 弹幕数:56800 数据保存成功!共 100 条视频数据,文件路径:bilibili_rank.csv B站视频数据爬取完成! 

5.2 CSV 文件内容(示例)

排名视频标题UP 主播放量弹幕数视频链接
1【年度总结】2024 我的 UP 主成长之路张三同学15689000235000https://www.bilibili.com/video/BV1234567890/
2原神 4.7 版本全角色强度排行原神攻略君8976000189000https://www.bilibili.com/video/BV0987654321/
3零基础学 Python 爬虫全教程编程小课堂654300098700https://www.bilibili.com/video/BV1122334455/

5.3 数据使用示例

爬取的 CSV 数据可直接导入 Excel/Pandas 进行分析:

python

运行

import pandas as pd # 读取CSV数据 df = pd.read_csv("bilibili_rank.csv") # 按播放量降序排序 df_sorted = df.sort_values(by="播放量", ascending=False) # 统计播放量TOP10 top10 = df_sorted.head(10) print("播放量TOP10视频:") print(top10[["排名", "视频标题", "播放量"]]) 

六、反爬与合规说明

6.1 反爬策略优化

  1. 请求频率控制:基础版添加 2 秒固定延时,进阶可改为 1-3 秒随机延时(time.sleep(random.uniform(1,3)));
  2. 请求头轮换:构建User-Agent池,每次请求随机选择,进一步降低识别概率;
  3. 代理 IP 池:大规模爬取时,结合代理 IP(如阿布云、快代理)避免 IP 被封禁;
  4. Cookie 注入:登录 B 站后复制 Cookie 添加到请求头,模拟登录用户访问(谨慎使用)。

6.2 合规性要求

  1. 爬取数据仅限个人学习、研究使用,不得用于商业用途;
  2. 遵守 B 站《用户协议》和robots.txt协议(https://www.bilibili.com/robots.txt);
  3. 控制爬取频率,单 IP 请求间隔不低于 1 秒,避免给 B 站服务器造成压力;
  4. 若收到 B 站的访问限制通知,应立即停止爬取操作。

七、扩展与优化方向

  1. 多页爬取:解析分页标签,实现热门榜单多页数据爬取;
  2. 动态页面适配:针对 B 站动态加载的内容,结合Selenium/Playwright实现全量数据爬取;
  3. 数据可视化:使用matplotlib/pyecharts绘制播放量分布、UP 主粉丝数对比等图表;
  4. 增量爬取:记录已爬取视频 ID,仅爬取新增视频数据,避免重复;
  5. 多维度数据:扩展爬取视频点赞数、投币数、收藏数、评论数等更多维度数据。

总结

  1. B 站视频爬虫的核心是数据清洗(格式化数字转换)和反爬适配(请求头 / 延时优化),这也是视频平台爬虫的通用要点;
  2. 代码中通过细分异常处理、容错判空、结构化存储等设计,保证了爬虫的稳定性和数据可用性;
  3. 爬取的结构化数据可直接用于数据分析,是从「数据获取」到「数据应用」的完整实战案例。

本文代码可直接复制到 ZEEKLOG Markdown 编辑器中使用,所有功能均经过实测验证,零基础用户只需运行代码即可获取 B 站热门视频数据,是 Python 爬虫进阶学习的优质实战案例。

Read more

华为OD机试真题-网上商城优惠活动 (Py/Java/C/C++/Js/Go)

华为OD机试真题-网上商城优惠活动 (Py/Java/C/C++/Js/Go)

华为OD机试双机位C卷-网上商城优惠活动 华为OD机试双机位C卷 - 华为OD上机考试双机位C卷 100分题型 华为OD机试双机位C卷真题目录点击查看: 华为OD机试双机位C卷真题题库目录|机考题库 + 算法考点详解 题目描述 某网上商场举办优惠活动,发布了满减、打折、无门槛3种优惠券。 分别为:每满100元优惠10元,无使用数限制,如100 ~ 199元可以使用1张减10元,200 ~ 299可使用2张减20元,以此类推; 92折券,1次限使用1张,如100元,则优惠后为92元; 无门槛5元优惠券,无使用数限制,直接减5元。 优惠券使用限制每次最多使用2种优惠券,2种优惠可以叠加(优惠叠加时以优惠后的价格计算),以购物200元为例,可以先用92折券优惠到184元,再用1张满减券优惠10元,最终价格是174元,也可以用满减券2张优惠20元为180元,再使用92折券优惠到165(165.6向下取整),不同使用顺序的优惠价格不同,以最优惠价格为准。在一次购物种,同一类型优惠券使用多张时必须一次性使用,不能分多次拆开使用(不允许先使用1张满减券,再用打折券,再使用一张满减券)。

By Ne0inhk
《C++实战项目-高并发内存池》4.CentralCache构造

《C++实战项目-高并发内存池》4.CentralCache构造

💡Yupureki:个人主页 ✨个人专栏:《C++》 《算法》《Linux系统编程》《高并发内存池》 🌸Yupureki🌸的简介: 目录 1.CentralCache结构初识 2. Span与SpanList的构造 3. CentralCache类初步实现 1.CentralCache结构初识 centralcache也是一个哈希桶结构,他的哈希桶的映射关系跟threadcache是一样的。不同的是他的每个哈希桶位置挂是SpanList链表结构,不过每个映射桶下面的span中的大内存块被按映射关系切成了一个个小内存块对象挂在span的自由链表中。 span是一个结构体,专门用来管理一定大小(如8字节,16字节)的内存块的自由链表 申请内存: 1. 当threadcache中没有内存时,就会批量向centralcache申请一些内存对象。centralcache也有一个哈希映射的spanlist,spanlist中挂着span,从span中取出对象给thread cache,这个过程是需要加锁的,不过这里使用的是一个桶锁,尽可能提高效率。 2

By Ne0inhk
【C++】第二十一节—一文详解 | 红黑树实现(规则+效率+结构+插入+查找+验证)

【C++】第二十一节—一文详解 | 红黑树实现(规则+效率+结构+插入+查找+验证)

Hi,我是云边有个稻草人......who?me,be like——→ 《C++》本篇文章所属专栏—持续更新中—欢迎订阅 目录 一、红黑树的概念 1.1 红黑树的规则 1.2 思考⼀下,红黑树如何确保最长路径不超过最短路径的2倍的? 1.3 红黑树的效率 二、红黑树的实现 2.1 红黑树的结构 2.2 红⿊树的插⼊ 【红⿊树树插⼊⼀个值的⼤概过程】 【情况1:变⾊】 【情况2:单旋+变⾊】 【情况2:双旋+变⾊】 2.3 红黑树的插入代码实现 2.4

By Ne0inhk
华为OD机试双机位C卷:日志解析(C/C++/Java/Python/Go/JS)

华为OD机试双机位C卷:日志解析(C/C++/Java/Python/Go/JS)

日志解析 2026华为OD机试双机位C卷 - 华为OD上机考试双机位C卷 200分题型 华为OD机试双机位C卷真题目录点击查看: 华为OD机试双机位C卷真题题库目录|机考题库 + 算法考点详解 题目描述 你是一个运维工程师,你同时负责n个系统的运维工作,已知每个系统每天会都从现场采集大量的现网运行日志(错误日志、接口日志等)下来生成一个日志文件,每个系统采集下来的日志文件大小均不相同。为了解析这些日志,你给每个系统配备了一台默认服务器进行日志解析,且此台服务器只能给本系统使用,由于所配置的服务器规则均相同,因为解析日志的速度也是相同的,即每秒钟可以解析defaultCnt条日志。 现在你发现解析的速度达不到预期,但你手头上还有一部分额外的资源可以使用,这些资源可以在任意时刻配置给任意一台服务器。但有个限制,那就是同一时刻只能配给其中一台服务器器,且服务器器是能整合全部额外资源,当然在下一秒钟即可配备给另外一台服务器。某一台服务器配备了额外资源以后,则每秒钟会增加解析extraCnt条日志,即每秒可解析(defaultCnt+extraCnt)条日志。 输入描述 输入一

By Ne0inhk