0%

爬取b站视频弹幕并制作词云一

用过B站的小伙伴们都知道,B站的弹幕是一绝。 今天我们爬取指定bv号视频下的弹幕,添加到csv文件中并制作成词云。

优化后的完整代码:代码地址

一、分析弹幕的接口url地址

这里有两个地址可以实现爬取弹幕,一个是有限条的,另一个则是根据日期循环爬取,可以实现所有弹幕的爬取。
案例是冰冰的vlog.001

第一个不需要登录就可以爬取的接口为:

1
https://comment.bilibili.com/{oid/cid}.xml

我抓包抓了很久也没有找到这样的包,b站现在把弹幕的接口藏得很深…不是很好找,最后是看着别人的博客在移动端的网页js中找到了这样的请求地址。

可以看到返回的结果是一个xml界面,只需要请求这个界面使用xpath对其进行定位就可以很容易的获取到自己想要的数据。
那么我们现在来分析第二个接口:

可以看到右侧有一个弹幕历史的功能,我们先把抓包记录清除一下,点击按钮看看有什么新抓到的包:

可以看到这里出现了一个新的请求,内容是这样的:

这不就是对应的日期嘛,不用想,请求头中肯定会有对应的月份数据,按月份来分组,达到这样的日历点击效果。果然:

点击十二月后,有了新的请求:

因为这个视频是12-31号发布的,所以十二月只有这一个数据。
点击具体的一天后:

弹幕的数据就出现了:

所以我们只需要请求对应的日期索引(以月份分组),再循环遍历发送请求就好了,这里需要登录,所以我们使用到cookie,接下来看代码编写。

二、代码编写

1.引入库

代码如下:

1
2
3
4
5
import requests
from lxml import etree
import pandas as pd
from wordcloud import WordCloud
import jieba

2.爬虫类编写

初始化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class BarrageSpider:
def __init__(self, bv):
# 需要一个bv号,在接下来的代码中进行替换操作
self.bv = bv
# 不需要登录的弹幕接口地址 只能爬取部分弹幕
self.barrage_url = 'https://comment.bilibili.com/{}.xml'
# 需要登陆的弹幕接口地址 根据日期进行分类 需要循环爬取 最后归总数据
self.date_url = 'https://api.bilibili.com/x/v2/dm/history?type=1&oid={}&date={}' # 2021-01-01
# 点击按钮弹出日历的数据接口,这里我们用来作索引
self.index_url = 'https://api.bilibili.com/x/v2/dm/history/index?type=1&oid={}&month={}' # 2021-01
# 在抓包工具中找的一个简洁的请求,里面有我们需要的oid或者是cid
self.bv_url = 'https://api.bilibili.com/x/player/pagelist?bvid=' + bv + '&jsonp=jsonp'
# 不需要登录接口的伪装头
self.comment = {
'referer': 'https://www.bilibili.com/',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
'Chrome/87.0.4280.88 Safari/537.36 Edg/87.0.664.66 '
}
# 需要登录的伪装头 因为需要登录 ip代理已经没有意义了 这里就不再使用IP代理
self.date_headers = {
"referer": "https://www.bilibili.com/",
"origin": "https://www.bilibili.com",
"cookie": "cookie",
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/87.0.4280.88 Safari/537.36 Edg/87.0.664.66 "
}

封装函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# 从接口返回的json中获取到我们的cid 注: cid = oid
def get_cid(self):
# 定位到数据data中下面的cid
return requests.get(url=self.bv_url, headers=self.comment).json()['data'][0]['cid']

# 解析不需要登录的接口 返回类型是xml文件
def parse_url(self):
# 获取指定视频的cid/oid
cid = self.get_cid()
# 对页面进行伪装请求,这里注意不要转换成text,使用二进制
response = requests.get(url=self.barrage_url.format(cid), headers=self.comment).content
# etree解析
data = etree.HTML(response)
# 定位到所有的d元素
barrage_list = data.xpath('//d')
for barrage in barrage_list:
# 获取d元素的p属性值
info = barrage.xpath('./@p')[0].split(',')
# 获取弹幕内容
content = barrage.xpath('./text()')[0]
item = {'出现时间': info[0], '弹幕模式': info[1], '字体大小': info[2], '颜色': info[3], '发送时间': info[4], '弹幕池': info[5],
'用户ID': info[6], 'rowID': info[7], '内容': content}
# 因为这只是一部分弹幕 所以就没有进行持久化存储 没有必要
print(item)

# 循环爬取所有弹幕 需要传入month的数据 根据视频发布的日期到现在的所有月份
def parse_date_url(self, month):
# 存放爬到的数据
result = []
# 获取视频的oid
oid = self.get_cid()
# 获取日期索引
date_by_month = requests.get(url=self.index_url.format(oid, month), headers=self.date_headers).json()['data']
# 根据日期索引循环请求
for day in date_by_month:
# 注意还是二进制文件
date_page = requests.get(url=self.date_url.format(oid, day), headers=self.date_headers).content
date_data = etree.HTML(date_page)
# 解析到到所有的d元素
barrage_list = date_data.xpath('//d')
# 循环解析数据
for barrage in barrage_list:
# 获取d元素的p属性值
things = barrage.xpath('./@p')[0].split(',')
# 获取弹幕内容 并去掉所有空格
content = barrage.xpath('./text()')[0].replace(" ", "")
item = {'出现时间': things[0], '弹幕模式': things[1], '字体大小': things[2], '颜色': things[3], '发送时间': things[4],
'弹幕池': things[5],
'用户ID': things[6], 'rowID': things[7], '内容': content}
result.append(item)
# 返回封装好的数据
return result

# 舍友指导下的一行代码生成词云 编译器自动格式化了 本质还是一行代码
def wordCloud(self):
WordCloud(font_path="C:/Windows/Fonts/simfang.ttf", background_color='white', scale=16).generate(" ".join(
[c for c in jieba.cut("".join(str((pd.read_csv('{}弹幕池数据集.csv'.format(self.bv))['内容']).tolist()))) if len(c) > 1])).to_file(
"{}词云.png".format(self.bv))

主函数调用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
if __name__ == '__main__':
# 输入指定的视频bv号
bv_id = input('输入视频对应的bv号:')
# new一个对象
spider = BarrageSpider(bv_id)
# 请求今年1月和去年12月的数据 并合并数据
one = spider.parse_date_url('2021-01')
two = spider.parse_date_url('2020-12')
one.extend(two)
# 数据格式化处理 并输出csv格式文件
data = pd.DataFrame(one)
data.drop_duplicates(subset=['rowID'], keep='first')
# 字符集编码需要为utf-8-sig 不然会乱码
data.to_csv('{}弹幕池数据集.csv'.format(bv_id), index=False, encoding='utf-8-sig')
# 生成词云
spider.wordCloud()

运行结果


可以看到有5000条数据。
词云如图:

注:案例仅供学习

本文标题:爬取b站视频弹幕并制作词云一

文章作者:fanchen

发布时间:2021年01月16日 - 16:45:26

最后更新:2021年02月05日 - 17:58:45

原始链接:http://88fanchen.github.io/posts/fc448f83/

许可协议:署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。