python教程b站推荐(Python爬取B站弹幕Gephi梳理主线剧情)
python教程b站推荐(Python爬取B站弹幕Gephi梳理主线剧情)完整爬取代码如下不难看出,CID 则是对应着各个视频的 ID,接下来用正则提取即可。利用 Chrome 浏览器抓包可知,B站的弹幕文件以 XML 文档式进行储存,如下所示(共三千条实时弹幕)其 URL 为:http://comment.bilibili.com/183362119.xml数字183362119则代表该视频专属 ID ,通过改变数字即可得到相应的弹幕文件。打开第1集的视频,查看源码,如下图所示。
作者 | 皖渝
出品 | 凹凸数据(ID:alltodata)
头图 | CSDN 下载自东方IC
爬取介绍
利用 Chrome 浏览器抓包可知,B站的弹幕文件以 XML 文档式进行储存,如下所示(共三千条实时弹幕)
其 URL 为:http://comment.bilibili.com/183362119.xml
数字183362119则代表该视频专属 ID ,通过改变数字即可得到相应的弹幕文件。打开第1集的视频,查看源码,如下图所示。
不难看出,CID 则是对应着各个视频的 ID,接下来用正则提取即可。
完整爬取代码如下
import requests
import re
from bs4 import BeautifulSoup as BS
import os
path='C:/Users/dell/Desktop/柯南'
if os.path.exists(path)==False:
os.makedirs(path)
os.chdir(path)
def gethtml(url header):
r=requests.get(url headers=header)
r.encoding='utf-8'
return r.text
def crawl_comments(r_text):
txt1=gethtml(url header)
pat='"cid":(\d )'
chapter_total=re.findall(pat txt1)[1:-2]
count=1
for chapter in chapter_total:
url_base='http://comment.bilibili.com/{}.xml'.format(chapter)
txt2=gethtml(url_base header)
soup=BS(txt2 'lxml')
all_d=soup.find_all('d')
with open('{}.txt'.format(count) 'w' encoding='utf-8') as f:
for d in all_d:
f.write(d.get_text '\n')
print('第{}话弹幕写入完毕'.format(count))
count =1
if __name__=='__main__':
url='https://www.bilibili.com/bangumi/play/ep321808'
header={'user-agent':'Opera/12.80 (Windows NT 5.1; U; en) Presto/2.10.289 Version/12.02'}
r_text=gethtml(url header)
crawl_comments(r_text)
最终的全部弹幕文件都在桌面的"柯南"文件下
注:这里共爬取到980个弹幕文件。【B站的柯南自941集后就跳到994集(大会员才能观看的)。虽然目前更新到1032话,但并没有1032集内容,如下图所示】
弹幕可视化
1、主要人物讨论总次数分析
统计人数总次数
注:role.txt 是主要人物名文件(需考虑到弹幕一般不会对人物的全名进行称呼,多数使用的是昵称,否则可能与实际情况相差较大。)
import jieba
import os
import pandas as pd
os.chdir('C:/Users/dell/Desktop')
jieba.load_userdict('role.txt')
role=[ i.replace('\n' '') for i in open('role.txt' 'r' encoding='utf-8').readlines()]
txt_all=os.listdir('./柯南/')
txt_all.sort(key=lambda x:int(x.split('.')[0])) #按集数排序
count=1
def role_count:
df = pd.DataFrame
for chapter in txt_all:
names={}
data=
with open('./柯南/{}'.format(chapter) 'r' encoding='utf-8') as f:
for line in f.readlines:
poss=jieba.cut(line)
for word in poss:
if word in role:
if names.get(word) is None:
names[word]=0
names[word] =1
df_new = pd.DataFrame.from_dict(names orient='index' columns=['{}'.format(count)])
df = pd.concat([df df_new] axis=1)
print('第{}集人物统计完毕'.format(count))
count =1
df.T.to_csv('role_count.csv' encoding='gb18030')
可视化
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif']=['kaiti']
plt.style.use('ggplot')
df=pd.read_csv('role_count.csv' encoding='gbk')
df=df.fillna(0).set_index('episode')
plt.figure(figsize=(10 5))
role_sum=df.sum.to_frame.sort_values(by=0 ascending=False)
g=sns.barplot(role_sum.index role_sum[0] palette='Set3' alpha=0.8)
index=np.arange(len(role_sum))
for name count in zip(index role_sum[0]):
g.text(name count 50 int(count) ha='center' va='bottom' )
plt.title('B站名侦探柯南弹幕——主要人物讨论总次数分布')
plt.ylabel('讨论次数')
plt.show
虽说是万年小学生,柯南还是有变回新一的时候,且剧情也并不只是"找犯人—抓犯人"。接下来从数据的角度来,扒扒一些精彩剧情集数。
2、柯南变回新一集数统计
考虑到部分集数中新一是在回忆中出现的,为减少偏差,将讨论的阈值设为250次,绘制如下分布图
其讨论次数结果及剧集名如下表所示
有兴趣的朋友可以码一下,除235集外,均是柯南变回新一的集数。
相关代码如下:
df=pd.read_csv('role_count.csv' encoding='gbk')
df=df.fillna(0).set_index('episode')
xinyi=df[df['新一']>=250]['新一'].to_frame
print(xinyi) #新一登场集数
plt.figure(figsize=(10 5))
plt.plot(df.index df['新一'] label='新一' color='blue' alpha=0.6)
plt.annotate('集数:50 讨论次数:309'
xy=(50 309)
xytext=(40 330)
arrowprops=dict(color='red' headwidth=8 headlength=8)
)
plt.annotate('集数:206 讨论次数:263'
xy=(206 263)
xytext=(195 280)
arrowprops=dict(color='red' headwidth=8 headlength=8)
)
plt.annotate('集数:571 讨论次数:290'
xy=(571 290)
xytext=(585 310)
arrowprops=dict(color='red' headwidth=8 headlength=8)
)
plt.hlines(xmin=df.index.min xmax=df.index.max y=250 linestyles='--' colors='red')
plt.legend(loc='best' frameon=False)
plt.xlabel('集数')
plt.ylabel('讨论次数')
plt.title('工藤新一讨论次数分布图')
plt.show
以讨论次数最多的572集,绘制词云图(剔除了高频词"新一",防止遗漏其他信息)如下所示:
从图中可看出,出现频率较高地词有整容、服部、声音、爱情等。(看来凶手是整成了新一的模样进行犯罪的,还有新兰的感情戏在里面,值得一看)
3、主线集数内容分析
主线剧情主要是围绕着组织成员(琴酒、伏特加、贝尔摩德)展开,绘制分布图如下:
plt.figure(figsize=(10 5))
names=['琴酒' '伏特加' '贝姐']
colors=['#090707' '#004e66' '#EC7357']
alphas=[0.8 0.7 0.6]
for name color alpha in zip(names colors alphas):
plt.plot(df.index df[name] label=name color=color alpha=alpha)
plt.legend(loc='best' frameon=False)
plt.annotate('集数:{} 讨论次数:{}'.
format(df['贝姐'].idxmax int(df['贝姐'].max))
xy=(df['贝姐'].idxmax df['贝姐'].max)
xytext=(df['贝姐'].idxmax 30 df['贝姐'].max)
arrowprops=dict(color='red' headwidth=8 headlength=8)
)
plt.xlabel('集数')
plt.ylabel('讨论次数')
plt.title('酒厂成员讨论次数分布图')
plt.hlines(xmin=df.index.min xmax=df.index.max y=200 linestyles='--' colors='red')
plt.ylim(0 400)
#输出主线剧集
mainline=set(list(df[df['贝姐']>=200].index) list(df[df['琴酒']>=200].index)) #伏特加可忽略不计
print(mainline)
从上图分析可知,组织成员的行动基本一致,其中贝姐(贝尔摩德)的人气在三人中是较高的,特别是在375集(与黑暗组织直面对决系列),讨论次数高达379。此外,统计其讨论次数大于200次的集数,结果如下:
以讨论次数最高的375集为内容,绘制词云图(剔除了高频词"贝姐",防止遗漏其他信息)如下
从图中可知,天使、琴酒、干妈、心疼、狙击手等词汇出现频率较高。从词频较低的败北主线中可以看出,这次酒厂行动应该是失败告终。
人物形象网络分析1、合并 txt 文件
为尽可能反映出弹幕观众对人物形象的描述,考虑到一集弹幕共3000条,为减少运行成本,这里仅选取特定人物讨论次数最多的20集合并后再进行分析。
import os
import pandas as pd
df=pd.read_csv('role_count.csv' encoding='gbk')
df=df.fillna(0).set_index('episode')
huiyuan_ep=list(df.sort_values(by='灰原哀' ascending=False).index[:20])
mergefiledir = 'C:/Users/dell/Desktop/柯南'
file=open('txt_all.txt' 'w' encoding='UTF-8')
count=0
for filename in huiyuan_ep:
filepath=mergefiledir '/' str(filename) '.txt'
for line in open(filepath encoding='UTF-8'):
file.writelines(line)
file.write('\n')
count =1
print('第{}集写入完毕'.format(count))
file.close
2、人物形象可视化
借助共现矩阵的思想,即同一句话中出现两个指定的词则计数1。指定起始点 Source 为灰原哀,代码如下所示:(注:其中,stopwods.txt 为停止词文件,role.txt 为人物昵称文件)
import codecs
import csv
import jieba
linesName=
names={}
relationship={}
jieba.load_userdict('role.txt')
txt=[ line.strip() for line in open('stopwords.txt' 'r' encoding='utf-8')]
name_list=[ i.replace('\n' '') for i in open('role.txt' 'r' encoding='utf-8').readlines()]
def base(path):
with codecs.open(path 'r' 'UTF-8') as f:
for line in f.readlines:
line=line.replace('\r\n' '')
poss = jieba.cut(line)
linesName.append([])
for word in poss:
if word in txt:
continue
linesName[-1].append(word)
if names.get(word) is None:
names[word]=0
relationship[word]={}
names[word] =1
return linesName relationship
def relationships(linesName relationship name_list):
for line in linesName:
for name1 in line:
if name1 in name_list:
for name2 in line:
if name1==name2:
continue
if relationship[name1].get(name2) is None:
relationship[name1][name2]=1
else:
relationship[name1][name2] =1
return relationship
def write_csv(relationship):
csv_writer2=open('edges.csv' 'w' encoding='gb18030')
writer=csv.writer(csv_writer2 delimiter=' ' lineterminator='\n')
writer.writerow(['Source' 'Target' 'Weight'])
for name edges in relationship.items:
for k v in edges.items:
if v>10:
writer.writerow([name k v])
csv_writer2.close
if __name__=='__main__':
linesName relationship=base('txt_all.txt')
data=relationships(linesName relationship name_list)
write_csv(data)
将生成的文件导入 Gephi ,得到如下人物形象图
线条越粗的线,代表该人物特征越明显。不难看出,大家对于哀酱的评价主要是美腻、可爱、心疼。
再做一张琴酒的~
哈哈哈,大家对琴酒的评价就比较逗逼了,变态,痴汉,聪明啥都有~
你以为的琴酒,实际上的琴酒(手动滑稽
以上就是本次 python 实战的全部内容~
欢迎大家关注皖渝的博客:
https://blog.csdn.net/shine4869/article/details/107381791
点分享