1. 项目背景与核心价值
B站作为国内最大的年轻人文化社区,其视频数据蕴含着丰富的用户行为和市场趋势信息。对于内容创作者、市场分析师或学术研究者而言,获取B站热门视频的UP主粉丝数、标签分类等数据,能够帮助快速把握平台内容生态的变化规律。
传统手动收集这些数据效率极低,一个热门分区可能包含上千个视频,每个视频需要记录标题、播放量、UP主信息等十余项字段。而通过Python爬虫技术,我们可以在几分钟内自动化完成这些工作,并将结果导出为结构化的Excel或CSV文件。
这个项目的独特价值在于:
- 完整覆盖视频基础信息、UP主数据和标签体系
- 绕过B站反爬机制实现稳定采集
- 提供一键导出功能,无需二次处理
- 支持定时自动采集,建立长期数据监控
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术方案设计
2.1 整体架构设计
爬虫系统采用分层设计模式,主要包含四个模块:
- 请求管理层:处理HTTP请求、代理轮换和异常重试
- 数据解析层:提取HTML/JSON中的目标字段
- 存储管理层:将清洗后的数据持久化存储
- 调度控制层:管理爬取频率和任务队列
python复制class BilibiliSpider:
def __init__(self):
self.session = requests.Session()
self.headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
'Referer': 'https://www.bilibili.com/'
}
def get_video_info(self, bvid):
# 实现视频数据获取逻辑
pass
def get_up_info(self, mid):
# 实现UP主数据获取逻辑
pass
2.2 反爬应对策略
B站的反爬机制主要包括:
- 请求频率限制(每分钟约30次)
- 关键接口需要携带cookies
- 部分数据通过JavaScript动态加载
我们的解决方案:
- 使用代理IP池(建议使用付费API服务)
- 随机化请求间隔(0.5-2秒)
- 模拟完整浏览器行为(携带所有必要headers)
- 对动态加载内容使用Selenium辅助采集
重要提示:请将爬取频率控制在合理范围,避免对B站服务器造成压力。建议单个IP的请求间隔不低于1秒。
3. 核心功能实现
3.1 视频数据采集
B站视频页的关键数据接口:
- 基础信息:
https://api.bilibili.com/x/web-interface/view?bvid={bvid} - 标签信息:
https://api.bilibili.com/x/tag/archive/tags?bvid={bvid} - 实时数据:
https://api.bilibili.com/x/web-interface/archive/stat?bvid={bvid}
典型响应数据结构:
json复制{
"code": 0,
"data": {
"bvid": "BV1xx411c7mu",
"title": "【4K】XXX",
"owner": {
"mid": 123456,
"name": "UP主名称"
},
"stat": {
"view": 152341,
"danmaku": 2341,
"reply": 543,
"favorite": 3214,
"coin": 654,
"share": 432,
"like": 5432
}
}
}
3.2 UP主信息获取
通过UP主MID获取详细信息的接口:
https://api.bilibili.com/x/space/acc/info?mid={mid}
关键字段包括:
- 粉丝数(follower)
- 总播放量(archive.view)
- 获赞数(likes)
- 会员等级(level)
3.3 数据存储实现
使用pandas进行数据整理和导出:
python复制import pandas as pd
def save_to_excel(data_list, filename):
df = pd.DataFrame(data_list)
writer = pd.ExcelWriter(filename, engine='xlsxwriter')
df.to_excel(writer, index=False)
writer.close()
典型数据结构示例:
| bvid | title | up_name | up_mid | up_fans | view | danmaku | tags |
|---|---|---|---|---|---|---|---|
| BV1... | 视频标题 | UP主A | 123456 | 54231 | 152341 | 2341 | 科技,数码 |
4. 完整代码实现
4.1 基础爬取功能
python复制import requests
import json
import time
import random
from fake_useragent import UserAgent
class BiliSpider:
def __init__(self):
self.ua = UserAgent()
self.session = requests.Session()
self.base_headers = {
'Referer': 'https://www.bilibili.com/',
'Origin': 'https://www.bilibili.com'
}
def get_json(self, url, params=None):
headers = dict(self.base_headers)
headers['User-Agent'] = self.ua.random
try:
resp = self.session.get(url, headers=headers, params=params, timeout=10)
if resp.status_code == 200:
return resp.json()
return None
except Exception as e:
print(f"请求失败: {e}")
return None
def get_video_info(self, bvid):
api_url = "https://api.bilibili.com/x/web-interface/view"
params = {'bvid': bvid}
return self.get_json(api_url, params)
def get_up_info(self, mid):
api_url = "https://api.bilibili.com/x/space/acc/info"
params = {'mid': mid}
return self.get_json(api_url, params)
def get_video_tags(self, bvid):
api_url = "https://api.bilibili.com/x/tag/archive/tags"
params = {'bvid': bvid}
return self.get_json(api_url, params)
4.2 数据清洗与导出
python复制import pandas as pd
from datetime import datetime
class DataProcessor:
@staticmethod
def process_video_data(raw_data):
if not raw_data or raw_data.get('code') != 0:
return None
data = raw_data['data']
video_info = {
'bvid': data['bvid'],
'title': data['title'],
'desc': data['desc'],
'pubdate': datetime.fromtimestamp(data['pubdate']).strftime('%Y-%m-%d %H:%M'),
'up_mid': data['owner']['mid'],
'up_name': data['owner']['name'],
'view': data['stat']['view'],
'danmaku': data['stat']['danmaku'],
'reply': data['stat']['reply'],
'favorite': data['stat']['favorite'],
'coin': data['stat']['coin'],
'share': data['stat']['share'],
'like': data['stat']['like']
}
return video_info
@staticmethod
def process_up_data(raw_data):
if not raw_data or raw_data.get('code') != 0:
return None
data = raw_data['data']
up_info = {
'mid': data['mid'],
'name': data['name'],
'sex': data['sex'],
'face': data['face'],
'sign': data['sign'],
'level': data['level'],
'follower': data['follower']
}
return up_info
@staticmethod
def process_tags_data(raw_data):
if not raw_data or raw_data.get('code') != 0:
return []
return [tag['tag_name'] for tag in raw_data['data']]
@staticmethod
def save_to_excel(data_list, filename):
df = pd.DataFrame(data_list)
df.to_excel(filename, index=False)
5. 实战操作指南
5.1 环境准备
需要安装的Python库:
bash复制pip install requests pandas xlsxwriter fake-useragent
5.2 获取热门视频列表
B站热门视频接口(需登录获取cookies):
https://api.bilibili.com/x/web-interface/popular
示例代码:
python复制def get_popular_videos(spider, count=20):
popular_url = "https://api.bilibili.com/x/web-interface/popular"
result = spider.get_json(popular_url)
if not result or result.get('code') != 0:
return []
videos = result['data'][:count]
return [v['bvid'] for v in videos]
5.3 完整采集流程
python复制def main():
spider = BiliSpider()
processor = DataProcessor()
# 获取热门视频BV号列表
bvid_list = get_popular_videos(spider, 10)
all_data = []
for bvid in bvid_list:
# 随机延迟1-3秒
time.sleep(random.uniform(1, 3))
# 获取视频基础信息
video_data = spider.get_video_info(bvid)
if not video_data:
continue
processed_video = processor.process_video_data(video_data)
if not processed_video:
continue
# 获取UP主信息
up_data = spider.get_up_info(processed_video['up_mid'])
if up_data:
processed_up = processor.process_up_data(up_data)
processed_video.update(processed_up)
# 获取视频标签
tags_data = spider.get_video_tags(bvid)
if tags_data:
processed_video['tags'] = ','.join(processor.process_tags_data(tags_data))
all_data.append(processed_video)
# 保存结果
if all_data:
processor.save_to_excel(all_data, 'bilibili_hot_videos.xlsx')
print(f"成功保存{len(all_data)}条数据")
else:
print("未获取到有效数据")
if __name__ == '__main__':
main()
6. 高级技巧与优化
6.1 并发采集实现
使用线程池提高采集效率:
python复制from concurrent.futures import ThreadPoolExecutor
def fetch_video_data(bvid):
spider = BiliSpider()
processor = DataProcessor()
video_data = spider.get_video_info(bvid)
if not video_data:
return None
processed_video = processor.process_video_data(video_data)
if not processed_video:
return None
# 获取附加信息...
return processed_video
def concurrent_main():
spider = BiliSpider()
bvid_list = get_popular_videos(spider, 50)
with ThreadPoolExecutor(max_workers=5) as executor:
results = list(executor.map(fetch_video_data, bvid_list))
valid_data = [r for r in results if r]
if valid_data:
DataProcessor.save_to_excel(valid_data, 'bilibili_concurrent.xlsx')
6.2 数据可视化分析
使用matplotlib进行基础数据分析:
python复制import matplotlib.pyplot as plt
def analyze_data(filename):
df = pd.read_excel(filename)
# 播放量TOP10视频
top_views = df.sort_values('view', ascending=False).head(10)
plt.figure(figsize=(12, 6))
plt.barh(top_views['title'], top_views['view'])
plt.xlabel('播放量')
plt.title('B站热门视频播放量TOP10')
plt.tight_layout()
plt.savefig('top_views.png')
# UP主粉丝分布
plt.figure(figsize=(10, 6))
plt.scatter(df['up_follower'], df['view'], alpha=0.6)
plt.xscale('log')
plt.yscale('log')
plt.xlabel('UP主粉丝数(log)')
plt.ylabel('视频播放量(log)')
plt.title('UP主粉丝数与视频播放量关系')
plt.savefig('follower_view.png')
7. 常见问题与解决方案
7.1 请求被限制(返回412状态码)
可能原因:
- 请求频率过高
- cookies失效
- User-Agent被识别
解决方案:
- 降低请求频率(增加随机延迟)
- 更新cookies(手动登录获取新cookies)
- 使用更真实的浏览器指纹(通过fake-useragent库)
7.2 数据字段缺失
常见于:
- 新账号没有某些统计字段
- 部分接口返回数据结构变化
处理方法:
python复制def safe_get(data, keys, default=None):
try:
for key in keys.split('.'):
data = data[key]
return data
except (KeyError, TypeError):
return default
# 使用示例
view_count = safe_get(video_data, 'data.stat.view', 0)
7.3 验证码触发
当出现验证码时:
- 立即停止爬取30分钟以上
- 更换IP地址
- 检查请求headers是否完整
- 考虑使用自动化工具处理验证码(如第三方打码平台)
8. 项目扩展方向
8.1 定时自动化采集
使用APScheduler实现定时任务:
python复制from apscheduler.schedulers.blocking import BlockingScheduler
def scheduled_job():
print("开始执行定时采集任务...")
main()
scheduler = BlockingScheduler()
scheduler.add_job(scheduled_job, 'interval', hours=6)
scheduler.start()
8.2 数据库存储
改用MySQL持久化存储:
python复制import pymysql
from sqlalchemy import create_engine
def save_to_mysql(data_list):
engine = create_engine('mysql+pymysql://user:password@localhost/bilibili')
df = pd.DataFrame(data_list)
df.to_sql('video_data', con=engine, if_exists='append', index=False)
8.3 多维度数据分析
扩展分析维度:
- 标签关联分析(哪些标签经常同时出现)
- UP主内容领域分布
- 发布时间与播放量关系
- 弹幕情感分析
这个爬虫项目在实际运营中,我发现最关键的三个经验是:1)保持合理的请求间隔比用大量代理IP更重要;2)B站的接口结构相对稳定,但需要定期检查数据字段;3)对于学术研究用途,最好在导出数据中包含采集时间戳,方便后续做时间序列分析。
