爬取国产动漫视频弹幕数据分析

发布时间 2023-06-05 00:13:19作者: 大数据黄坤

                                                                                        python爬虫--爬取国产动漫视频弹幕数据分析

 

一.选题背景

 

随着互联网的发展,视频弹幕网站越来越流行,弹幕的信息通过视频在用户间分享流转,使弹幕具有了传播的特点。弹幕的信息包含了用户的主观情感,用户能在文字中加入情感色彩的词藻,使弹幕具有了描述人类主观喜好、赞赏、感觉等情感的特点。弹幕在传播过程中可能会在某个时间节点或者某个用户参与后,其热议程度呈井喷式增长。

 

二.主题式网络爬虫设计方案

 

  1. 该网络爬虫为Bilibili弹幕数据爬虫
  2. 旨在爬取Bilibili的弹幕文本数据,以及发布时间,对文本进行情感极性分析,对弹幕发布日期进行统计,总结相关规律,为Up主及相关运营工作人员提供参考。
  3. 设计方案:

弹幕地址:https://api.bilibili.com/x/v1/dm/list.so?oid=7633504

获取弹幕

import requests
import re
import numpy as np
import pandas as pd
from bs4 import BeautifulSoup
import datetime
import matplotlib.pyplot as plt
from pyecharts.charts import Pie
import webbrowser
from wordcloud import WordCloud
import jieba
plt.style.use('seaborn-dark')
barrage_list = []
def get_barrage():
url = 'https://api.bilibili.com/x/v1/dm/list.so?oid=7633504'
res = requests.get(url)
res.encoding = 'utf-8'
res_xml = res.content.decode('utf-8')
barrage_all = []
pattern = re.compile('<d.*?>(.*?)</d>')
global barrage_list
barrage_list = pattern.findall(res_xml)
html = res.text
soup = BeautifulSoup(html, 'html.parser')
for target in soup.find_all('d'):
value = target.get('p').split(',')
barrage_all.append(
{'时间': value[0], '弹幕模式': value[1], '弹幕字号': value[2], '弹幕颜色': value[3], '时间戳': value[4],
'弹幕池': value[5], '发送者ID': value[6], '历史弹幕': value[7]})
return barrage_all

 

 

数据的处理

def data_processing(barrage_df):
barrage_time = (barrage_df['时间'].astype(float)).astype(int)
time_list = []
for a_time in barrage_time:
m, s = divmod(a_time, 60)
h, m = divmod(m, 60)
a_time = str(h) + ':' + str(m) + ':' + str(s)
time_list.append(a_time)
barrage_df['时间'] = time_list
barrage_type = barrage_df['弹幕模式'].astype(int)
areas = [0, 3, 4, 5, 6, 7, 8]
pattern = ['滚动弹幕', '底端弹幕', '顶端弹幕', '逆向弹幕', '精准定位', '高级弹幕']
barrage_df['弹幕模式'] = pd.cut(barrage_type, areas, right=True, labels=pattern)
barrage_tool = barrage_df['弹幕池'].astype(int)
areas = [-1, 0, 1, 2]
pattern = ['普通池', '字幕池', '特殊池']
barrage_df['弹幕池'] = pd.cut(barrage_tool, areas, right=True, labels=pattern)
barrage_timestamp = barrage_df['时间戳'].astype(int)
timestamp_list = []
for timestamp in barrage_timestamp:
timestamp_list.append(datetime.datetime.fromtimestamp(timestamp))
barrage_df['时间戳'] = timestamp_list
barrage_df.to_csv('狐妖小红娘王权总篇集弹幕.csv', encoding='utf_8_sig')
 
 
 
对时间的分析所构建的图
time_list = []
for a_time in barrage_time:
h, m, s = a_time.strip().split(':')
temp = int(h) * 60 + int(m) + int(s) / 60
time_list.append(temp)
time_df = pd.DataFrame(time_list)
time_df.plot(kind='kde', label='弹幕密度')
plt.title('王权总篇弹幕密度')
plt.xlabel('时间/分')
plt.ylabel('百分比')
plt.legend(['弹幕密度'])
plt.xlim(0, )
plt.show()

 由上图可知,在视频一开始时便有比较多的弹幕,在视频的第80分钟这一时段便有本集最多的弹幕,这一分钟大约有本集1.4%的弹幕铺天盖地袭来,占据你的屏幕

 

 

