一、项目背景 哈哈哈哈哈哈哈哈巴比爸比挖鼻巴伯哈哈哈哈哈哈哈哈哈哈哈哈哈哈歪比歪比哈哈哈哈哈哈哈哈哈哈茉莉哈哈哈哈哈哈哈哈哈哈哈啊游古德马来西亚哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈茉莉哈哈哈哈哈哈哈哈哈哈哈哈哈茉莉哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈

嗝~~~~~~~~~~~~~~~~~~~·~~~~~~~~~~~~~~~~~~

二、效果展示 本次数据可视化分析使用的是pyecharts工具包,最终结果如下:


其中问号表示弹幕内容为"?“或多个”?"的词语(弹幕)

起飞类词语表示包含了"飞"、"起飞"的词语(弹幕)

老……词语表示 如"老倒霉蛋了","老男酮了"之类的词语(弹幕)

经典表示 如"经典双线程","经典三级两技能"等包含了经典的词语(弹幕)

其他的我相信不用解释各位都明白是什么意思。

三、爬取数据

分为两部分:

  • 1.抓取B站UP主"艺术家阿克曼"个人主页所有视频链接
  • 2.抓取每个视频中的弹幕内容并写入.csv文件

使用工具:requests 、 lxml 、 re 、Selenium 、time 、pandas 、numpy、datatime

1.抓取B站UP主"艺术家阿克曼"个人主页所有视频链接

数据来源为B站饭堂老板"艺术家阿克曼"个人主页所有视频链接,该up主空间内除了一个视频的内容为影流之主,其他视频的内容皆与马老师有关,所以我们从他下手。

①关于B站个人空间:

从个人空间的形式可以看出该网页的数据是异步加载的,直接用requests请求是获取不到视频有关内容的。所以这一步我们用Selenium,打开页面后,使用.page_source获取页面的HTML,从而提取每个视频的链接

②关于翻页:

阿克曼B站个人空间的链接为:

https://space.bilibili.com/451618887/video?tid=0&page=2&keyword=&order=pubdate

很明显,链接中有一个参数为page,改变它我们将可以实现翻页。

③获取某一页所有视频链接:

可以看到每个视频的链接都在li标签下的a标签的href属性中,可以通过xpath直接获取href。我们也可以先获取li标签中data-aid属性值,然后拼凑字符串,获取完整的链接。

④思路流程代码如下:

import re
from lxml import etree
import selenium.webdriver
import time# Selenium打开视频作者个人网页,并获取页面HTML
driver = selenium.webdriver.Chrome()
driver.get("https://space.bilibili.com/451618887/video?tid=0&keyword=&order=pubdate")  # 第一页的page参数可有可无
response = driver.page_source
driver.close()# 分析HTML,获取总页数
html = etree.HTML(response,etree.HTMLParser()) #
all_pages = html.xpath("//span[@class=\"be-pager-total\"]/text()")
all_pages = re.findall("\d+",str(all_pages))[0]# 获取所有页的HTML,获取所有视频链接
video_lastcode_list =[]
for page in range(eval(all_pages)):url = "https://space.bilibili.com/451618887/video?tid=0&page={}&keyword=&order=pubdate".format(page+1)# 获取该页HTMLdriver = selenium.webdriver.Chrome()driver.get(url)time.sleep(2)response = driver.page_sourcedriver.close()html = etree.HTML(response, etree.HTMLParser())# 获取该页中所有视频链接video_lastcode = html.xpath('//div[@id=\"submit-video-list\"]/ul[@class=\"clearfix cube-list\"]//li/@data-aid')   # 获取data-aidvideo_lastcode_list.extend(video_lastcode)url_list = []  # 拼凑字符串
for video_lastcode in video_lastcode_list:url = "https://www.bilibili.com/video/" + video_lastcodeurl_list.append(url)print(url_list)

打印结果:

['https://www.bilibili.com/video/BV1Y7411y74a', 'https://www.bilibili.com/video/BV1Y7411y7b5',………………'https://www.bilibili.com/video/BV16J411T7D5']

2.抓取视频中弹幕

以下面这个视频为例,打开网页开发者工具,Network,刷新页面,可以看到各个数据的通信。发现这样一组数据:

