本文主要是介绍Python大作业——弹幕数据分析(获取url cid 弹幕 画出词云),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
首先要知道我们要做什么,要是茫无目的那就说明都做不了,我们这次的数据分析
步骤:
① 获取视频url
② 获取视频cid
③ 获取视频弹幕
第一步:获取视频url
解析我在弹幕里直接写了,可以自己看一看,我就不在博客里写了,不好排版
Get_Url代码部分
from bs4 import BeautifulSoup
from selenium import webdriverurl = 'https://space.bilibili.com/517327498/channel/seriesdetail?sid=60119'
print("------------开始罗翔说评论地址------------") # 2021-11-6 edg VS DK# chrome驱动,需要放在Python安装的目录下
driver = webdriver.Chrome(r"C:\Users\12430\AppData\Local\Programs\Python\Python39\chromedriver.exe")driver.get(url)
data = driver.page_source # 获取到页面信息
# print(data)
soup = BeautifulSoup(data, 'lxml') # 我们可以利用他解析HTML代码,并且在解析HTML代码的时候,如果
# HTML代码不规范或者不完整,lxml解析器会自动修复或补全代码,从而提高效率
# print(soup) # 不信你查看一下count = 1
res = []all = soup.find_all('li', attrs={'class': 'small-item fakeDanmu-item'}) # 找到这个专辑里的视频,只要是
# B站的专辑都在这个class里
print("all: ", end="")
print(all)
# print(all) # 不信你看一下for li in all:if count <= 15: # 罗翔的这个专辑视频就15个,所以爬15次a = li.find('a', attrs={'class': 'cover cover-normal'})print("这是a: ", end="")print(a)res.append('https:' + a.get("href")) # href的格式是//www.bilibili.com/video/BV1k44y1g7Dp这样count += 1else:break
print(res)
with open('Urls/链接.txt', 'w') as f: # 将结果放入这个文件里for link in res:f.write(link + '\n')
print("已将全部链接放入到链接.txt文件中")
第二步:获取视频cid
① 首先我们要定位到这个cid到第放在了什么鬼地方,发现在下面这个位置
在script中,我们发现cid好出现在了这个window.__playinfo__中,所以我们要想办法获取他它,这边有一个办法就是startswith()这个方法
② 知道cid位置以后就想这么获取它就行了
首先用startswith()获取链接,不知道为什么罗翔老师的视频不是//cn开头而是//upos(图一),
通过正则表达式的方法获取到cid的部分(图二)。
然后通过一系列的处理获取前9个数字,至此获取cid成功
Get_Cid 代码部分
from bs4 import BeautifulSoup
from selenium import webdriver
import re
import timecids = []
Urls = []cid_start = 15
# sets_end = len(open('Urls/Link.txt', 'r').readlines()) + sets_start - 1anime_name = "《罗翔读评论》"with open('Urls/Link.txt', 'r') as f:for line in f.readlines():Urls.append(line.strip())print("开始爬取动漫" + anime_name + "所有视频的Cid")# chrome驱动,需要放在Python安装的目录下
driver = webdriver.Chrome(r"C:\Users\12430\AppData\Local\Programs\Python\Python39\chromedriver.exe")
link = '' # 需要先定义,后面查找url的时候可能会应为找不到url而导致link未定义
cid = ''
for url in Urls:driver.get(url)data = driver.page_sourcesoup = BeautifulSoup(data, 'lxml')all = soup.find_all('script')for a in all:if str(a).startswith("<script>window.__playinfo__"): # 必须这样否则找不到res = a# print(res)links = re.split(r':', str(res))# print("-----------------------")# print(links)for url in links:# 这个链接前面是域名,中国的都是以cn开头,但是现在好像不是了if url.startswith("//"): # 不知道为什么有些视频不是//cn开头的,所以用//来实现print(url)print("-----------------------")link = urlbreakcid = re.findall(".*/(.*)-1-.*", link)# 获取到视频的cid,存进数组然后一起存进Cid.txt文件中cid = cid[0]# 处理特殊情况下,长度不符合cid,去除尾部部分# 需要根据Cid进行特殊处理if len(cid) > 9:length = len(cid)a = length - 9cid = cid[:-a]# print(cid)cids.append(cid)# 每抓完一个网页休眠5秒time.sleep(5)print(anime_name + "第" + str(cid_start) + "集Cid爬取完毕")cid_start -= 1print(cid)with open('Urls/Cid.txt', 'w') as f:for id in cids:f.write(id + '\n')print("已将全部视频的Cid放入到Cid.txt文件中")
第三步:获取视频的弹幕
① 首先我们需要一个headers 获取 用户代理 和 Cookie,这个自己用F12找找就行,
然后开始寻找视频的时间,首先我们发现罗翔老师读评论的第一期和最后一期好像是2020/7/14和
2022/4/29来着,那么我们就设置2020/7/13到2022/4/30来寻找视频日期
② 开始获取弹幕数据,这边就不用图片了,代码太长了
第一步,首先要知道<d></d>这个标签里的这么多数字是什么
第一个参数 17.57900 记作DM_time,是弹幕在视频中出现的时间,以秒数为单位。
第二个参数 1 记作DM_mode,是弹幕的模式1…3 滚动弹幕 4底端弹幕 5顶端弹幕 6.逆向弹幕 7精准定位 8高级弹幕
第三个参数 25 记作DM_font,是字号, 12非常小,16特小,18小,25中,36大,45很大,64特别大
第四个参数 16777215 记作DM_color,是字体的颜色以HTML颜色的十进制为准
第五个参数 1653783652 记作DM_realTime,是发送弹幕的时间戳
第六个参数 0 记作DM_pool,是弹幕池 0普通池 1字幕池 2特殊池(高级弹幕)
第七个参数 def8eb39记作DM_userID,是发送者的ID,用于“屏蔽此弹幕的发送者”功能
第八个参数 1062823650710860544 记作DM_id,是弹幕在弹幕数据库中rowID,也就是这条弹幕是历史总弹幕的第几条
第九个参数 11 这个玩意好像没什么用?
弹幕本体 记作DM_text
Get_BulletChat部分
import re
import jieba
from bs4 import BeautifulSoup
import time
import pandas as pd
import requests
import datetimeheaders = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) ""Chrome/102.0.5005.63 Safari/537.36","Connection": "keep-alive",# 这个cookie的获取方法在文档中已说明"Cookie": "buvid3=F2278A54-EB90-05CA-6AC4-25606AB151A009225infoc; CURRENT_FNVAL=4048; ""b_lsid=10C7E85F4_1812E57B9D8; _uuid=1CCF3FED-9CA9-4996-69104-410F55A4243FE10121infoc; ""buvid4=AD4080D0-B5E7-8064-1B21-3F1AFE743B2510754-022060418-h0Kdr2RwgtcfH98SEPYQ1Q%3D%3D; ""buvid_fp=16eaee4e45e32b194417c0880035c419; CURRENT_BLACKGAP=0; blackside_state=0; sid=7hhsqlnl; ""rpdid=|(J|)Yllu|k~0J'uYlRJJmmuY; ""b_timer=%7B%22ffp%22%3A%7B%22333.788.fp.risk_F2278A54%22%3A%221812E57C17E%22%2C%22333.999.fp"".risk_F2278A54%22%3A%221812E5BBF35%22%7D%7D "
}
sets = 15 # 最新一期的数字
anime_name = "罗翔读评论"
dates = [] # 日期数组,用于填充url
# 遍历日期 包括begin和end的日期 生成类似2020-04-29的格式的日期
begin = datetime.date(2020, 7, 13)
end = datetime.date(2022, 4, 30)
# 需要一天一天找,所以比较慢
d = begin
delta = datetime.timedelta(days=1) # 一天一天找
while d <= end:dates.append(str(d.strftime("%Y-%m-%d")))d += delta
# print(dates)
#Cids = [] # Cid数组,用于填充url
with open('Urls/Cid.txt', 'r') as f:for line in f.readlines():Cids.append(line.strip())
print(Cids) # 15--1for cid in Cids:print("正在爬取第" + str(sets) + "期的" + anime_name + "弹幕...")# 每次都要重置这些数据dm_data = [] # 弹幕数据dm_text = [] # 弹幕本体# 弹幕的八个参数和弹幕本体DM_time = [] # 弹幕时间DM_mode = [] # 弹幕模式DM_font = [] # 弹幕类型DM_color = [] # 弹幕颜色DM_realTime = [] # 弹幕时间戳DM_pool = [] # 弹幕池DM_userID = [] # 弹幕发送者IDDM_id = [] # 弹幕是弹幕数据库中的第几条DM_text = []print("正在爬取第" + str(sets) + "期的弹幕...")for date in dates:url = 'https://api.bilibili.com/x/v1/dm/list.so?oid=' + cidresponse = requests.get(url=url, headers=headers) # 返回文本信息response.encoding = 'utf-8'soup = BeautifulSoup(response.text, 'lxml') # 建立soup对象all = soup.find_all("d") # d是弹幕标签# print(all)for d in all:# 弹幕数据dm_data.append(str(d.get("p")).split(",")) # p是标签# 弹幕本体dm_text.append(d.get_text())print("第" + str(sets) + "集" + " " + str(date) + "数据爬取完毕!")# 分别把数据存进这几个数组for i in dm_data:DM_time.append(i[0])DM_mode.append(i[1])DM_font.append(i[2])DM_color.append(i[3])DM_realTime.append(i[4])DM_pool.append(i[5])DM_userID.append(i[6])DM_id.append(i[7])for i in dm_text:DM_text.append(i)# 利用pandas进行csv文件的写入dt = {"DM_time": DM_time, "DM_mode": DM_mode, "DM_font": DM_font, "DM_color": DM_color,"DM_realTime": DM_realTime, "DM_pool": DM_pool, "DM_userID": DM_userID, "DM_id": DM_id, "DM_text": DM_text}d = pd.DataFrame(dt)d.to_csv('./Danmu/Danmu-' + str(sets) + '.csv', encoding='utf-8') # 存储弹幕信息print("已将弹幕放入到Danmu-" + str(sets) + ".csv文件中")sets -= 1# 每抓完一个网页休眠7秒print("缓冲中...")time.sleep(5)print("已将罗翔读评论的第①期到第①⑤期的弹幕爬取完毕")
第四步,开始作图
主要功能:
import matplotlib.pyplot as plt
import matplotlib
import pandas as pd
import os
from wordcloud import WordCloud
import jiebaanime_name = "罗翔读评论"
file_dir = "./Danmu/"
# 获取文件名
files = [files for root, dirs, files in os.walk(file_dir)]
print(files)# 去重
def duplicate(files):for file in files:print(file)data_df = pd.DataFrame()df = pd.read_csv(file_dir + file, encoding="utf-8-sig", index_col=0, engine='python')df = pd.concat([data_df, df])# 开始去重data = df.drop_duplicates(subset=['DM_id'], keep='first')data.to_csv(file_dir + file, encoding='utf-8-sig', index=True, index_label="")print("去重完毕")# 每一期弹幕总数的变化折线图
def danmuSumPlot(files):print("弹幕总数变化图绘制中...")list1 = ['1', '2', '3', '4','5', '6', '7', '8','9', '10', '11', '12','13', '14', '15']data_sum = []for file in files:data = pd.read_csv(file_dir + file, encoding="utf-8-sig", index_col=0)data_sum.append(len(data))matplotlib.rcParams["font.family"] = "SimHei"plt.plot(list1, data_sum, "m", ':')plt.ylabel("弹幕数量")plt.xlabel("《罗翔读评论》期数")plt.title("每一期弹幕总数的变化图")plt.savefig('./Analysis/弹幕分析图片/弹幕总数变化图', dpi=800)plt.show()print("绘制完毕")# 发弹幕总数TOP10的用户柱状图
def danmuUserTopBarh(files):print("弹幕TOP20用户图绘制中...")datas = []for file in files:datas.append(pd.read_csv(file_dir + file, encoding="utf-8", index_col=0))# 先合并全部csv文件,再进行统计data = pd.concat(datas)data = data.groupby('DM_userID').size().reset_index(name="count")data = data.sort_values("count", ascending=False)label = [] # y轴的值width = [] # 给出具体每个直方图的数值i = 0for item in data.values:if i < 20:label.append(item[0])width.append(item[1])i += 1else:breakmatplotlib.rcParams["font.family"] = "SimHei"y = [20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1] # 给出在y轴上的位置plt.barh(y=y, width=width, tick_label=label) # 绘制水平直方图plt.ylabel("用户ID")plt.xlabel("弹幕数")plt.title("发弹幕总数TOP20的用户柱状图")plt.subplots_adjust(left=0.22) # 控制图片左边的间隔 避免显示不全plt.savefig('./Analysis/弹幕分析图片/TOP20柱状图', dpi=600)print("绘制完毕")# 每期弹幕密度变化图
def danmuDensityChange(files):print("弹幕密度变化图绘制中...")sets = 1for file in files:data = pd.read_csv(file_dir + file, encoding="utf-8-sig", index_col=0)data = data.sort_values("DM_time")# 先对弹幕发送时间进行取整data['DM_time'] = [int(item) for item in data.DM_time]data = data.groupby('DM_time').size().reset_index(name="counted")list2 = [item for item in data.DM_time]data_sum = [item for item in data.counted]matplotlib.rcParams["font.family"] = "SimHei"plt.plot(list2, data_sum, "c")plt.ylabel("弹幕数量")plt.xlabel("视频时间轴/(秒)")plt.title(str(sets) + "期弹幕密度变化图")plt.savefig("./Analysis/弹幕密度变化/" + str(sets) + '期弹幕密度变化图', dpi=600)sets += 1print("绘制完毕")# 每期的弹幕词云,词云已经用jieba库进行去词了
def danmuWordCloud(files):print("弹幕词云绘制中...")sets = 1for file in files:data = pd.read_csv(file_dir + file, encoding="utf-8-sig", index_col=0)# 先把全部弹幕信息写成一个字符串,再调用方法words = ''for item in data.DM_text:words += itemwords = " ".join(jieba.cut(words))# 这个scale参数是画布大小参数,也就是调整分辨率的,10代表是原来的10倍大小,越高分辨率越高wd = WordCloud(font_path='simhei.ttf',max_words=80,background_color='white',min_font_size=5,width=1920,height=1080,scale=10).generate(words)plt.imshow(wd)plt.axis("off")wd.to_file("./Analysis/词云/第" + str(sets) + "期词云.jpg")sets += 1print("绘制完毕")# 每期的弹幕词云(jieba去词以后-带图片背景)
def danmuWordCloud_Img(files):print("弹幕词云绘制中...")# df = pd.DataFrame()# jieba.load_userdict("./Tools/" + anime_name + "词汇.txt")sets = 1sets_s = 1for file in files:data = pd.read_csv(file_dir + file, encoding="utf-8", index_col=0)# 先把全部弹幕信息写成一个字符串,再调用方法words = ''for item in data.DM_text:words += str(item)# print(words)with open('./自定义文件/stopwords.txt', 'r+', encoding='utf-8') as fp:stopwords = fp.read().split('\n') # 将停用词词典的每一行停用词作为列表中的一个元素word_list = [] # 用于存储过滤停用词后的分词结果'seg_list = jieba.cut(words)for seg in seg_list:if seg not in stopwords:word_list.append(seg)words = " ".join(word_list)mask = plt.imread('./Analysis/maskImages/' + anime_name + '.jpg') # 读取图片作为词云图轮廓# if sets == 15:# sets = 1# 这个scale参数是画布大小参数,也就是调整分辨率的,10代表是原来的10倍大小,越高分辨率越高wd = WordCloud(font_path='simhei.ttf',max_words=800,background_color='white',min_font_size=1,# width=1920,# height=1080,mask=mask,scale=5).generate(words)plt.imshow(wd)plt.axis("off")wd.to_file("./Analysis/带形状词云/" + anime_name + "第" + str(sets_s) + "集词云_Image.jpg")# sets += 1sets_s += 1print("绘制完毕")# 弹幕颜色直方图
def DM_color(files):plt.rcParams['font.sans-serif'] = ['SimHei']plt.rcParams['axes.unicode_minus'] = Falsefor file in files:data_df = pd.DataFrame()data = pd.read_csv(file_dir + file) # 读取数据文件data = pd.concat([data_df, data]) # 拼接数据data_color = data['DM_color'].value_counts() # 统计用户发送的弹幕使用同一种颜色的数量data_color = data_color.head(7) # 选出前七种颜色favorite_color = [] # 弹幕颜色的十六进制颜色码# 将爬取到的十进制颜色码转换为十六进制颜色码for a_color in data_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], data_color.values, color=favorite_color)plt.title(anime_name + '弹幕颜色使用数量前七名')plt.xlabel('排名')plt.ylabel('使用该颜色的弹幕数量')# plt.show()fig.savefig(r'./Analysis/弹幕分析图片/' + anime_name + 'Color.png', transparent=True) # 保存if __name__ == '__main__':# # 去重duplicate(files[0])# 每一期弹幕总数的变化折线图danmuSumPlot(files[0])# 发弹幕总数TOP20的用户柱状图danmuUserTopBarh(files[0])# 弹幕颜色直方图DM_color(files[0])# 每期弹幕密度变化图danmuDensityChange(files[0])# 每期的弹幕词云danmuWordCloud(files[0])# 每期的弹幕词云(jieba去词以后-带图片背景)danmuWordCloud_Img(files[0])
这边就贴一张结果图片当实例吧
最后鸣谢这篇文章对我的帮助:(1条消息) python大作业——B站弹幕数据爬取与分析_lkx_icy的博客-CSDN博客_python爬取b站弹幕并进行数据分析
这篇关于Python大作业——弹幕数据分析(获取url cid 弹幕 画出词云)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!