1. 为什么需要B站UP主视频数据爬虫
在内容创作和数据分析领域,获取平台视频的详细数据已经成为刚需。对于B站UP主而言,分析自己或竞品账号的视频表现是优化内容策略的基础;对于MCN机构,需要批量监控旗下账号的数据表现;而对于数据分析师,这些数据则是研究平台生态和用户行为的重要素材。
目前B站官方API存在诸多限制:一是未开放所有数据字段(如精确播放量只显示"xx万"这样的模糊值),二是频繁调用容易触发限流,三是无法批量获取多个视频的完整数据包。这就使得通过爬虫技术直接从网页端获取数据成为更可行的方案。
一个完善的B站视频爬虫应该能获取以下核心数据维度:
- 基础信息:视频标题、AV/BV号、发布时间、时长
- 核心指标:精确播放量、弹幕数、点赞、投币、收藏、转发
- UP主信息:昵称、UID、个人简介
- 内容标签:视频分区、参与的活动标签
- 社交数据:实时评论区热词(需额外开发)
2. 技术选型与工具准备
2.1 基础工具链搭建
Python仍然是爬虫开发的首选语言,主要依赖以下库:
python复制# 网络请求
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
# 页面解析
from bs4 import BeautifulSoup
import json
# 数据存储
import openpyxl
from datetime import datetime
# 反爬应对
import random
import time
from fake_useragent import UserAgent
特别说明几个关键选择:
- 使用requests.Session配合重试机制,比单纯用requests.get更稳定
- BeautifulSoup虽然比lxml慢,但对异常HTML容错更好
- 放弃selenium方案,因为:
- 资源消耗大
- 被识别风险高
- 实际测试发现B站前端渲染的数据同样需要接口调用
2.2 反爬应对策略
B站的反爬机制主要包括:
- 频率限制(每分钟约20-30次请求较安全)
- User-Agent检测
- 行为指纹识别
我们的应对方案:
python复制def create_session():
session = requests.Session()
retries = Retry(total=3, backoff_factor=1)
session.mount('https://', HTTPAdapter(max_retries=retries))
ua = UserAgent()
session.headers.update({
'User-Agent': ua.random,
'Referer': 'https://www.bilibili.com/',
'Accept-Language': 'zh-CN,zh;q=0.9'
})
return session
重要提示:务必设置5秒以上的随机延迟,避免短时间内高频访问。实测连续请求超过30次/分钟会触发临时封禁。
3. 核心数据抓取实现
3.1 视频基础信息获取
B站视频页的数据实际上是通过接口返回的JSON数据,隐藏在前端代码中。通过分析页面结构,我们发现关键数据位于:
html复制<script>window.__INITIAL_STATE__={...}</script>
提取逻辑示例:
python复制def get_video_info(vid):
url = f"https://www.bilibili.com/video/{vid}"
session = create_session()
try:
resp = session.get(url, timeout=10)
resp.raise_for_status()
soup = BeautifulSoup(resp.text, 'html.parser')
script = soup.find('script', text=re.compile('window.__INITIAL_STATE__'))
json_str = script.text.split('=', 1)[1].rstrip(';')
data = json.loads(json_str)
video_data = data['videoData']
return {
'title': video_data['title'],
'aid': video_data['aid'],
'bvid': video_data['bvid'],
'pubdate': datetime.fromtimestamp(video_data['pubdate']),
'duration': video_data['duration'],
'desc': video_data['desc'],
'owner': {
'mid': video_data['owner']['mid'],
'name': video_data['owner']['name']
}
}
except Exception as e:
print(f"获取视频{vid}信息失败: {str(e)}")
return None
3.2 详细统计数据抓取
播放量等详细数据需要通过另一个接口获取:
code复制https://api.bilibili.com/x/web-interface/archive/stat?aid={aid}
请求示例:
python复制def get_video_stats(aid):
api_url = f"https://api.bilibili.com/x/web-interface/archive/stat?aid={aid}"
try:
resp = session.get(api_url)
data = resp.json()
if data['code'] == 0:
return data['data']
else:
print(f"获取统计信息失败: {data['message']}")
return None
except Exception as e:
print(f"请求统计接口异常: {str(e)}")
return None
返回的数据结构示例:
json复制{
"aid": 123456789,
"bvid": "BV1xx411x7xx",
"view": 152368, // 播放量
"danmaku": 2345, // 弹幕数
"reply": 876, // 评论数
"favorite": 5432, // 收藏数
"coin": 1234, // 投币数
"share": 567, // 分享数
"like": 9876, // 点赞数
"dislike": 0 // 点踩数(已弃用)
}
4. 批量处理与数据存储
4.1 输入输出设计
推荐使用CSV或Excel作为输入输出格式,便于非技术人员使用。输入文件建议格式:
code复制BV1xx411x7xx
https://www.bilibili.com/video/BV1xx411x7yy
123456789
对应的处理逻辑:
python复制def process_input_file(filename):
vids = []
with open(filename, 'r') as f:
for line in f:
line = line.strip()
if not line:
continue
# 处理BV号
if line.startswith('BV'):
vids.append(line)
# 处理URL
elif line.startswith('http'):
match = re.search(r'video/(BV\w+)', line)
if match:
vids.append(match.group(1))
# 处理AV号
elif line.isdigit():
vids.append(f"av{line}")
return vids
4.2 数据存储优化
使用openpyxl存储数据时,建议:
- 为不同数据类型设置合适的列宽
- 添加数据验证和格式设置
- 实现错误重试机制
示例代码:
python复制def save_to_excel(data, filename):
wb = openpyxl.Workbook()
ws = wb.active
ws.title = "视频数据"
# 设置列宽
column_widths = {
'A': 50, # 标题
'B': 20, # BV号
'C': 15, # 播放量
# ...其他列设置
}
for col, width in column_widths.items():
ws.column_dimensions[col].width = width
# 写入表头
headers = ["标题", "BV号", "播放量", "弹幕数", "点赞数", "投币数",
"收藏数", "转发数", "UP主", "UID", "发布时间", "时长"]
ws.append(headers)
# 写入数据
for item in data:
row = [
item['title'],
item['bvid'],
item['stats']['view'],
# ...其他字段
]
ws.append(row)
# 保存文件
wb.save(filename)
5. 高级功能扩展
5.1 增量爬取机制
实现增量爬取需要:
- 建立本地数据库记录已爬取视频
- 每次运行前检查更新状态
- 只抓取新增或变更的内容
SQLite实现示例:
python复制import sqlite3
def init_db():
conn = sqlite3.connect('bilibili.db')
c = conn.cursor()
c.execute('''CREATE TABLE IF NOT EXISTS videos
(bvid TEXT PRIMARY KEY,
last_update TIMESTAMP)''')
conn.commit()
return conn
def check_update_status(conn, bvid):
c = conn.cursor()
c.execute("SELECT last_update FROM videos WHERE bvid=?", (bvid,))
result = c.fetchone()
return result[0] if result else None
5.2 分布式爬虫架构
当需要爬取大量数据时,可以考虑:
- 使用Redis作为任务队列
- 部署多个爬虫worker
- 实现心跳检测和故障转移
核心架构:
code复制任务生产者 -> Redis队列 -> 爬虫Worker -> 结果存储
^ |
|____________|
状态监控
实现要点:
python复制import redis
from rq import Queue
# 任务分发
def enqueue_tasks(vid_list):
r = redis.Redis()
q = Queue(connection=r)
for vid in vid_list:
q.enqueue('crawl_task', vid)
# Worker端
def crawl_task(vid):
try:
data = get_video_info(vid)
if data:
save_to_db(data)
return True
except Exception as e:
log_error(e)
return False
6. 合规使用建议
-
严格遵守robots.txt规定
- B站在robots.txt中明确禁止爬取的部分内容要规避
- 建议爬取间隔不低于3秒/次
-
数据使用限制
- 不得用于商业牟利
- 禁止公开原始数据
- 分析结果需脱敏处理
-
性能优化建议
- 使用本地缓存减少重复请求
- 设置合理的超时时间(建议10-15秒)
- 实现断点续爬功能
-
法律风险提示
- 大量爬取可能违反《反不正当竞争法》
- 用户隐私数据(如评论区个人信息)绝对禁止爬取
- 建议在凌晨等低峰期执行爬取任务
我在实际开发中发现,最稳定的方案是每天定时爬取少量数据(如每个UP主最新20个视频),长期积累的效果比一次性大规模爬取更好。对于数据分析需求,建议优先使用B站官方开放平台提供的数据接口,只有当接口无法满足需求时再考虑网页爬取方案。
