一、爬取目标-B站排行榜

大家好,我是老王!

今天给大家分享一期python爬虫案例,这次爬取的对象是:B站热门排行榜数据

爬取的目标网址是:https://www.bilibili.com/v/popular/rank/all

【python爬虫案例】利用python爬取B站热门排行榜数据附源码_B站排行榜

咱们以目标为驱动,以兴趣为导向,先来看下爬虫程序运行后得到的excel文档数据:

【python爬虫案例】利用python爬取B站热门排行榜数据附源码_B站排行榜_02

那代码是如何实现B站排行榜数据爬取的了?下面逐一讲解一下python的实现。

二、B站排行榜网站分析

我们先来分析一下页面,这个页面包含多个分类下的排行榜的数据,首先按F12打开浏览器的抓包工具。

这里跟大家说一个抓包的技巧:先清空浏览器之前的所有请求,再点击某个分类,这样请求数会非常少,便于找到我们需要的接口

【python爬虫案例】利用python爬取B站热门排行榜数据附源码_B站排行榜_03

可以看到,总共发送了2个ajax请求,我们想要的数据就在这个v2的接口里。

现在我们来分析一下这个接口的请求参数:

【python爬虫案例】利用python爬取B站热门排行榜数据附源码_B站排行榜_04

发现有5个参数,经过老王的验证:后面3个参数不需要携带,也是可以请求成功的。

而这个rid参数,是藏在一个下面这个 popular.js 代码里的,那就好办了,下面直接开始撸代码....

【python爬虫案例】利用python爬取B站热门排行榜数据附源码_B站排行榜_05

三、B站排行榜爬虫代码详解

我们把上面这段js代码复制下来,放到python代码里

categorys = [{
    "name": "全站",
    "tid": 0,
    "slug": "all"
}, {
    "name": "番剧",
    "type": "bangumi",
    "tid": 13,
    "slug": "bangumi",
    "season_type": 1
}, {
    "name": "国产动画",
    "type": "bangumi",
    "tid": 168,
    "slug": "guochan",
    "season_type": 4
}, {
    "name": "国创相关",
    "tid": 168,
    "slug": "guochuang"
}, {
    "name": "纪录片",
    "type": "cinema",
    "slug": "documentary",
    "tid": 177,
    "season_type": 3
}, {
    "name": "动画",
    "tid": 1,
    "slug": "douga"
}, {
    "name": "音乐",
    "tid": 3,
    "slug": "music"
}, {
    "name": "舞蹈",
    "tid": 129,
    "slug": "dance"
}, {
    "name": "游戏",
    "tid": 4,
    "slug": "game"
}, {
    "name": "知识",
    "tid": 36,
    "slug": "knowledge"
}, {
    "name": "科技",
    "tid": 188,
    "slug": "tech"
}, {
    "name": "运动",
    "tid": 234,
    "slug": "sports"
}, {
    "name": "汽车",
    "tid": 223,
    "slug": "car"
}, {
    "name": "生活",
    "tid": 160,
    "slug": "life"
}, {
    "name": "美食",
    "tid": 211,
    "slug": "food"
}, {
    "name": "动物圈",
    "tid": 217,
    "slug": "animal"
}, {
    "name": "鬼畜",
    "tid": 119,
    "slug": "kichiku"
}, {
    "name": "时尚",
    "tid": 155,
    "slug": "fashion"
}, {
    "name": "娱",
    "tid": 5,
    "slug": "ent"
}, {
    "name": "影视",
    "tid": 181,
    "slug": "cinephile"
}, {
    "name": "电影",
    "type": "cinema",
    "slug": "movie",
    "tid": 23,
    "season_type": 2
}, {
    "name": "电视剧",
    "type": "cinema",
    "slug": "tv",
    "tid": 11,
    "season_type": 5
}, {
    "name": "综艺",
    "type": "cinema",
    "slug": "variety",
    "season_type": 7
}, {
    "name": "原创",
    "slug": "origin",
    "tid": 0,
    "rank_type": "origin"
}, {
    "name": "新人",
    "slug": "rookie",
    "tid": 0,
    "rank_type": "rookie"
}]

然后循环遍历这个分类列表,取出我们需要的tid,然后发送请求就可以了。

不过这里有一点要注意的是:这个排行榜的接口并不是固定的,他会针对不同的板块分成3个接口。下面是分别实现的代码。

板块1:全站、动画、音乐、舞蹈、游戏、知识、科技、运动、汽车、生活、美食、动物圈、鬼畜、时尚、娱乐、影视

