爬取B站视频信息竟如此简单!!

576 阅读6分钟

追求一个人不能全靠【感动】。

准时早安晚安,风雨无阻地送三餐,并不会让对方喜欢上你,甚至有可能会厌恶你。

更加正确的做法是提升自己,让对方感受到你的【魅力】和【踏实】。

一个女人能爱上你,是爱你的人,而不是你的跪舔。

懂得嘘寒问暖,却毫无追求,认不清现实。

每天想着各种拌饭讨好她,虽然会让她开心,但并不会让她产生喜欢,更别谈爱上你。

这种男人并不会真正给对方带来【安全感】,即使对方说了【我爱你】,也不会太长久,舔到最后只会是一场空。

获取B站视频信息

今天为大家带来的实战项目是获取B站的视频信息数据。B站的视频信息数据主要有哪些呢?

  • 视频点赞数。
  • 视频投币数
  • 视频收藏数
  • 视频弹幕数
  • 视频播放时长
  • 视频发布时间

上面所说的几点内容就是视频的主要信息数据,我们的目的就是要将这些数据获取下来,并将数据保存到数据库中。

目标网页分析

我们爬取的数据是来自《观察者网》的所有视频的数据。

对于B站的每一个视频来说每一个视频都有一个AV号。

例如,我们点击第一个视频,在里面我们就可以看到视频的各个数据了,比如说:点赞数、投币数、收藏数、弹幕数、视频播放时长、视频发布时间。

看到了吗?视频的主要数据刚刚好是在网页中呈现出来的。

为了确保数据的准确与无误,我们需要点击Network,查看Response,看看里面是不是同样也包含此类数据,如果包含相同数据的话,说明数据确实包含在网页中,而不是通过js或者是Ajax异步加载出来的,当然我更希望是通过这两个手段去渲染数据,我也容易一点。

如上图所示,数据的确包含在网页中,也就是说可以通过Python正常的进行模拟访问并获取到我们想要的数据。

分析到这里,我就先把本次爬取的代码框架先写出来吧

# 获取网页信息
def get_html(url):
    pass

# 解析数据
def parse_html(html):
    pass

# 保存数据
def save_data(data):
    pass

获取网页信息

接下来就是将网页中我们需要的数据提取出来,这里我选择使用xpath来提取数据。

相对来说xpath比较简单,具体代码如下所示:

def parse_html(html):
    html = etree.HTML(html)
    title = html.xpath('//span[contains(@class, "tit")]/text()')[0]
    danmu = int(re.findall('\d+', html.xpath('//span[@class="dm"]/text()')[0])[0])
    times = html.xpath('//div[@class="video-data"]/span[3]/text()')[0]
    like = int(re.findall('\d+', html.xpath('//div[@class="ops"]/span[@class="like"]/@title')[0])[0])
    coin =int(re.findall('\d+', html.xpath('//div[@class="ops"]/span[2]/@title')[0])[0])
    collect = int(re.findall('\d+', html.xpath('//div[@class="ops"]/span[@class="collect"]/@title')[0])[0])
    return title, danmu, times, like, coin, collect

我对上面的代码做简单的说明:

title代表的是视频的标题;

danmu代表的是视频的弹幕数;

times代表的是视频发布时间;

like代表的是是点赞数;

coin代表的是投币数;

collect代表的是收藏数。

相信xpath语法不需要我再做任何的解释了吧。

认真阅读本文的小伙伴一定可以发现这边获取的数据还差一个,那就是视频时长。经过我的仔细查找之后也没有在响应中找到视频的播放时长,说实话,我当时瞬间懵了。

于是我返回上一级链接,如下所示:

image-20210411003510573

并将页面刷新,我很幸运,终于被我给找着了。

image-20210411003906545

在一个页面中一共有30个视频,视频的标题、ID号、播放时长都在这面了。从上图可以很明显的看出,数据是通过Ajax异步加载出来的json数据。

获取所有视频的链接与时间

通过对上面的分析,我已经有了大体的思路了。三十个视频的ID号都在这里了,这个是第一页视频的所有信息,首先我必须知道它所给的API在不同的页面会有什么变化。

# 第一页
https://api.bilibili.com/x/space/arc/search?mid=10330740&ps=30&tid=0&pn=1&keyword=&order=pubdate&jsonp=jsonp

