爬虫实战:超详细,超级细!如何自动化爬取bilibili视频

## ***爬虫实战:如何自动化爬取bilibili视频***

## **1.**使用的python模块


import requests

import json

from multiprocessing import Pool

import os

from lxml import etree

import regex


爬取的网站:[B站](https://www.bilibili.com/)完整的代码在最后

## 2.获取我们所需要的的网站视频url


进入某个专区

比如我现在进入动画专区,现在我想要按视频热度爬取


F12可以帮我们观察网站随页数的变化

当你点击某一页,比如第二页,你就会发现在下图我所定位的6500ms 到7000ms区间中有一条信息

(当然时间区间不一定相同,主要是当你点击时,会出现一个不合群的信息,那这一般就是ajax请求。)

这其实就ajax异步请求

我们所需要的东西都在里面


点击这个请求

会出现如下界面

很明显,这个result ,这个英文单词意义太明显了。

再次点击进去


result里面一系列,我们可以观察我们所爬取的界面

来确定是否是我们所需要的的信息

很明显的确是我们所需要的,并且是一个json对象


到现在为止我们需要视频的url,title

也就是里面的arcurl,title

也就是说我们找到了正确的请求,现在我们要返回到请求头,来构造我们的请求头


我们只需要根据参数来构造请求头就可以了

其中很多参数我们并不知道是什么,但无所谓

我们只需使用控制变量法,一个一个删除然后再次请求就可以确定哪个参数是重要的,哪个不重要

这里我就不演示了,

这些参数我们可能不明白什么意思,但里面还是有一些很明显的参数,比如page,这个就是页数,pagesize就是每页的视频数量



referer为防盗链

这样我们就可以请求我们所需要的页数


url='https://s.search.bilibili.com/cate/search?'

headers={

    'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36',

    'referer': 'https://www.bilibili.com/'

}

params = {

        'main_ver': 'v3',

        'search_type': 'video',

        'view_type': 'hot_rank',

        'order': 'click',

        'copy_right': '-1',

        'cate_id': '24',

        'page': page,

        'pagesize': 20,

        'jsonp': 'jsonp',

        'time_from': '20200912',

        'time_to': '20200919'

    }

    try:

        res = session.get(url, headers=headers, params=params)

        res.raise_for_status()

    except Exception as e:

        print(e)


json.loads()可以把字符串变为json

这样我们就提取了每一页的视频信息


result = json.loads(res.text)['result']

    for info in result:

          description = info['description']

          arcurl = info['arcurl']

          bvid = info['bvid']

          tag = info['tag']

          title = info['title']


## 3.真正的爬取视频

我们再进入某个视频 F12一下


这里出现了很多后缀为m4s的请求,再根据B站视频进度条播放的特点 :陆陆续续的加载。

可以确定后缀为m4s的请求 应该是视频的某一片段。那么我们同样的只需要构造他的请求头应该可以同样的获取他的视频。


到这里他的请求参数出现了很大的变化,不在像之前那么简单易读,很多参数很难确定

所以我们换一种方式。我们可以查看他的源代码仔细观看


在他的网页源码里面居然就有视频的url,那我们就不需要解析请求头了,偷个懒直接利用xpath解析源码好吧。

好好观察一下,这个Scripts的位置就可以,然后我们只需要里面的json数据,所以我们再次定位了一下

[20:]就是这个意思,也可以用正则表达式更简单的来解决

然后B站的视频和音频是分开放的,所以需要两个url。


            try:

                response = session.get(arcurl, headers=headers)

                response.raise_for_status()

            except Exception as e:

                print(e)

            html = etree.HTML(response.content)

            video_infos = html.xpath('//head/script[5]/text()')[0][20:]

            video_json = json.loads(video_infos)

            VideoURL = video_json['data']['dash']['video'][0]['baseUrl']

            AudioURl = video_json['data']['dash']['audio'][0]['baseUrl']


## 4.文件存放

因为文件名有一定限制,有些字符不能取,但是B站视频标题可以有这些字符,所以我们要用正则来替换

违法字符,替换的字符随你 ,我选择了 and。

然后先通过os模块创建文件,

再利用BilibiliDownload函数下载


re = regex.compile('[/\\:?*><|"]')

            dirname = re.sub(' and ', title)

            if not os.path.exists('E:/Bilibili/' + dirname):

                os.makedirs('E:/Bilibili/' + dirname)

                print('目录文件创建成功!')

            # 下载视频和音频

            print('正在下载 "' + dirname + '"  视频····')

            BiliBiliDownload(homeurl=url, url=VideoURL,

                            name='E:/Bilibili' + '/' + dirname + '/' + dirname + '_Video_.mp4', session=session)

            print('正在下载 "' + dirname + '" 的音频····')

            BiliBiliDownload(homeurl=url, url=AudioURl,

                            name='E:/Bilibili' + '/' + dirname + '/' + dirname + '_Audio_.mp3',

                            session=session)


def BiliBiliDownload(url, name, session):

    session.options(url=url, headers=headers,verify=False)

    # 每次下载1M的数据

    begin = 0

    end = 1024*512-1

    flag=0

    while True:

        headers.update({'Range': 'bytes='+str(begin) + '-' + str(end)})

        res = session.get(url=url, headers=headers,verify=False)

        if res.status_code != 416:

            begin = end + 1

            end = end + 1024*512

        else:

            headers.update({'Range': str(end + 1) + '-'})

            res = session.get(url=url, headers=headers,verify=False)

            flag=1

        with open(name, 'ab') as fp:

            fp.write(res.content)

        if flag==1:

            break


我最后使用了多进程,进程池加快了下载速度

## 5.代码


import requests

import json

from multiprocessing import Pool

import os

from lxml import etree

import regex

headers={

    'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36',

    'referer': 'https://www.bilibili.com/'

}

def BiliBiliDownload(homeurl,url, name, session=requests.session()):

    session.options(url=url, headers=headers,verify=False)

    # 每次下载1M的数据

    begin = 0

    end = 1024*512-1

    flag=0

    while True:

        headers.update({'Range': 'bytes='+str(begin) + '-' + str(end)})

        res = session.get(url=url, headers=headers,verify=False)

        if res.status_code != 416:

            begin = end + 1

            end = end + 1024*512

        else:

            headers.update({'Range': str(end + 1) + '-'})

            res = session.get(url=url, headers=headers,verify=False)

            flag=1

        with open(name, 'ab') as fp:

            fp.write(res.content)

        if flag==1:

            break

def get_Video_url(session,url,page):

    params = {

        'main_ver': 'v3',

        'search_type': 'video',

        'view_type': 'hot_rank',

        'order': 'click',

        'copy_right': '-1',

        'cate_id': '24',

        'page': page,

        'pagesize': 20,

        'jsonp': 'jsonp',

        'time_from': '20200912',

        'time_to': '20200919'

    }

    try:

        res = session.get(url, headers=headers, params=params)

        res.raise_for_status()

    except Exception as e:

        print(e)

    result = json.loads(res.text)['result']

    for info in result:

        if int(info['play']) < 50000:

            description = info['description']

            arcurl = info['arcurl']

            bvid = info['bvid']

            tag = info['tag']

            title = info['title']

            try:

                response = session.get(arcurl, headers=headers)

                response.raise_for_status()

            except Exception as e:

                print(e)

            html = etree.HTML(response.content)

            video_infos = html.xpath('//head/script[5]/text()')[0][20:]

            video_json = json.loads(video_infos)

            VideoURL = video_json['data']['dash']['video'][0]['baseUrl']

            AudioURl = video_json['data']['dash']['audio'][0]['baseUrl']

            # 获取文件夹的名称

            re = regex.compile('[/\\:?*><|"]')

            dirname = re.sub(' and ', title)

            if not os.path.exists('E:/Bilibili/' + dirname):

                os.makedirs('E:/Bilibili/' + dirname)

                print('目录文件创建成功!')

            # 下载视频和音频

            print('正在下载 "' + dirname + '"  视频····')

            BiliBiliDownload(homeurl=url, url=VideoURL,

                            name='E:/Bilibili' + '/' + dirname + '/' + dirname + '_Video_.mp4', session=session)

            print('正在下载 "' + dirname + '" 的音频····')

            BiliBiliDownload(homeurl=url, url=AudioURl,

                            name='E:/Bilibili' + '/' + dirname + '/' + dirname + '_Audio_.mp3',

                            session=session)

if __name__ == '__main__':

    session=requests.session()

    url='https://s.search.bilibili.com/cate/search?'

    pool=Pool(8)

    for i in range(5):

        pool.apply_async(get_Video_url,(session,url,i+1))

    pool.close()

    pool.join()


©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,976评论 6 513
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,249评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,449评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,433评论 1 296
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,460评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,132评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,721评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,641评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,180评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,267评论 3 339
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,408评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,076评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,767评论 3 332
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,255评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,386评论 1 271
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,764评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,413评论 2 358