受限于弹幕池的数量,没有办法可以爬取到B站更多的弹幕呢?[Python基础]

python

本文的文字及图片来源于网络,仅供学习、交流使用,不具有任何商业用途,如有问题请及时联系我们以作处理。

以下文章源于Python干货铺子 ,作者:不正经的kimol君

刚接触Python的新手、小白,可以复制下面的链接去免费观看Python的基础入门教学视频

https://v.douyu.com/author/y6AZ4jn9jwKW

 

前言

想必小破站大家都很熟悉叭,里面充满了各种神奇的视频,其中的「弹幕」成为了许多人的快乐源泉。关于它的爬虫也有很多,但大部分都受限于弹幕池的数量,只能爬取到其中很少一部分的弹幕。

那么,有没有办法可以爬取到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文件,其格式大致如下:

 

因此,需要将其中的信息提取出来,「p」标签对应的字段分别为:

 

那么,利用正则可以它们都提前出来:

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

【视频播放量】:%d

【弹幕数量】: %d

【上传日期】: %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)

爬取过程如下:

 

可以看到,一共「11471条」弹幕,我们获取到了「11000条」,占比已经很高了,而且csv文件已经安静地躺在了我的电脑中,打开它:

 

至此,大功告成,舒坦了~~

以上是 受限于弹幕池的数量,没有办法可以爬取到B站更多的弹幕呢?[Python基础] 的全部内容, 来源链接: utcz.com/z/530980.html

回到顶部