写点什么

超详细超级细 B 站视频爬取

发布于: 2020 年 09 月 19 日

本文仅作学习研究,切勿用于商业行为

*1.*使用的python模块



import requests
import json
from multiprocessing import Pool
import os
from lxml import etree
import regex



爬取的网站:B站完整的代码在最后



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()



发布于: 2020 年 09 月 19 日阅读数: 162
用户头像

还未添加个人签名 2020.09.16 加入

还未添加个人简介

评论

发布
暂无评论
超详细超级细B站视频爬取