这篇文章主要介绍了Python如何爬取到B站的弹幕,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。
本文的文字及图片来源于网络,仅供学习、交流使用,不具有任何商业用途,如有问题请及时联系我们以作处理。
想必小破站大家都很熟悉叭,里面充满了各种神奇的视频,其中的「弹幕」成为了许多人的快乐源泉。关于它的爬虫也有很多,但大部分都受限于弹幕池的数量,只能爬取到其中很少一部分的弹幕。
那么,有没有办法可以爬取到B站更多的弹幕呢?
首先,我们需要找到B站视频弹幕的接口,通过浏览器的F12调试工具抓包可以发现,其接口为:
https://api.bilibili.com/x/v1/dm/list.so?oid={oid/cid}
其实,除了这个接口之外,还有另外一个接口同样是可以获取到弹幕的:
https://comment.bilibili.com/{oid/cid}.xml
其中「oid」和「cid」是iB站给每个视频分配的一个id号,但是通常我们在浏览器地址看到的是「bvid」,因此需要做个转换:
这里相关的接口有很多,可以定义如下函数:
def get_cid(bvid):
'''
通过视频的bvid获得视频的cid
输入:视频的bvid
输出:视频的cid
'''
url = 'https://api.bilibili.com/x/player/pagelist?bvid=%s&jsonp=jsonp'%bvid
res = requests.get(url)
data = res.json()
return data['data'][0]['cid']
有了「cid」之后,我们便可以通过刚才发现的弹幕接口爬取弹幕了,代码如下:
oid = get_cid(bvid) # 这里的cid和oid是一样的
url = 'https://api.bilibili.com/x/v1/dm/list.so?oid=%d'%oid
res = requests.get(url)
res.encoding = 'utf-8'
text = res.text
注意:这里需要指定res的编码方式为utf-8,否则会出现乱码现象。
我们得到的请求为一个xml文件,其格式大致如下:
那么,利用正则可以它们都提前出来:
def parse_dm(text):
'''
解析视频弹幕
输入:视频弹幕的原数据
输出:弹幕的解析结果
'''
result = [] # 用于存储解析结果
data = re.findall('<d p="(.*?)">(.*?)</d>',text)
for d in data:
item = {} # 每条弹幕数据
dm = d[0].split(',') # 弹幕的相关详细,如出现时间,用户等
item['出现时间'] = float(dm[0])
item['模式'] = int(dm[1])
item['字号'] = int(dm[2])
item['颜色'] = int(dm[3])
item['评论时间'] = time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(int(dm[4])))
item['弹幕池'] = int(dm[5])
item['用户ID'] = dm[6] # 并非真实用户ID,而是CRC32检验后的十六进制结果
item['rowID'] = dm[7] # 弹幕在数据库中的ID,用于“历史弹幕”功能
item['弹幕内容'] = d[1]
result.append(item)
return result
通过解析requests的请求,便能得到相应的弹幕:
dms = parse_dm(text) # 解析弹幕
但是,这里受到弹幕池的限制,每次只能抓取一小部分,当弹幕数量很多时,显然这个办法行不通。
那么,我们得另寻它路了~
通过分析,我们可以找到另外一个接口,用于获取每天的弹幕历史数据:
https://api.bilibili.com/x/v2/dm/history?type=1&oid={oid/cid}&date=xx-xx
「date」为日期,通过遍历日期便可获得更多的弹幕。需要注意的是,这个接口需要登陆,因此在请求的时候必须得加入cookies,可以定义如下函数:
def get_history(bvid,date):
'''
获取视频历史弹幕
输入:视频bvid,日期
输出:视频某一日期开始的弹幕
'''
oid = get_cid(bvid)
url = 'https://api.bilibili.com/x/v2/dm/history?type=1&oid=%d&date=%s'%(oid,date)
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:82.0) Gecko/20100101 Firefox/82.0',
'Accept': '*/*',
'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
'Origin': 'https://www.bilibili.com',
'Connection': 'keep-alive',
'Referer': 'https://www.bilibili.com/video/BV1k54y1U79J',
'TE': 'Trailers'}
# 此接口需要登陆,因此需要cookies
cookies = {}
res = requests.get(url,headers=headers,cookies=cookies)
res.encoding = 'utf-8'
text = res.text
dms = parse_dm(text) # 解析弹幕
return dms
想要获得更多的弹幕,遍历每一天的弹幕数据即可,但是这样存在两个问题:「效率太低」、「数据重复」。因为,每次获取通常可以得到跨越几天的数据,所以我们无需每天都去访问,而是根据结果逐步往前推即可:
def get_dms(bvid):
'''
获取视频弹幕(此方法获取的弹幕数量更多)
输入:视频的bvid
输出:视频的弹幕
'''
print('视频解析中...')
info = get_info(bvid)
print('视频解析完成!')
print('【视频标题】: %s\n【视频播放量】:%d\n【弹幕数量】: %d\n【上传日期】: %s'%(info[0],info[1],info[2],info[3]))
dms = get_dm(bvid) # 存储弹幕
if len(dms) >= info[2]: # 如果弹幕数量已抓满
return dms
else:
dms = []
date = time.strftime('%Y-%m-%d',time.localtime(time.time())) # 从今天开始
while True:
dm = get_history(bvid,date)
dms.extend(dm)
print('"%s"弹幕爬取完成!(%d条)'%(date,len(dm)))
if len(dm) == 0: # 如果为空
break
end = dm[-1]['评论时间'].split()[0] # 取最后一条弹幕的日期
if end == date: # 如果最后一条仍为当天,则往下推一天
end = (datetime.datetime.strptime(end,'%Y-%m-%d')-datetime.timedelta(days=1)).strftime('%Y-%m-%d')
if end == info[3]: # 如果已经到达上传那天
break
else:
date = end
dm = get_history(bvid,info[3]) # 避免忽略上传那天的部分数据
dms.extend(dm)
print('弹幕爬取完成!(共%d条)'%len(dms))
print('数据去重中...')
dms = del_repeat(dms,'rowID') # 按rowID给弹幕去重
print('数据去重完成!(共%d条)'%len(dms))
return dms
运行主函数:
if __name__ == '__main__':
dms = get_dms('BV1HJ411L7DP')
dms = pd.DataFrame(dms)
dms.to_csv('一路向北.csv',index=False)
爬取过程如下:
感谢你能够认真阅读完这篇文章,希望小编分享的“Python如何爬取到B站的弹幕”这篇文章对大家有帮助,同时也希望大家多多支持亿速云,关注亿速云行业资讯频道,更多相关知识等着你来学习!
亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。
原文链接:https://my.oschina.net/u/4848094/blog/4769486