def parse2(tid, rank_type):
    """
    爬取:全站、动画、音乐、舞蹈、游戏、知识、科技、运动、汽车、生活、美食、动物圈、鬼畜、时尚、娱乐、影视
    @param tid: id
    @param rank_type: 类型
    @return:
    """
    # 发起请求,获得数据
    url = "https://api.bilibili.com/x/web-interface/ranking/v2"
    params = {
        'rid': tid,
        'type': 'all',
    }
    # 如果有值,则使用(针对原创、新人这2个分类)
    if rank_type:
        params['type'] = rank_type
    resp = requests.get(url, params=params, headers=headers)
    result = resp.json()

    # 解析数据:标题 链接 作者 分类 发布时间 视频时长 播放数 弹幕数 回复数 点赞数 投币数 分享数 点赞数 不喜欢数 发布位置
    rank_list = result["data"]["list"]
    title_list = []
    short_link_list = []
    author_list = []
    tname_list = []
    pubdate_list = []
    view_list = []
    danmaku_list = []
    reply_list = []
    favorite_list = []
    coin_list = []
    share_list = []
    like_list = []
    pub_location_list = []
    for rank in rank_list:
        title_list.append(rank.get("title"))    # 标题
        short_link_list.append(rank.get("short_link_v2"))  # 短链
        author_list.append(rank["owner"]["name"])  # 作者
        tname_list.append(rank["tname"])  # 分类
        pubdate = rank["pubdate"]   # 发布时间
        pubdate = datetime.datetime.fromtimestamp(pubdate)
        pubdate = pubdate.strftime('%Y-%m-%d %H:%M:%S')
        pubdate_list.append(pubdate)
        pub_location_list.append(rank.get("pub_location")) # pub_location这个键可能不存在,所以这里用get函数

        stat = rank["stat"]
        view_list.append(stat["view"])  # 播放数
        like_list.append(stat["like"])  # 点赞数
        danmaku_list.append(stat["danmaku"])  # 弹幕数
        reply_list.append(stat["reply"])  # 回复数
        favorite_list.append(stat["favorite"])  # 点赞数
        coin_list.append(stat["coin"])  # 投币数
        share_list.append(stat["share"])  # 分享数

    return {
        "标题": title_list,
        "作者": author_list,
        "链接": short_link_list,
        "分类": tname_list,
        "发布时间": pubdate_list,
        "播放数": view_list,
        "点赞数": like_list,
        "弹幕数": danmaku_list,
        "回复数": reply_list,
        "收藏数": favorite_list,
        "投币数": coin_list,
        "分享数": share_list,
        "发布位置": pub_location_list
    }

板块2:国产动画、纪录片、电影、电视剧、综艺

def parse1(season_type):
    """
    爬取:国产动画、纪录片、电影、电视剧、综艺
    @param season_type: 类型
    @return:
    """
    url = 'https://api.bilibili.com/pgc/season/rank/web/list'
    params = {
        'day': '3',
        'season_type': season_type,
    }
    resp = requests.get(url, params=params, headers=headers)
    result = resp.json()

    # 解析数据
    rank_list = result["data"]["list"]
    title_list = []
    url_list = []
    rating_list = []
    desc_list = []
    play_list = []
    view_list = []
    follow_list = []
    danmaku_list = []
    for rank in rank_list:
        title_list.append(rank.get("title"))  # 标题
        url_list.append(rank.get("url"))  # 链接
        rating_list.append(rank["rating"])  # 评分
        desc_list.append(rank["desc"])  # 更新至多少集
        play_list.append(rank["icon_font"]["text"])  # 播放数
        stat = rank["stat"]
        view_list.append(stat["view"])  # 播放数
        follow_list.append(stat["follow"])  # 关注数
        danmaku_list.append(stat["danmaku"])  # 弹幕数

    return {
        "标题": title_list,
        "链接": url_list,
        "评分": rating_list,
        "描述": desc_list,
        "播放数1": play_list,
        "播放数": view_list,
        "关注数": follow_list,
        "弹幕数": danmaku_list,
    }

板块3:番剧

def parse_fanju(season_type):
    """
    爬取番剧
    @param season_type: 类型
    @return:
    """
    url = 'https://api.bilibili.com/pgc/web/rank/list'
    params = {
        'day': '3',
        'season_type': season_type
    }
    resp = requests.get(url, params=params, headers=headers)
    result = resp.json()

    # 解析数据
    rank_list = result["result"]["list"]
    title_list = []
    url_list = []
    rating_list = []
    desc_list = []
    play_list = []
    view_list = []
    follow_list = []
    danmaku_list = []
    for rank in rank_list:
        title_list.append(rank.get("title"))  # 标题
        url_list.append(rank.get("url"))  # 链接
        rating_list.append(rank["rating"])  # 评分
        desc_list.append(rank["new_ep"]["index_show"])  # 更新至多少集
        play_list.append(rank["icon_font"]["text"])  # 播放数
        stat = rank["stat"]
        view_list.append(stat["view"])  # 播放数
        follow_list.append(stat["follow"])  # 关注数
        danmaku_list.append(stat["danmaku"])  # 弹幕数

    return {
        "标题": title_list,
        "链接": url_list,
        "评分": rating_list,
        "描述": desc_list,
        "播放数1": play_list,
        "播放数": view_list,
        "关注数": follow_list,
        "弹幕数": danmaku_list,
    }

最后,我们将获得到的数据,调用to_csv()方法导出成文件即可:

def save_to_csv(data, csv_name):
    """
    数据保存到csv
    @param dms: 弹幕列表数据
    @param csv_name: csv文件名字
    @return:
    """
    # 把列表转换成 dataframe
    df = pd.DataFrame(data)
    # 写入数据
    df.to_csv(csv_name, index=False, encoding="utf_8_sig")

注意,此处有坑!!

to_csv的时候,一定要加上参数 encoding='utf_8_sig' 的选项,不然保存后的数据会乱码,尤其是windows系统用户!

四、B站排行榜结果

这样我们就得到了B站排行榜所有分类下的csv文件了,效果图如下:

【python爬虫案例】利用python爬取B站热门排行榜数据附源码_网络爬虫_06

我们随便打开一个排行榜分类来看一下

1、电影排行榜

【python爬虫案例】利用python爬取B站热门排行榜数据附源码_网络爬虫_07

2、游戏排行榜

【python爬虫案例】利用python爬取B站热门排行榜数据附源码_python爬虫_08

3、番剧排行榜

【python爬虫案例】利用python爬取B站热门排行榜数据附源码_python爬虫_09

五、python爬虫源代码获取

我是@王哪跑,持续分享python干货,各类副业技巧及软件!

附完整python源码及csv表格数据:

点击此处,获得完整版源码

【看文末回复关键字"B站100",即可免费获取完整版B站热门排行榜python爬虫源码】