基于XPath与动态分页策略的B站视频信息批量采集实战
1. 为什么需要批量采集B站视频信息
最近在做市场调研时,我发现手动收集B站视频数据实在太费时间了。每次搜索关键词后,都要一页页翻看,记录视频标题、播放量这些基础信息。特别是当需要分析某个垂直领域的内容趋势时,这种手工操作方式简直让人崩溃。
这时候就需要用到自动化采集技术了。通过编写爬虫程序,我们可以快速获取B站搜索结果页的视频信息。比如你想了解"人工智能"相关视频的播放量分布,或者想分析某个UP主的内容发布规律,批量采集数据就能帮上大忙。
XPath作为XML路径语言,特别适合用来解析HTML文档结构。相比正则表达式,它的语法更直观,定位元素也更精准。我在实际项目中测试过,用XPath提取B站视频信息,代码可读性和稳定性都比其他方法要好。
2. 准备工作与环境搭建
2.1 必备工具清单
在开始写代码前,我们需要准备好这些工具:
- Python 3.6+(推荐3.8版本)
- requests库(用于发送HTTP请求)
- lxml库(解析HTML和XPath处理)
- pandas库(数据存储和处理)
安装这些库很简单,用pip一行命令就能搞定:
pip install requests lxml pandas
2.2 分析B站页面结构
打开B站搜索页面,按F12进入开发者工具。这里有个小技巧:点击开发者工具左上角的箭头图标,然后鼠标悬停在页面元素上,就能自动定位到对应的HTML代码。
我注意到B站的搜索结果页有几个特点:
- 视频卡片都包含在class为"bili-video-card"的div中
- 第一页和第二页的DOM结构有细微差异
- 分页参数是通过URL的query string控制的
3. XPath定位实战技巧
3.1 基础定位方法
在开发者工具中,右键点击元素选择"Copy XPath"可以快速获取定位路径。但直接复制的XPath往往过于具体,容易受页面结构调整影响。我建议使用相对路径和属性组合的方式。
比如获取视频标题,可以用:
//div[contains(@class,'bili-video-card')]//h3/@title
这样即使外层容器变化,只要class名称不变,定位依然有效。
3.2 处理动态分页
B站的搜索结果页有个特点:第一页的URL结构和其他页不同。经过测试,我发现分页规律是这样的:
第一页URL示例:
https://search.bilibili.com/all?keyword=人工智能
第二页开始会添加page参数:
https://search.bilibili.com/all?keyword=人工智能&page=2
在代码中需要做判断处理:
if 'page=' not in url:
# 第一页的特殊处理
items = bs.xpath('//div[@id="search-result"]//div[contains(@class,"video-card")]')
else:
# 其他页的通用处理
items = bs.xpath('//div[contains(@class,"video-list")]//div[contains(@class,"video-card")]')
4. 完整代码实现与优化
4.1 基础采集框架
先来看核心代码结构:
import requests
from lxml import etree
import pandas as pd
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)'
}
def parse_page(url):
html = requests.get(url, headers=headers).text
bs = etree.HTML(html)
# 这里放XPath提取逻辑
videos = []
return videos
def main():
base_url = "https://search.bilibili.com/all?keyword={keyword}&page={page}"
keyword = "人工智能"
all_videos = []
for page in range(1, 6): # 采集前5页
url = base_url.format(keyword=keyword, page=page)
videos = parse_page(url)
all_videos.extend(videos)
df = pd.DataFrame(all_videos)
df.to_excel("b站视频数据.xlsx", index=False)
4.2 异常处理与反爬策略
在实际运行中,我发现几个需要注意的问题:
- 请求频率控制:连续快速请求容易被封,建议添加随机延迟
import time
import random
time.sleep(random.uniform(1, 3)) # 1-3秒随机延迟
- 元素缺失处理:不是所有视频都有弹幕数等信息
try:
barrage = item.xpath('.//span[contains(@class,"barrage")]/text()')[0]
except IndexError:
barrage = '0'
- 代理设置:如果IP被封,可以考虑使用代理池
proxies = {
'http': 'http://your_proxy:port',
'https': 'https://your_proxy:port'
}
response = requests.get(url, headers=headers, proxies=proxies)
5. 数据处理与存储优化
5.1 数据清洗技巧
采集到的原始数据往往需要清洗:
# 处理播放量字符串(如"12.3万"转成123000)
def clean_play_count(text):
if '万' in text:
return int(float(text.replace('万','')) * 10000)
return int(text)
df['播放量'] = df['播放量'].apply(clean_play_count)
5.2 存储方案选择
根据数据量大小,可以考虑不同存储方式:
- 小数据量(<1万条):Excel或CSV
- 中等数据量:SQLite
- 大数据量:MySQL或MongoDB
我常用的CSV存储代码:
import csv
with open('data.csv', 'w', newline='', encoding='utf-8-sig') as f:
writer = csv.DictWriter(f, fieldnames=fieldnames)
writer.writeheader()
writer.writerows(data)
6. 项目实战经验分享
在实际项目中,我遇到过几个典型问题:
-
XPath失效:B站前端改版导致定位失效。解决方案是使用更宽松的定位策略,比如用contains()匹配class名而不是完整路径。
-
分页中断:有时采集到第5页就停止了。后来发现是因为页码参数有特殊处理,需要在URL中添加"&o=xxx"参数。
-
数据不全:部分视频信息加载较慢。可以尝试添加等待时间或使用Selenium等工具。
一个健壮的采集脚本应该包含这些功能:
- 自动重试机制
- 断点续采功能
- 日志记录系统
- 数据去重处理
最后提醒一点,采集数据时要注意遵守网站的使用条款,控制请求频率,避免对服务器造成过大压力。
更多推荐


所有评论(0)