点进去,可以得到该数据的请求连接Request URL: https://api.bilibili.com/x/v1/dm/list.so?oid=123951171 。
这就是B站弹幕的API接口,每个视频都有各自的oid参数,只需要改变oid参数,我们便可以获取每个视频的弹幕数据。

①抓取视频oid:

我们在视频的HTML中搜索oid或者cid(网上查阅资料得知cid和oid是同值的),并没有搜索到相关内容,可能是网页为了防爬隐藏了参数名字。但实际上在HTML中还存在oid参数的值。

以上面的视频"大司马:最吓人的一局比赛,强行洗脑观众!反复强调自己王者水平,令人细思极恐"为例。我们可以在Network中得知该视频oid=123951171,然后再HTML中 crtl+F 搜索123951171,搜索结果:

通过正则表达式,我们可以从HTML中获取oid值,然后拼凑字符串,以获取视频所对应的API接口链接。

代码如下:

import requests
import reheaders = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36'}
request = requests.get(url, headers=headers) # url为某个视频链接
response = request.text
oid = re.findall("\d\d/\d\d/(\d{9})/",response)[0]

②抓取视频弹幕:

我们进入B站视频弹幕API的链接,其数据是XML格式:


所有弹幕内容都在d标签文本内容中。d标签的p属性中有以逗号为分割的8个参数,各个参数的含义如下:

  • 参数1:弹幕出现的时间,以秒数为单位
  • 参数2:弹幕的模式,1-3 滚动弹幕,4 底端弹幕,5顶端弹幕,6 逆向弹幕,7 精准定位,8 高级弹幕
  • 参数3:字号 (12非常小,16特小,18小,25中,36大,45很大,64特别大)
  • 参数4:字体的颜色;这串数字是十进制表示;通常软件中使用的是十六进制颜色码;如白色,其RGB值为(255,255,255),十进制值:16777215,十六进制值:#FFFFFF
  • 参数5:unix时间戳,从1970年1月1日(UTC/GMT的午夜)开始所经过的秒数
  • 参数6:弹幕池,0普通池,1字幕池,2特殊池
  • 参数7:发送者的ID,用于“屏蔽此弹幕的发送者”功能
  • 参数8:弹幕在弹幕数据库中rowID 用于“历史弹幕”功能。

该API接口的弹幕时间从2019-09开始的(历史弹幕的抓取不在本文讨论范围内),每个视频弹幕数量大概在600-2000条。

我们只抓取弹幕内容、发表时间、弹幕模式。

代码如下:

import requests
import re
from lxml import etree
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36'}
request = requests.get(url, headers=headers)
response = request.content
html = etree.HTML(response,etree.HTMLParser())
danmu_list = html.xpath('//d/text()') # 爬取弹幕类容
danmu_time_list = [datetime.datetime.fromtimestamp(eval(i.split(",")[4])).strftime("%Y-%m-%d %H:%M:%S") for i in html.xpath("//d/@p")]   #爬取弹幕发表时间
danmu_class_list = [i.split(",")[1] for i in html.xpath("//d/@p")]   #爬取弹幕类型# 对于弹幕类型,我们再对它简单的转换一下def judge_class(x):x = eval(x)if 1<= x <= 3:return "滚动弹幕"elif x==4:return "底端弹幕"elif x==5:return "顶端弹幕"elif x==6:return "逆向弹幕"elif x== 7:return "精准定位弹幕"else:return "高级弹幕"
danmu_class_list = [judge_class(i) for i in danmu_class_list]print(danmu_list)
print(danmu_time_list)
print(danmu_class_list)

结果如下:

