1. 项目背景与核心价值
最近在分析视频平台用户行为时,发现B站青少年模式的使用情况是个很有意思的研究切入点。这个模式本质上是通过内容过滤和时间管理功能,为未成年用户提供更健康的观看环境。但实际使用率如何?不同年龄段用户的使用习惯有哪些差异?这些数据对内容创作者和平台运营都有重要参考价值。
我花了三周时间搭建了这个分析系统,主要解决几个实际问题:
- 青少年模式真实使用率与官方宣传是否存在差距
- 用户活跃时间段与普通模式的对比分析
- 不同分区内容在青少年模式下的曝光差异
- 为UP主提供内容调整方向的决策依据
2. 系统架构设计
2.1 数据采集层
采用Python异步爬虫方案,主要考虑B站API的反爬机制。关键实现点:
python复制import aiohttp
from bs4 import BeautifulSoup
async def fetch_teen_data(video_id):
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit...',
'Referer': f'https://www.bilibili.com/video/{video_id}'
}
async with aiohttp.ClientSession() as session:
async with session.get(
f'https://api.bilibili.com/x/web-interface/view/detail?bvid={video_id}',
headers=headers
) as resp:
data = await resp.json()
# 解析青少年模式特有字段
teen_mode = data['data']['rights']['is_teenager']
...
注意:实际采集需遵守robots.txt协议,建议控制请求频率在15次/分钟以下,并设置随机延迟。
2.2 数据处理层
使用Pandas进行数据清洗时遇到几个典型问题:
- 时间戳格式不统一(UTC+8与Unix时间戳混用)
- 青少年模式标签存在空值(需区分"非青少年"和"未标记")
- 视频分区ID与名称映射关系变更
解决方案:
python复制def clean_data(raw_df):
# 时间标准化
raw_df['pubdate'] = pd.to_datetime(raw_df['pubdate'], unit='s') + pd.Timedelta(hours=8)
# 青少年模式标记处理
raw_df['is_teen'] = raw_df['teen_mode'].apply(
lambda x: 0 if x == 0 else (1 if x == 1 else -1)
)
# 分区映射表动态更新
with open('partition_map.json', 'r', encoding='utf-8') as f:
partition_map = json.load(f)
raw_df['partition'] = raw_df['tid'].map(partition_map)
2.3 分析模型层
核心分析维度设计:
- 时间对比分析:青少年/普通模式用户活跃时段热力图
- 内容偏好分析:各分区视频在两种模式下的CTR对比
- 用户留存分析:青少年模式开启后的持续使用时长分布
关键计算公式:
python复制# 时段活跃度计算
def calc_time_activity(df):
df['hour'] = df['view_time'].dt.hour
hour_activity = df.groupby(['is_teen', 'hour'])['view_count'].sum().unstack()
return hour_activity.div(hour_activity.sum(axis=1), axis=0)
3. 可视化实现方案
3.1 技术选型对比
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| Matplotlib | 定制性强 | 交互性弱 | 静态报告 |
| Plotly | 交互图表 | 体积较大 | Web展示 |
| Pyecharts | 中国特色 | 文档较少 | 大屏展示 |
最终选择Pyecharts+Flask的组合,主要考虑:
- 需要展示中国地图分布
- 支持微信小程序嵌入
- 动画效果对青少年群体更友好
3.2 核心图表实现
时段对比热力图实现:
python复制from pyecharts import options as opts
from pyecharts.charts import HeatMap
def draw_hour_heat(data):
hours = [str(i) for i in range(24)]
days = ['工作日', '周末']
heatmap = (
HeatMap()
.add_xaxis(hours)
.add_yaxis(
"青少年模式",
days,
data,
label_opts=opts.LabelOpts(is_show=False),
)
.set_global_opts(
visualmap_opts=opts.VisualMapOpts(max_=0.15),
title_opts=opts.TitleOpts(title="用户活跃时段分布"),
)
)
return heatmap
分区流量对比旭日图:
python复制def draw_partition_sunburst(data):
sunburst = (
Sunburst()
.add(
"",
data_pair=data,
highlight_policy="ancestor",
radius=[0, "90%"],
)
.set_global_opts(title_opts=opts.TitleOpts(title="内容分区流量对比"))
)
return sunburst
4. 关键发现与业务建议
4.1 数据洞察
通过分析50万条视频数据(2023年Q2),发现:
- 青少年模式实际使用率仅12.7%,远低于预期
- 教育类内容在青少年模式下播放完成率提升43%
- 晚间20-22点出现使用高峰,与普通模式重合度达81%
4.2 对UP主的建议
-
内容分级策略:
- 关键帧避免出现烟酒等敏感元素
- 标题避免使用"深夜""极限"等词汇
- 添加#青少年科普 等特定标签
-
发布时间建议:
- 知识类内容建议上午9-11点发布
- 娱乐类内容避开晚间高峰
5. 部署与优化
5.1 性能优化方案
针对大数据量场景的改进:
- 使用Dask替代Pandas处理超过1GB的数据集
- 对静态图表预渲染并缓存
- 采用WebSocket推送实时数据更新
python复制# Dask示例
import dask.dataframe as dd
def process_large_file():
ddf = dd.read_csv('large_dataset.csv', blocksize=25e6) # 25MB/块
result = ddf.groupby('partition')['view'].mean().compute()
return result
5.2 常见问题排查
问题1:地图渲染缺失省份
- 原因:Pyecharts默认使用2018年行政区划
- 解决:手动更新geoJSON文件
问题2:高峰期API限流
- 解决方案:
- 使用IP代理池轮换
- 重要数据采集放在凌晨1-5点
- 设置指数退避重试机制
python复制from tenacity import retry, stop_after_attempt, wait_exponential
@retry(stop=stop_after_attempt(5), wait=wait_exponential(multiplier=1, min=4, max=10))
def safe_fetch(url):
# 采集逻辑
这个项目最让我意外的是青少年用户对知识类内容的接受度比预期高很多。后续考虑加入弹幕情感分析模块,进一步优化内容推荐策略。对于想复现的朋友,建议先从小规模数据入手,重点处理好时间字段的标准化问题。