使用Python获取B站弹幕

学习Python爬虫的一个练习。侵权立删。

这个只是一个练习项目。没什么实际意义。。。就是获取某个自己喜欢的UP主所有视频的弹幕,然后分析一下哪个弹幕出厂次数最高。(这个目的也没实现,因为我没有获取到一个视频所有的弹幕,大家看到的话就当一乐。。。)

B站的话,基本直接获取网页基本上什么内容都没有的,全是动态加载的。因此个人知道的只有三个方法获取,一 抓包工具分析接口,获取接口API返回的数据 二 Splash作为中间代理 动态加载,返回加载好的网页 三 Selenuim(第二个个人实验失败 第三个 还未尝试。。。),因此就选择抓包工具分析接口。

一,找到UP主的空间主页。比如 我喜欢的一个UP主(这里提到会不会侵权啊)

找到返回视频信息的接口。。。然后打开下一页,分析接口参数的变化。。。
首页

https://api.bilibili.com/x/space/arc/search?mid=161419374&ps=30&tid=0&pn=10&order=pubdate&jsonp=jsonp

第二页

https://api.bilibili.com/x/space/arc/search?mid=161419374&ps=30&tid=0&pn=2&order=pubdate&jsonp=jsonp

简略的视频描述
可以发现 pn 发生了变化。。。由1变成了2其他的未变。。。末页是多少可以根据视频数/30 + 1得出。而视频数在这个接口的返回数据中包含着。也就是说,可以根据这个接口获取到UP主所有视频的简略描述信息。。。而这个数据就是json数据。怎么获取其中需要内容就不多说了。

二,查看某个视频的弹幕

这个。。。找了半天,发现了一个接口。返回的是一个xml, 而且弹幕数最大是1000,弹幕数超过1000的。可能需要登录查看历史弹幕一天一天的获取吧。。。我只是学习一下,就用这个最大返回1000弹幕的接口吧

https://api.bilibili.com/x/v1/dm/list.so?oid=157261069

这个就是某一个视频的弹幕接口。。。oid就是视频的cid,cid可以根据视频的aid获得

三,整合一下

可以根据第一步获取到的视频信息找打UP所有视频的aid,根据aid获取到所有cid,根据cid加上视频接口就可以获取到所有弹幕了。。。

代码如下。

import time

import requests
from lxml.html import etree

import pymysql


'''
四个接口
获取up主主页信息:
根据up主UID 获取出视频个数 然后计算出应该有几页。 num/30+1
https://api.bilibili.com/x/space/navnum?mid=161419374&jsonp=jsonp
直接根据这个接口也可以查看视频个数。 不过更主要作用是查看视频的相关信息集合。包括视频aid
https://api.bilibili.com/x/space/arc/search?mid=161419374&ps=30&tid=0&pn=1&order=pubdate&jsonp=jsonp

获取视频详情信息  主要包括cid  弹幕接口的id就是 cid
https://api.bilibili.com/x/web-interface/view?aid={}
{"code":0,"message":"0","ttl":1,"data":{"bvid":"","aid":84828732,"videos":1,"tid":76,"tname":"美食圈","copyright":1,"pic":"http://i1.hdslb.com/bfs/archive/bce895aa633adf97076206ad70205d356e324d86.jpg","title":"山药二牛和老板娘一起过年,做一桌简单好吃的年夜饭,这才是过年该有的样子","pubdate":1579862416,"ctime":1579862416,"desc":"过年了,山药,二牛,老板娘在家做了一桌美味的年夜饭,看着就让人流口水","state":0,"attribute":16768,"duration":295,"rights":{"bp":0,"elec":0,"download":1,"movie":0,"pay":0,"hd5":1,"no_reprint":1,"autoplay":1,"ugc_pay":0,"is_cooperation":0,"ugc_pay_preview":0,"no_background":0},"owner":{"mid":161419374,"name":"山药视频","face":"http://i0.hdslb.com/bfs/face/357b015de3b9f4c04527d4fefb844460397ac8b0.jpg"},"stat":{"aid":84828732,"view":266347,"danmaku":973,"reply":289,"favorite":555,"coin":3037,"share":169,"now_rank":0,"his_rank":0,"like":15336,"dislike":0,"evaluation":""},"dynamic":"#生活##美食圈##美食#","cid":145067294,"dimension":{"width":3840,"height":1772,"rotate":0},"no_cache":false,"pages":[{"cid":145067294,"page":1,"from":"vupload","part":"年夜饭","duration":295,"vid":"","weblink":"","dimension":{"width":3840,"height":1772,"rotate":0}}],"subtitle":{"allow_submit":false,"list":[]}}}
获取视频弹幕信息
https://api.bilibili.com/x/v1/dm/list.so?oid={}
'''

#链接MySQL数据库。。。这种方式应该是最低级的。。。但是也可以用
conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', password='123456',
                       db='bilibili',  charset='utf8')