['人头落后一半控龙 就嗯控', '弹幕经典夸白银对手强', '哈哈哈哈哈哈哈哈', ………………………'不清楚是不是起飞 ]
['2020-03-24 09:40:22', '2020-03-24 09:40:23', '2020-03-24 09:40:26', ………………………… ,'2020-03-25 11:31:55']
['滚动弹幕', '滚动弹幕', '滚动弹幕'……………… '滚动弹幕']

③把抓到的内容写入.csv文件中:

需要注意的地方就是编码问题,试了好多种编码,只有
gb18030编码方式能使csv文档中文不乱码。

写入csv文件的代码如下:

import pandas as pd
try:df.to_csv("take off.csv", mode="a+", header=None, index=None, encoding="gb18030")print("写入成功")
except:print("写入失败")

四、数据预处理

简单设置几个循环,便可以爬取完所有弹幕的数据,并将所有数据存入take_off.csv文件中。文件中内容格式如下,其中#######为弹幕发表时间,不知道为什么显示不出来,但它的值还是正确的。take_off.csv文件中共有40W+行数据,说明瓷器爬取了"艺术家阿克曼"空间视频40W+条弹幕.

下面我们进行数据预处理,代码如下:

import pandas as pd
import numpy as np
import redf = pd.read_csv("take off.csv",engine="python",encoding="gb18030",header=None)
df.index = range(len(df))
df.columns = ["弹幕内容","发表时间","弹幕类型"]# 数据统一归类。如把带有 飞,起飞,飞飞飞字眼的内容都归为 起飞, 把可爱 字眼的内容都归为可爱,等……
geng = ["飞","可爱","害怕","上当","不对劲","哈","?","?","倒霉蛋","啥b","啥比","经典","病","ICU","吃","千层饼","双线程","被动触发","被动还就那个触发","好k","离谱","男同","男酮","肉蛋葱鸡","肉蛋充饥","芜湖","李在赣神魔","李在干什么","拖","崩撤卖溜","泪目","谜语人","折磨王","贼眉鼠眼","饭","饱了","xswl","666","配合的不是很好","skr","吸血怎么说","辅助杀手","神头鬼脸","胆小鬼","一枪不开","弱智墨菲特","一直可以的","量角器","角度","小法司","缪撒","抹布吸","因为你不会","多捞哦","歪比巴卜","歪比歪比","加密通信","马来西亚","坠机","老","不亏","红皮鸭子"]
geng = np.array(geng)   # 这里要变为ndarray格式的原因是列表不支持布尔索引,而ndarray可以。
def rename(x=None,geng=geng):index = [i in x for i in geng]# print(index)if sum(index) > 0:return geng[index][0]else:return x
df["弹幕内容"] = df["弹幕内容"].apply(rename)# 继续归一词语、给词语修改名字。如"飞"与"芜湖"都可以归为一类,并改名为"起飞类词语"
df["弹幕内容"] = df["弹幕内容"].apply(lambda x:re.sub("飞","起飞类词语",x))
df["弹幕内容"] = df["弹幕内容"].apply(lambda x:re.sub("芜湖","起飞类词语",x))df["弹幕内容"] = df["弹幕内容"].apply(lambda x:re.sub("哈","哈哈哈哈哈",x))
df["弹幕内容"] = df["弹幕内容"].apply(lambda x:re.sub("笑死我了","哈哈哈哈哈",x))df["弹幕内容"] = df["弹幕内容"].apply(lambda x:re.sub("肉蛋充饥","肉蛋葱鸡",x))
df["弹幕内容"] = df["弹幕内容"].apply(lambda x:re.sub("肉蛋葱鸡","肉蛋葱鸡",x))df["弹幕内容"] = df["弹幕内容"].apply(lambda x:re.sub("老","老……的词语",x))df["弹幕内容"] = df["弹幕内容"].apply(lambda x:re.sub("病","发病类词语",x))
df["弹幕内容"] = df["弹幕内容"].apply(lambda x:re.sub("ICU","发病类词语",x))df["弹幕内容"] = df["弹幕内容"].apply(lambda x:re.sub("拖","拖,就硬拖",x))df["弹幕内容"] = df["弹幕内容"].apply(lambda x:re.sub("李在赣神魔","李在赣神魔",x))
df["弹幕内容"] = df["弹幕内容"].apply(lambda x:re.sub("李在干什么","李在赣神魔",x))
df["弹幕内容"] = df["弹幕内容"].apply(lambda x:re.sub("你在干什么","李在赣神魔",x))df["弹幕内容"] = df["弹幕内容"].apply(lambda x:re.sub("一直可以的","……一直可以的",x))df["弹幕内容"] = df["弹幕内容"].apply(lambda x:re.sub("饭","下饭类词语",x))
df["弹幕内容"] = df["弹幕内容"].apply(lambda x:re.sub("饱了","下饭类词语",x))
df["弹幕内容"] = df["弹幕内容"].apply(lambda x:re.sub("吃","下饭类词语",x))df["弹幕内容"] = df["弹幕内容"].apply(lambda x:re.sub("?","问号",x))
df["弹幕内容"] = df["弹幕内容"].apply(lambda x:re.sub("\?","问号",x))df["弹幕内容"] = df["弹幕内容"].apply(lambda x:re.sub("歪比歪比","加密通信",x))
df["弹幕内容"] = df["弹幕内容"].apply(lambda x:re.sub("歪比巴卜","加密通信",x))
df["弹幕内容"] = df["弹幕内容"].apply(lambda x:re.sub("加密通信","加密通信",x))
df["弹幕内容"] = df["弹幕内容"].apply(lambda x:re.sub("马来西亚","加密通信",x))
# 归一后,我们接下来统计各个词语的出现频率
print(df["弹幕内容"].value_counts())
df1 = df["弹幕内容"].value_counts().reset_index()
df1 = pd.DataFrame(df1)
print(df1)
df1.to_csv("third_statistics.csv",header=None,index=None,encoding="gb18030")

third_statistics.csv文件的部分内容如下。根据该文件的数据,我们便可以画相关词频出现的数据图。

五、结语 完整代码:https://github.com/KongBaiVso/Scrap-Bilibili-bullet-screen-of-Dasima

如果你愿意,点个星。如果你不愿意,所以你愿意。

最终结果在最前面展示了,具体画图的代码就不贴不出来了,太乱了,大家可以选择自己处理数据,并使用顺手的工具去画图。

从数据分析中,发现最早出现起飞类词语是在2019-10-01,这个时间附近发生什么了??不太清楚。从这天起马老师跨行飞行员、厨师、游戏教学、秘密通信专家、哲学家,并在各个方面都取得了卓越的成就。在飞行员方面,多次突破第二宇宙速度,数次差点突破太阳系,成为现代人类历史上离太阳系边界最近的人类;在厨师方面,马老师荣誉称号为金牌厨师,著有"肉蛋葱鸡"、“肾腔洗鲤”、“泥载干渗膜"等多个自创菜品,风靡全国。游戏教学方面不用多说,大家有目共睹。在秘密通信这一专业领域中,马老师更是破译了马来西亚密语、戴夫语,本文开头的项目背景便是用马来西亚密语和戴夫语写的。作为哲学家,马老师提出的"会与不会”,"千层博弈"的哲学思想更是成为大家经常挂在口边的素材。

我们再来看看词频出现的统计图。开始我本以为起飞类词语会是出现频率最高的那一个,但我错了,没想到问号才是。这样看来马老师在大家眼中应该是一个更加神秘的人,大家对他的好奇与疑惑大于其他的一切。同时,"老……"一类词语与"经典……"一类词语排名第三与第四,这说明马老师在大家心中是老练的,是经典永流传的。紧随其后的是"哈哈哈哈"和下饭类词语,马老师除了给大家带来了经典,也给大家带来了欢乐,带来了胃口。大司马,永远滴神!

非mxz,非黑子,持中立观点,并不讨厌马老师。

Python爬取、可视化分析B站大司马视频40W+弹幕相关推荐

  1. python爬取+BI分析5000条内衣数据,发现妹子最爱这款文胸

    生活中我们经常会用python进行数据爬取,但是爬取简单分析难,很多人喜欢用echarts图表接口或者是python的第三方库进行数据可视化,甚至是用matlab,基本上都需要用代码实现,在数据展示上 ...

  2. python爬取并分析淘宝商品信息

    python爬取并分析淘宝商品信息 背景介绍 一.模拟登陆 二.爬取商品信息 1. 定义相关参数 2. 分析并定义正则 3. 数据爬取 三.简单数据分析 1.导入库 2.中文显示 3.读取数据 4.分 ...

  3. Python 爬取 201865 条《隐秘的角落》弹幕,发现看剧不如爬山?

    作者 | 朱小五 责编 | 屠敏 封图 | CSDN 付费下载自东方 IC 最近又火了一部国产剧:<隐秘的角落>. 如果你没看过,那可能会对朋友圈里大家说的"一起去爬山" ...

  4. Python 爬取 201865 条《隐秘的角落》弹幕数据,发现看剧不如爬山?

    Python 爬取 201865 条<隐秘的角落>弹幕数据,发现看剧不如爬山? 本文不涉及剧透!请放心食用 最近又火了一步国产剧:<隐秘的角落> 如果你没看过,那可能会对朋友圈 ...

  5. python分析b站_Python爬取并分析B站最热排行榜,我发现了这些秘密

    现在大家的生活中,已经越来越离不开B站了,2020年的第一季度,B站月活跃用户达到了1.72亿,日活跃用户也已经突破了5000万个用户.源源不断的流量让B站的up主们也是粉丝数目不断暴涨,百万粉丝的u ...

  6. 用Python爬取并分析了B站最热排行榜,我发现了这些秘密

    现在大家的生活中,已经越来越离不开B站了,2020年的第一季度,B站月活跃用户达到了1.72亿,日活跃用户也已经突破了5000万个用户.源源不断的流量让B站的up主们也是粉丝数目不断暴涨,百万粉丝的u ...

  7. python b站 礼物_用Python爬取并分析了B站最热排行榜,我发现了这些秘密

    现在大家的生活中,已经越来越离不开B站了,2020年的第一季度,B站月活跃用户达到了1.72亿,日活跃用户也已经突破了5000万个用户.源源不断的流量让B站的up主们也是粉丝数目不断暴涨,百万粉丝的u ...

  8. python 怎么爬桌软件数据_如何利用Python爬取并分析红岭创投的数据?

    第一步:爬取数据 通过 selenium + Firefox 的无头模式将需要的数据爬取出来,代码实现不在赘述,详细步骤可查看我的上一篇图文(如何利用Python爬取网易新闻), 由于 seleniu ...

  9. python爬取琳琅社区整站视频(一晚6000部)

    琳琅社区(传闻中最受男人喜爱的网站),哼哼,我倒要看看是不是真的 该项目用于爬取琳琅社区整站视频(仅供学习) 主要使用:python3.7 + scrapy2.19 + Mysql 8.0 + win ...

  10. python爬取芒果TV《乘风破浪的姐姐》弹幕数据(已完成)

    爬取平台:芒果TV 爬取内容:<乘风破浪的姐姐>弹幕数据(以前6期为例) 爬取工具:Anaconda3 Spyder 爬取日期:2020年7月1日 打开节目网页,通过开发者工具Networ ...

最新文章

  1. R语言构建仿真数据库(sqlite)并使用dplyr语法和SQL语法查询数据库、将dplyr语法查询语句翻译为SQL查询语句
  2. 如何实现科技论文里面的算法
  3. 根据二叉树前序遍历和中序遍历重建二叉树
  4. tensorflow笔记:多层LSTM代码分析
  5. 怎样从frm ibd恢复mysql_怎样从frm,ibd恢复MYSQL
  6. 万万没想到,JVM内存结构的面试题可以问的这么难?
  7. PyTorch系列 (二): pytorch数据读取自制数据集并
  8. rpg制作大师2003_RPG制作大师MV 我们一起做游戏(十四)
  9. vs2005/vs2008 快捷键【转】
  10. 【ORACLE】ORA-12547: TNS:lost contact
  11. 阿里云国际版跨境加速,全球加速和Squid 缓存代理解决方案
  12. 2020牛客暑期多校训练营(第九场) Groundhog and 2-Power Representation
  13. 第十三届蓝桥杯大赛软件赛省赛(b组c语言)
  14. PS技巧分享,不容错过
  15. DDOS攻击/防御介绍
  16. 机器学习从入门到创业手记-2.算法与导师
  17. 1、关于百兆口、千兆口、万兆口端口和网线的小常识
  18. ESP32与STM32的SPI半双工通信与握手协议讲解(附代码)
  19. Quectel EC200N-CN 大尺寸物联网首选LTE Cat 1模块[移远通信]
  20. Linux中搭建 Postfix 邮件服务器

热门文章

  1. 15-构造函数及原型
  2. 线程基础 第一篇:线程的定义、状态、属性、简单实现线程
  3. hdu3594-仙人掌图的判断tarjan找环-Cactus
  4. 【AnySDK】接入必读及常见问题
  5. 群晖web文件服务器docker,群晖docker搭建数据库服务器
  6. java计算机毕业设计房屋租赁系统源码+数据库+系统+lw文档+部署
  7. 华中科技大学计算机证书领取,华中科技大学计算机水平测试软考报名通知
  8. 第五章 Spanning Tree协议安全攻防
  9. 整数 (Integer)
  10. ArcEngine加载图层的五个步…