# 第二页
https://api.bilibili.com/x/space/arc/search?mid=10330740&ps=30&tid=0&pn=2&keyword=&order=pubdate&jsonp=jsonp

# 第三页
https://api.bilibili.com/x/space/arc/search?mid=10330740&ps=30&tid=0&pn=3&keyword=&order=pubdate&jsonp=jsonp

变化的参数就是pn,随着页面的变化而变化。

因此,我在这里定义了两个方法,分别获取视频的链接与视频的播放时长。

具体代码如下:

# 获取1-10页的视频链接
def get_url():
    urls = []
    # times = []
    for page in range(1, 10):
        api_url = f'https://api.bilibili.com/x/space/arc/search?mid=10330740&ps=30&tid=0&pn={page}&keyword=&order=pubdate&jsonp=jsonp'
        data = requests.get(api_url, headers=headers).json()
        bvids = jsonpath.jsonpath(data, '$.data..vlist..bvid')
        time.sleep(0.5)
        url = ['https://www.bilibili.com/video/'+bvid for bvid in bvids]
        urls.extend(url)
    return urls

# 获取1-10页的视频播放时长
def get_length():
    times = []
    for page in range(1, 10):
        api_url = f'https://api.bilibili.com/x/space/arc/search?mid=10330740&ps=30&tid=0&pn={page}&keyword=&order=pubdate&jsonp=jsonp'
        data = requests.get(api_url, headers=headers).json()
        length = jsonpath.jsonpath(data, '$.data..vlist..length')
        times.extend(length)
        time.sleep(0.5)
    return times

修改获取网页信息代码

现在我是已经成功拿到视频的播放时长了,为了方便存储,因此,需要将在获取网页信息代码中来调用获取视频播放时长的方法即可。修改之后的代码如下:

def parse_html(html):
    global pages
    lengths = get_length()

    html = etree.HTML(html)
    title = html.xpath('//span[contains(@class, "tit")]/text()')[0]
    danmu = int(re.findall('\d+', html.xpath('//span[@class="dm"]/text()')[0])[0])
    times = html.xpath('//div[@class="video-data"]/span[3]/text()')[0]
    like = int(re.findall('\d+', html.xpath('//div[@class="ops"]/span[@class="like"]/@title')[0])[0])
    coin =int(re.findall('\d+', html.xpath('//div[@class="ops"]/span[2]/@title')[0])[0])
    collect = int(re.findall('\d+', html.xpath('//div[@class="ops"]/span[@class="collect"]/@title')[0])[0])

    return title, danmu, times, like, coin, collect, lengths[pages]

因为,获取的播放时长,是放在一个列表中,因此,我在定义了一个全局变量:pages,当我调用完**save_data()**方法之后使pages加1即可。

这个pages其实表示的是每个视频的播放时长。

保存数据

本次保存的数据是放在MySQL数据库中,具体代码如下所示:

def save_data(data):
    host = 'localhost'
    user = 'root'
    password = '密码'
    port = 3306
    db = pymysql.connect(host=host, user=user, password=password, port=port, db='bilibli')
    cursor = db.cursor()
    sql = 'insert into data3(title, danmu, vediotime, likecount, coin, collect, vediolength) values (%s, %s, %s, %s, %s, %s, %s)'
    print(data)

    try:
        cursor.execute(sql, data)
        db.commit()
        print('插入成功')
    except Exception as e:
        print(e)
        db.rollback()
        print('插入失败')

注意在此之前必须要先创建好数据库bilibli和数据表data3,然后再调用上面的方法,将数据保存至数据库中。

本篇文章只做简单的爬虫知识,不做数据分析。

以本文为例,各位小伙伴们可以去爬取你们喜欢的UP主的视频详细数据,可以多爬取一个数据,比如说播放量,做B站视频Top100的数据分析,来巩固自己的爬虫知识和技巧,顺便再学习了简单的数据可视化知识。

最后

别人的建议只是参考答案,人生没有标准答案。

你不需要去抄别人,逼自己和别人一模一样,写你觉得对的答案就好。

看完文章,觉得对你有帮助的,可以给啃书君点个【在看】,我会继续努力,和你们一起成长前进。

文章的每一个字都是我用心敲出来的,只希望对得起每一位关注我的人。

点个【在看】,让我知道你们也在为人生【拼尽全力】。

我是啃书君,一个专注于学习的人,你懂的越多,你不懂的越多。更多精彩内容,我们下期再见!

respect