对弹幕颜色的图

barrage_color = fox_demon['弹幕颜色'].value_counts()
barrage_color = barrage_color.head(7)
favorite_color = []
for a_color in barrage_color.index:
temp = hex(a_color)
temp = '#' + temp[2:].upper()
while len(temp) < 7:
temp = temp[0] + '0' + temp[1:]
favorite_color.append(temp)
fig, ax = plt.subplots()
plt.bar([1, 2, 3, 4, 5, 6, 7], barrage_color.values, color=favorite_color)
plt.title('王权总篇弹幕颜色使用数量前七名')
plt.xlabel('排名')
plt.ylabel('使用该颜色的弹幕数量')
plt.show()

 由上图可知,绝大多数用户使用白色的弹幕,约占所有用户的四分之三,红色、黄色和绿色也有使用

 

 

 

对时间直方图,ID的图,ID分析饼图
barrage_date = fox_demon['时间戳'].dt.hour
bins = np.arange(0, 25, 1) - 0.5
fig, ax = plt.subplots()
barrage_date.hist(bins=bins, grid=False, align='mid')
plt.title('王权总篇各个时段弹幕数量')
plt.xlabel('时间段')
plt.ylabel('弹幕数量')
plt.xticks(np.arange(0, 24, 1))
plt.show()
barrage_userId = fox_demon['发送者ID'].value_counts()
barrage_userId = barrage_userId.head(10)
fig, ax = plt.subplots()
plt.bar(barrage_userId.index, barrage_userId.values, color=('r', 'g', 'b', 'c', 'm', 'r', 'g', 'b', 'c', 'm'))
plt.title('王权总篇发送弹幕前十的用户ID情况')
plt.xlabel('用户B站ID')
plt.ylabel('弹幕数量')
plt.xticks(size='small', rotation=50, fontsize=15)
plt.show()
barrage_comment = fox_demon['弹幕内容']
comment_len = []
comment_cloud = ''
for comment in barrage_comment:
comment_len.append(str(len(comment)) + '个字弹幕')
comment_cloud = comment_cloud + comment + '\n'
comment_len_sr = pd.Series(comment_len)
comment_len_count = comment_len_sr.value_counts()
fig, ax = plt.subplots()
plt.pie(comment_len_count.values, labels=comment_len_count.index, autopct='%0.2f%%')
plt.title('王权总篇弹幕长度')
plt.show()

 

 由上图可知,用户在一天中的21点至22点发送的弹幕数量最多,在午后至午夜这一时间段发送的弹幕比较多,可能是都起不来床也喜欢熬夜

 

由上图可知,发送弹幕数量前十的用户基本都发送了30条以上的弹幕,发送弹幕最多的用户甚至发送了70条弹幕,这应该就是真爱粉了

 

由上面两张图可知,绝大多数用户发送2-6个字的弹幕,其中4个字弹幕最多,约占所有弹幕的15.5%,也有用户发送了48个字超长的弹幕

 

 

词云,句云

text = ' '.join(jieba.cut(comment_cloud))
color_mask = plt.imread('susu.jpg')
cloud = WordCloud(
font_path=' C:\\Windows\\Fonts\\simsun.ttc',
background_color='white',
mask=color_mask,
max_words=2000,
max_font_size=1000
)
# 绘制“句”云
cloud_sentence = WordCloud(
font_path=' C:\\Windows\\Fonts\\simsun.ttc',
background_color='white',
mask=color_mask,
max_words=500,
max_font_size=1000
)
wCloud = cloud.generate(text)
wCloud_sentence = cloud_sentence.generate(comment_cloud)
wCloud.to_file('cloud.png')
wCloud_sentence.to_file('cloud_sentence.png')
fig, ax = plt.subplots()
plt.imshow(wCloud, interpolation='bilinear')
plt.title('王权总篇弹幕词云')
plt.axis('off')
plt.show()
fig, ax = plt.subplots()
plt.imshow(wCloud_sentence, interpolation='bilinear')
plt.title('王权总篇弹幕句云')
plt.axis('off')
plt.show()
if __name__ == '__main__':
barrage_df = pd.DataFrame(get_barrage())
barrage_df['弹幕内容'] = barrage_list
data_processing(barrage_df)
barrage_analyse_plt()
 

 

