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

千面之法: 释放 C++ 多态的灵活威力

千面之法: 释放 C++ 多态的灵活威力

目录 1:多态的概念 1.1:概念 2.多态的定义与实现 2.1:多态的构成条件 2.2:虚函数 2.3:虚函数的重写 2.3.1:虚函数重写的两个例外 2.3.1.1:协变(基类与派生类函数的返回值不同,基类虚函数返回基类对象的指针或引用,派生类虚函数返回派生类对象的指针或引用时) 2.3.1.2:析构函数的重写 2.4:C++11 override和final 2.4.1:final关键字 2.4.2:override关键字 2.5:重载、

By Ne0inhk

Trae编译C++

一、前置准备 1. 安装 Trae: * 下载对应系统版本(Windows/Linux/macOS),解压到自定义目录(如D:\trae); * 配置环境变量(将 Trae 的可执行文件路径加入系统PATH),确保终端 / 命令行能直接输入trae调用。 2. 确认依赖:Trae 依赖 GCC/Clang,需先安装: * Windows:安装 MinGW(推荐 MinGW-w64),配置gcc环境变量; * Linux:sudo apt install gcc g++(Debian/Ubuntu); * macOS:xcode-select --install安装 Xcode 命令行工具。 二、用 Trae 编译 C++ 的核心步骤(

By Ne0inhk

MISRA C++静态分析集成CI/CD:项目应用示例

将MISRA C++静态分析融入CI/CD:一位嵌入式工程师的实战手记 最近在参与一个车载ECU模块的开发,团队面临的最大挑战不是功能实现,而是如何确保每一行代码都经得起功能安全标准ISO 26262的严苛审查。我们最终选择将 MISRA C++ 静态分析深度集成到CI/CD流程中——这不仅是一次工具链升级,更是一场从“写完再检”到“边写边防”的工程思维转变。 今天我想以这个真实项目为例,和你聊聊我们是如何一步步把这套看似“教条”的编码规范,变成流水线里沉默却可靠的“守门人”的。 为什么是MISRA C++?不只是合规,更是系统确定性的基石 如果你做过汽车电子、工业控制或航空航天类项目,大概率听说过 MISRA(Motor Industry Software Reliability Association) 。它最初由英国汽车工业界发起,目标很明确: 让C/C++这种强大但危险的语言,在关键系统中变得可控、可预测。 而 MISRA C+

By Ne0inhk
【C++】优选算法必修篇之双指针实战:有效三角形个数 & 和为s的两个数字

【C++】优选算法必修篇之双指针实战:有效三角形个数 & 和为s的两个数字

【C++】优选算法必修篇之双指针实战:有效三角形个数 & 和为s的两个数字 * 双指针应用场景 * 目录 * 1. 有效三角形个数 * 1.1 题目链接 * 1.2 题目描述 * 1.3 题目示例 * 1.4 算法思路 * 1.5 核心代码 * 1.6 示例测试(总代码) * 2. 和为s的两个数字 * 2.1 题目链接 * 2.2 题目描述 * 2.3 题目示例 * 2.4 算法思路 * 2.5 核心代码 * 2.6 示例测试(总代码) * 总结

By Ne0inhk