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

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

以下文章源于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\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)

爬取过程如下:

 

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

 

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

python爬虫与项目实战,网络爬虫是一个自动提取网页的程序,它为搜索引擎从万维网上下载网页,是搜索引擎的重要组成。 随着网络的迅速发展,万维网成为大量信息的载体,如何有效地提取并利用这些信息成为一个巨大的挑战。搜索引擎(Search Engine),例如传统的通用搜索引擎AltaVista,Yahoo!和Google等,作为一个辅助人们检索信息的工具成为用户访问万维网的入口和指南。但是,这些通用性搜索引擎也存在着一定的局限性,如: (1)不同领域、不同背景的用户往往具有不同的检索目的和需求,通用搜索引擎所返回的结果包含大量用户不关心的网页。 (2)通用搜索引擎的目标是尽可能大的网络覆盖率,有限的搜索引擎服务器资源与无限的网络数据资源之间的矛盾将进一步加深。 (3)万维网数据形式的丰富和网络技术的不断发展,图片、数据库、音频、视频多媒体等不同数据大量出现,通用搜索引擎往往对这些信息含量密集且具有一定结构的数据无能为力,不能很好地发现和获取。 (4)通用搜索引擎大多提供基于关键字的检索,难以支持根据语义信息提出的查询。 网络爬虫 为了解决上述问题,定向抓取相关网页资源的聚焦爬虫应运而生。聚焦爬虫是一个自动下载网页的程序,它根据既定的抓取目标,有选择的访问万维网上的网页与相关的链接,获取所需要的信息。与通用爬虫(general purpose web crawler)不同,聚焦爬虫并不追求大的覆盖,而将目标定为抓取与某一特定主题内容相关的网页,为面向主题的用户查询准备数据资源。 传统爬虫从一个或若干初始网页的URL开始,获得初始网页上的URL,在抓取网页的过程中,不断从当前页面上抽取新的URL放入队列,直到满足系统的一定停止件。聚焦爬虫的工作流程较为复杂,需要根据一定的网页分析算法过滤与主题无关的链接,保留有用的链接并将其放入等待抓取的URL队列。然后,它将根据一定的搜索策略从队列中选择下一步要抓取的网页URL,并重复上述过程,直到达到系统的某一件时停止。另外,所有被爬虫抓取的网页将会被系统存贮,进行一定的分析、过滤,并建立索引,以便之后的查询和检索;对于聚焦爬虫来说,这一过程所得到的分析结果还可能对以后的抓取过程给出反馈和指导。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值