cursor = conn.cursor()

# 视频信息列表,内部是一个字典。稍后可以尝试使用队列。
video_list = []


# 获取up主视频的aid cid title放入列表中
def get_video_cid(mid):

    # 获取视频页数
    url = 'https://api.bilibili.com/x/space/navnum?mid={}&jsonp=jsonp'.format(mid)
    response_up = requests.get(url)
    up_json = response_up.json()
    pages = int(up_json.get('data').get('video'))//30 + 1

    # 循环调用接口 每次页数不同
    for i in range(1, pages+1):
        main_api = 'https://api.bilibili.com/x/space/arc/search?mid={}' \
                  '&ps=30&tid=0&pn={}&order=pubdate&jsonp=jsonp'.format(mid, i)

        response = requests.get(main_api)
        main_dict = response.json()
        content_list = main_dict.get('data').get('list').get('vlist')
        for content in content_list:
            detail_api = 'https://api.bilibili.com/x/web-interface/view?aid={}'.format(content.get('aid'))
            response_detail = requests.get(detail_api)
            detail_dict = response_detail.json()

            # 视频信息列表中元素
            video_dict = dict()
            video_dict['aid'] = detail_dict.get('data').get('aid')
            video_dict['cid'] = detail_dict.get('data').get('cid')
            video_dict['title'] = detail_dict.get('data').get('title')
            print(video_dict.get('title'))
            video_list.append(video_dict)
            time.sleep(1)


# 根据cid以及弹幕接口获取到相关视频的弹幕
def get_save_dm(video_dict, table_name):
    """
    :param video_dict: 视频信息字典
    :return: None
    """
    time.sleep(1)   # 防止爬的过快被封掉IP

    aid = video_dict.get('aid')
    cid = video_dict.get('cid')
    title = video_dict.get('title')

    # 该接口返回的是XML数据
    dm_api = 'https://api.bilibili.com/x/v1/dm/list.so?oid={}'.format(cid)
    response = requests.get(dm_api)
    response.encoding = 'utf-8'

    # 解析XML
    tree = etree.HTML(response.content)
    dm_list = tree.xpath("//d/text()")
    for dm in dm_list:
        dm_str = str(dm)
        try:
            sql = 'INSERT INTO {}(aid, cid, title, dm_content) VALUES(%s, %s, %s, %s);'.format(table_name)
            cursor.execute(sql, (aid, cid, title, dm_str))
        except Exception:
            print(title, dm_str, "获取失败")
            continue
        else:
            print(title, dm_str, "获取成功")


if __name__ == '__main__':

    uid = int(input("请输入视频作者的uid"))
    table_name = 'dm_'+input("请输入视频作者姓名拼音")

    get_video_cid(uid)
    print(len(video_list))
    for video in video_list:
        get_save_dm(video, table_name)

    # 数据提交
    conn.commit()
    conn.close()

最终把数据存到了数据库。。。过程只需要知道UP的UID,通过手机点开详情就可以看得到。。。
数据库要提前设置好。。。
比如山药的。。。表设计

CREATE TABLE `dm_shanyao` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `aid` int(11) DEFAULT NULL,
  `cid` int(11) DEFAULT NULL,
  `title` char(255) DEFAULT NULL,
  `dm_content` varchar(1000) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=279602 DEFAULT CHARSET=utf8;

然后运行刚才的程序 输入UID和表名dm_ 后面的拼音
161419374
shanyao

操作数据库
可以看看获取到了多少弹幕 大约 27万。。。

select count(*) from dm_shanyao;
mysql> select count(*) from dm_shanyao;
+----------+
| count(*) |
+----------+
|   279601 |
+----------+
1 row in set (1.00 sec)

就可以将弹幕存进数据库(肯定不全。。。没关系),看看获取到的弹幕哪些数量是最多的。查询时间可能有点长。。。

select dm_content,count(*) from dm_shanyao group by dm_content order by count(*) desc;
mysql> select dm_content,count(*) from dm_shanyao group by dm_content order by count(*) desc limit 0,20;
+------------+----------+
| dm_content | count(*) |
+------------+----------+
| 好湿好湿   |      757 |
| 大人       |      445 |
| ???     |      433 |
| 无情铁手   |      404 |
| 哈哈哈     |      373 |
| 参见大人   |      373 |
| 蛏子       |      350 |
| 好湿       |      328 |
| 新年快乐   |      340 |
| ?         |      315 |
| 鸡你太美   |      298 |
| 来了       |      256 |
| 开花       |      250 |
| ????   |      243 |
| 哈哈哈哈   |      231 |
| 致死量     |      230 |
| 哈哈       |      220 |
| 饿了       |      211 |
| 无情铁嘴   |      199 |
| 哈哈哈哈哈 |      195 |
+------------+----------+
20 rows in set (2 min 18.84 sec)
发布了59 篇原创文章 · 获赞 11 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/dandanfengyun/article/details/104540930