引言
B站(Bilibili)作为中国领先的年轻人文化社区和视频平台,其番剧区一直是动漫爱好者聚集的重要场所。对于内容创作者、版权方以及市场分析师而言,了解B站番剧的播放量趋势具有重要价值。本文将详细介绍如何使用Python爬虫技术获取B站番剧数据,并进行播放量趋势分析。
一、技术准备
在开始之前,我们需要准备以下工具和库:
● Python 3.7+
● Requests库:用于发送HTTP请求
● BeautifulSoup4:用于解析HTML
● Selenium:用于处理动态加载内容
● Pandas:用于数据处理
● Matplotlib/Seaborn:用于数据可视化
● Pyecharts:用于交互式可视化
此外,还需要下载对应浏览器的WebDriver,如ChromeDriver。
二、B站番剧页面分析
B站番剧主要有两种页面:
- 番剧索引页:https://www.bilibili.com/anime/index/
- 单个番剧详情页:https://www.bilibili.com/bangumi/play/ss{season_id}
我们的爬取策略是: - 从索引页获取所有番剧的season_id
- 对每个番剧详情页进行访问,获取播放量等数据
三、爬虫实现
3.1 获取番剧列表
```import requests
from bs4 import BeautifulSoup
import re
import time
import random
import pandas as pd
代理信息
proxyHost = "www.16yun.cn"
proxyPort = "5445"
proxyUser = "16QMSOML"
proxyPass = "280651"
proxies = {
"http": f"http://{proxyUser}:{proxyPass}@{proxyHost}:{proxyPort}",
"https": f"http://{proxyUser}:{proxyPass}@{proxyHost}:{proxyPort}"
}
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
'Referer': 'https://www.bilibili.com/'
}
def get_anime_list(page_num=5):
"""获取番剧列表"""
base_url = "https://www.bilibili.com/anime/index/#season_version=-1&area=-1&is_finish=-1©right=-1&season_status=-1&season_month=-1&year=-1&style_id=-1&order=3&st=1&sort=0&page={}"
anime_list = []
for page in range(1, page_num+1):
url = base_url.format(page)
try:
response = requests.get(url, headers=headers, proxies=proxies)
soup = BeautifulSoup(response.text, 'html.parser')
items = soup.find_all('li', class_='bangumi-item')
for item in items:
try:
title = item.find('p', class_='title').text.strip()
link = item.find('a')['href']
season_id = re.search(r'ss(\d+)', link).group(1)
anime_list.append({
'title': title,
'season_id': season_id,
'link': link
})
except Exception as e:
print(f"解析单个番剧出错: {e}")
continue
print(f"第{page}页番剧列表获取完成")
time.sleep(random.uniform(1, 3))
except Exception as e:
print(f"获取第{page}页出错: {e}")
continue
return anime_list
3.2 使用Selenium获取动态加载数据
由于B站很多数据是动态加载的,我们需要使用Selenium来模拟浏览器行为:
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
def setup_selenium():
"""配置Selenium"""
chrome_options = Options()
chrome_options.add_argument('--headless')
chrome_options.add_argument('--disable-gpu')
chrome_options.add_argument('--no-sandbox')
driver = webdriver.Chrome(options=chrome_options)
driver.implicitly_wait(10)
return driver
def get_anime_details(driver, season_id):
"""获取番剧详情"""
url = f"https://www.bilibili.com/bangumi/play/ss{season_id}"
driver.get(url)
try:
# 等待页面加载完成
WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.CLASS_NAME, 'media-info'))
# 获取播放量
play_count = driver.find_element(By.XPATH, '//span[@class="info-count-item"][1]/span').text
# 获取追番数
follow_count = driver.find_element(By.XPATH, '//span[@class="info-count-item"][2]/span').text
# 获取评分
score = driver.find_element(By.CLASS_NAME, 'score').text
# 获取弹幕总数
danmaku_count = driver.find_element(By.XPATH, '//span[@class="info-count-item"][3]/span').text
return {
'play_count': play_count,
'follow_count': follow_count,
'score': score,
'danmaku_count': danmaku_count
}
except Exception as e:
print(f"获取番剧{season_id}详情出错: {e}")
return None
finally:
time.sleep(random.uniform(2, 5))
3.3 数据采集主流程
```def main_crawler():
# 获取番剧列表
anime_list = get_anime_list(page_num=3) # 演示只爬取3页
# 初始化Selenium
driver = setup_selenium()
# 存储所有番剧数据
all_anime_data = []
for anime in anime_list:
details = get_anime_details(driver, anime['season_id'])
if details:
anime.update(details)
all_anime_data.append(anime)
print(f"已获取番剧数据: {anime['title']}")
# 关闭浏览器
driver.quit()
# 保存数据到CSV
df = pd.DataFrame(all_anime_data)
df.to_csv('bilibili_anime_data.csv', index=False, encoding='utf_8_sig')
return df
# 执行爬虫
anime_data = main_crawler()
四、数据清洗与处理
获取的原始数据需要进行清洗和处理:
```def clean_data(df):
# 转换播放量为数值
df['play_count'] = df['play_count'].apply(lambda x: float(x[:-1])*10000 if '万' in x else float(x))
# 转换追番数为数值
df['follow_count'] = df['follow_count'].apply(lambda x: float(x[:-1])*10000 if '万' in x else float(x))
# 转换弹幕数为数值
df['danmaku_count'] = df['danmaku_count'].apply(lambda x: float(x[:-1])*10000 if '万' in x else float(x))
# 处理评分为数值
df['score'] = pd.to_numeric(df['score'], errors='coerce')
# 添加播放/追番比指标
df['play_follow_ratio'] = df['play_count'] / df['follow_count']
return df
# 清洗数据
cleaned_data = clean_data(anime_data)
五、数据分析与可视化
5.1 基础统计分析
```# 基本统计信息
print(cleaned_data.describe())
# 播放量Top10番剧
top10_play = cleaned_data.sort_values('play_count', ascending=False).head(10)
print(top10_play[['title', 'play_count']])
5.2 使用Matplotlib可视化
import matplotlib.pyplot as plt
import seaborn as sns
plt.style.use('ggplot')
# 播放量分布
plt.figure(figsize=(12, 6))
sns.histplot(cleaned_data['play_count']/10000, bins=30, kde=True)
plt.title('B站番剧播放量分布(万)')
plt.xlabel('播放量(万)')
plt.ylabel('数量')
plt.show()
# 播放量与追番数关系
plt.figure(figsize=(10, 8))
sns.scatterplot(x='follow_count', y='play_count', hue='score',
size='danmaku_count', data=cleaned_data)
plt.title('B站番剧播放量与追番数关系')
plt.xlabel('追番数')
plt.ylabel('播放量')
plt.show()
六、播放量趋势分析
6.1 时间序列分析
要分析播放量趋势,我们需要获取番剧的历史播放数据。B站没有直接提供这个接口,但我们可以通过以下方法间接获取:
- 从番剧的每集播放量变化推断总体趋势
- 使用第三方API或B站开放平台接口(需要申请权限)
这里我们模拟一个时间序列分析的示例:
```import numpy as np
from datetime import datetime, timedelta
模拟生成时间序列数据
def generate_time_series(anime_id):
dates = pd.date_range(end=datetime.today(), periods=30).tolist()
base = np.random.randint(10000, 50000)
dailyplay = [base + np.random.randint(-2000, 5000) for in range(30)]
cumulative_play = np.cumsum(daily_play)
return pd.DataFrame({
'date': dates,
'daily_play': daily_play,
'cumulative_play': cumulative_play,
'anime_id': anime_id
})
为每个番剧生成时间序列数据
time_series_data = pd.concat(
[generate_time_series(anime['seasonid']) for , anime in cleaned_data.head(5).iterrows()]
)
可视化时间序列
plt.figure(figsize=(14, 8))
for anime_id, group in time_series_data.groupby('anime_id'):
plt.plot(group['date'], group['cumulative_play'], label=anime_id)
plt.title('番剧累计播放量趋势')
plt.xlabel('日期')
plt.ylabel('累计播放量')
plt.legend()
plt.grid(True)
plt.show()
6.2 趋势预测
我们可以使用Facebook的Prophet库进行简单的趋势预测:
from prophet import Prophet
def forecast_play_trend(df):
# 准备数据
df = df.rename(columns={'date': 'ds', 'cumulative_play': 'y'})
# 初始化模型
model = Prophet(
growth='linear',
seasonality_mode='multiplicative',
daily_seasonality=False,
weekly_seasonality=True,
yearly_seasonality=False
)
# 拟合模型
model.fit(df)
# 创建未来日期
future = model.make_future_dataframe(periods=7)
# 预测
forecast = model.predict(future)
return model, forecast
对第一个番剧进行预测
sample_anime = time_series_data[time_series_data['anime_id'] == cleaned_data.iloc[0]['season_id']]
model, forecast = forecast_play_trend(sample_anime)
绘制预测结果
fig = model.plot(forecast)
plt.title('番剧播放量趋势预测')
plt.xlabel('日期')
plt.ylabel('累计播放量')
plt.show()
```
七、结论与建议
通过以上分析,我们可以得出以下结论:
- 播放量分布:B站番剧播放量呈现明显的长尾分布,少数头部番剧占据了大部分播放量。
- 关键指标关系:
○ 播放量与追番数存在强正相关关系
○ 高评分番剧通常能保持稳定的播放增长
○ 弹幕数与播放量的比值可以反映番剧的互动活跃度 - 趋势分析:
○ 新上架番剧通常在前两周有最大的播放增长
○ 完结番剧的播放量增长会放缓但不会停止