三.主题页面的结构特征分析

 

Bilibili的弹幕数据虽然出现在视频上的。实际上在网页中,弹幕是被隐藏在源代码中,以XML的数据格式进行加载。且弹幕数据的文档链接构成为https://comment.bilibili.com/cid.xml,即以一个固定的url地址+视频的cid+.xml组成。只要找到你想要的视频cid,替换这个url就可以爬取所有弹幕。

总代码:

import requests
import re
import numpy as np
import pandas as pd
from bs4 import BeautifulSoup
import datetime
import matplotlib.pyplot as plt
from pyecharts.charts import Pie
import webbrowser
from wordcloud import WordCloud
import jieba
plt.style.use('seaborn-dark')
barrage_list = []
def get_barrage():
url = 'https://api.bilibili.com/x/v1/dm/list.so?oid=7633504'
res = requests.get(url)
res.encoding = 'utf-8'
res_xml = res.content.decode('utf-8')
barrage_all = []
pattern = re.compile('<d.*?>(.*?)</d>')
global barrage_list
barrage_list = pattern.findall(res_xml)
html = res.text
soup = BeautifulSoup(html, 'html.parser')
for target in soup.find_all('d'):
value = target.get('p').split(',')
barrage_all.append(
{'时间': value[0], '弹幕模式': value[1], '弹幕字号': value[2], '弹幕颜色': value[3], '时间戳': value[4],
'弹幕池': value[5], '发送者ID': value[6], '历史弹幕': value[7]})
return barrage_all
def data_processing(barrage_df):
barrage_time = (barrage_df['时间'].astype(float)).astype(int)
time_list = []
for a_time in barrage_time:
m, s = divmod(a_time, 60)
h, m = divmod(m, 60)
a_time = str(h) + ':' + str(m) + ':' + str(s)
time_list.append(a_time)
barrage_df['时间'] = time_list
barrage_type = barrage_df['弹幕模式'].astype(int)
areas = [0, 3, 4, 5, 6, 7, 8]
pattern = ['滚动弹幕', '底端弹幕', '顶端弹幕', '逆向弹幕', '精准定位', '高级弹幕']
barrage_df['弹幕模式'] = pd.cut(barrage_type, areas, right=True, labels=pattern)
barrage_tool = barrage_df['弹幕池'].astype(int)
areas = [-1, 0, 1, 2]
pattern = ['普通池', '字幕池', '特殊池']
barrage_df['弹幕池'] = pd.cut(barrage_tool, areas, right=True, labels=pattern)
barrage_timestamp = barrage_df['时间戳'].astype(int)
timestamp_list = []
for timestamp in barrage_timestamp:
timestamp_list.append(datetime.datetime.fromtimestamp(timestamp))
barrage_df['时间戳'] = timestamp_list
barrage_df.to_csv('狐妖小红娘王权总篇集弹幕.csv', encoding='utf_8_sig')
def barrage_analyse_plt():
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
fox_demon = pd.read_csv('狐妖小红娘王权总篇集弹幕.csv', parse_dates=['时间戳'])
barrage_time = fox_demon['时间']
time_list = []
for a_time in barrage_time:
h, m, s = a_time.strip().split(':')
temp = int(h) * 60 + int(m) + int(s) / 60
time_list.append(temp)
time_df = pd.DataFrame(time_list)
time_df.plot(kind='kde', label='弹幕密度')
plt.title('王权总篇弹幕密度')
plt.xlabel('时间/分')
plt.ylabel('百分比')
plt.legend(['弹幕密度'])
plt.xlim(0, )
plt.show()
barrage_color = fox_demon['弹幕颜色'].value_counts() # 统计用户发送的弹幕使用同一种颜色的数量
barrage_color = barrage_color.head(7)
favorite_color = []
for a_color in barrage_color.index:
temp = hex(a_color)
temp = '#' + temp[2:].upper()
while len(temp) < 7:
temp = temp[0] + '0' + temp[1:]
favorite_color.append(temp)
fig, ax = plt.subplots()
plt.bar([1, 2, 3, 4, 5, 6, 7], barrage_color.values, color=favorite_color)
plt.title('王权总篇弹幕颜色使用数量前七名')
plt.xlabel('排名')
plt.ylabel('使用该颜色的弹幕数量')
plt.show()
barrage_date = fox_demon['时间戳'].dt.hour
bins = np.arange(0, 25, 1) - 0.5
fig, ax = plt.subplots()
barrage_date.hist(bins=bins, grid=False, align='mid')
plt.title('王权总篇各个时段弹幕数量')
plt.xlabel('时间段')
plt.ylabel('弹幕数量')
plt.xticks(np.arange(0, 24, 1))
plt.show()
barrage_userId = fox_demon['发送者ID'].value_counts()
barrage_userId = barrage_userId.head(10)
fig, ax = plt.subplots()
plt.bar(barrage_userId.index, barrage_userId.values, color=('r', 'g', 'b', 'c', 'm', 'r', 'g', 'b', 'c', 'm'))
plt.title('王权总篇发送弹幕前十的用户ID情况')
plt.xlabel('用户B站ID')
plt.ylabel('弹幕数量')
plt.xticks(size='small', rotation=50, fontsize=15)
plt.show()
barrage_comment = fox_demon['弹幕内容']
comment_len = []
comment_cloud = ''
for comment in barrage_comment:
comment_len.append(str(len(comment)) + '个字弹幕')
comment_cloud = comment_cloud + comment + '\n'
comment_len_sr = pd.Series(comment_len)
comment_len_count = comment_len_sr.value_counts()
fig, ax = plt.subplots()
plt.pie(comment_len_count.values, labels=comment_len_count.index, autopct='%0.2f%%')
plt.title('王权总篇弹幕长度')
plt.show()
pie = Pie('狐妖小红娘弹幕内容分析', '弹幕长度', title_pos='left', width=1100, height=600)
pie.add(
"弹幕长度",
comment_len_count.index,
comment_len_count.values,
is_label_show=True,
is_more_utils=True,
legend_pos='right',
legend_orient='vertical'
)
pie.render('fox_demon.html')
webbrowser.open('fox_demon.html')
text = ' '.join(jieba.cut(comment_cloud))
color_mask = plt.imread('susu.jpg')
cloud = WordCloud(
font_path=' C:\\Windows\\Fonts\\simsun.ttc',
background_color='white',
mask=color_mask,
max_words=2000,
max_font_size=1000
)
# 绘制“句”云
cloud_sentence = WordCloud(
font_path=' C:\\Windows\\Fonts\\simsun.ttc',
background_color='white',
mask=color_mask,
max_words=500,
max_font_size=1000
)
wCloud = cloud.generate(text)
wCloud_sentence = cloud_sentence.generate(comment_cloud)
wCloud.to_file('cloud.png')
wCloud_sentence.to_file('cloud_sentence.png')
fig, ax = plt.subplots()
plt.imshow(wCloud, interpolation='bilinear')
plt.title('王权总篇弹幕词云')
plt.axis('off')
plt.show()
fig, ax = plt.subplots()
plt.imshow(wCloud_sentence, interpolation='bilinear')
plt.title('王权总篇弹幕句云')
plt.axis('off')
plt.show()
if __name__ == '__main__':
barrage_df = pd.DataFrame(get_barrage())
barrage_df['弹幕内容'] = barrage_list
data_processing(barrage_df)
barrage_analyse_plt()