网页视频抓取软件 格式工厂(完全小白篇-使用Python爬取bv号视频哔哩哔哩号 )
优采云 发布时间: 2022-03-14 09:02网页视频抓取软件 格式工厂(完全小白篇-使用Python爬取bv号视频哔哩哔哩号
)
完全白篇——使用Python爬取bv视频
B站相信大家都很熟悉了。这次想尝试直接爬B站的视频,但是搜了很多文章的博客,看来还是在B站还在用av号的时代。,所以这次想看看能不能爬取bv号的视频。
找B站的视频
BV号是B站自2020年3月23日起升级的视频码,BV号完全替代了之前的AV号,功能不变。既然网站都用了BV号,随便找个视频先看看情况:
对于单个视频,您无需查看 BV 编号后跟大字符串的内容。单个视频就是1的p序号。对于一个系列的视频来说,BV序号是固定的,不同的剧集对应不同的p序号,所以只要确定了BV序号和p序号,就可以定位视频的网页。
分析网页信息
相信大家在来之前应该都知道网络视频是怎么播放的。我不会详细介绍这里的“url”是什么。如果要爬取视频,最重要的是能够获取视频和音频文件的url信息。以前的AV号好像是说视频和音频文件是合二为一的。BV号上线后,我们看到的视频其实是音视频分离的。因此,对于 BV 号视频,视频(.flv)和音频(.mp3) 应该一起获取。
我们先看网页的源码:
哎,我吐了,不过,直接用ctrl+F搜索,搜索“url”,
我们会在一个音频类中找到一个视频类和“baseUrl”、“base_url”、“backupUrl”、“backup_url”,其中baseUrl的内容就是对应的音视频url。所以只要拿到网页的源代码,就可以把这两个地方提取出来。
获取网页信息
1. 获取网页源代码
这一点我就不赘述了,把自己模仿成浏览器,使用请求库函数访问网页:
# 用到的库
import requests
from requests import RequestException
from lxml import etree
from contextlib import closing
from pyquery import PyQuery as pq
import re
import os
import json
import subprocess
def getHtml(baseurl):
head = { #模拟浏览器身份头向对方发送消息
"user-agent": 这个地方填写你的浏览器身份信息,这一点只需随便找个请求把对应的user-agent内容复制过来就行
}
try:
response = requests.get(url = baseurl, headers = head)
# 200表示服务器接受请求,会传回网页源代码,所以把文本内容传回来就行了
if response.status_code==200:
return response.text
except:
print("请求失败")
getHtml()函数的作用是返回这个网页的源代码,传入网页地址即可。例如上图中:“Sentence Break 5.0”
2. 下一步是处理网页的源代码
# 传入网址、p序列号。这里说明一下,我下载的视频在下载系列时直接用p号命名,方便起见所以这个函数要用p号
def getVideo(baseurl,p):
html = getHtml(baseurl)
doc = pq(html)
title = doc('#viewbox_report > h1 > span').text()
pattern = r'\window\.__playinfo__=(.*?)\'
result = re.findall(pattern, html)[0]
temp = json.loads(result)
print(("开始下载--->")+title)
title = str(p)
try:
video_url = temp['data']['dash']['video'][0]['baseUrl']
audio_url = temp['data']['dash']['audio'][0]['baseUrl']
fileDownload(homeurl=baseurl, url=video_url, title=title, typ=0)
fileDownload(homeurl=baseurl, url=audio_url, title=title, typ=1)
try:
combine(title)
except:
print("对不起,您的电脑中未安装ffmpeg,不予享受合成服务,您可以尝试使用格式工厂等其他方式\n")
# 这个是针对av号的,还没试过
except Exception:
vedio_url = temp['data']['durl'][0]['url']
fileDownload(homeurl=baseurl, url=video_url, title=title, typ=0)
关注 video_url = temp['data']['dash']['video'][0]['baseUrl']。不适合一步获取 vedio:baseUrl 的内容,毕竟源码的内容太大了。所以再走一步,即先获取整个网页代码的一个小整体,然后再细分这个小整体中的每个成员。其实这部分源码在语法上就是一个Python字典,只不过这个字典中的对应收录了小字典、列表、元组等等。所以一步一步来,找到视音频的baseUrl在哪一层相信大家都能轻松实现。
(关于vedio_url = temp['data']['durl'][0]['url'],据说音视频集成在AV号(.mp4)中,当时的源码是这个关系可以找到url。)
根据url下载资源
所以最激动人心的部分来了:下载
音频和视频的下载必须单独下载。问题是现在只有 url。我们应该做什么?
当然,回到分析页面!
1. 探索规则
上图中的操作是用谷歌浏览器查看网页->刷新网页后得到的数据流。现在它处于暂停状态。我们发现,在下半场几次偶数流后,没有其他数据回传。同时,缓冲条也停留在图中的位置。可以看出,这些统一流必然收录音视频流。点击播放后,肯定会返回视频和音频流的数据,我们正在分析。
2. 验证规则
播放后,浏览器会周期性的接收到这样一个数据流,非常有规律,所以我们先只取其中的一部分。
因此,这些流是要分析的对象。
我们先来比较一下这三者之间的区别:相同的Request URL和其他信息,重要且相关的区别在于,
猜猜,这个范围的含义也很容易看出。所谓xy大概是指数据包中收录x到y的部分。然后再回到刚刚加载网页的时候,原来这种类型的流真的有范围:bytes=0-...这个项目!
此外,当所有数据传输完成后,播放将结束。此时网站给出的http状态码基本达到206:
现在我们的下载操作很明显:
1. 冒充浏览器的身份
2. 发出固定范围请求
3. 将获取的信息写入文件
def fileDownload(homeurl, url, title, typ):
# 添加请求头键值对,写上 refered:请求来源
headers = {
"user-agent": 这个地方还是照旧填写你的浏览器身份信息
}
headers.update({'Referer': homeurl})
if typ==0:
filename = "./"+title+".flv"
else:
filename = "./"+title+".mp3"
res = requests.Session()
# 指定每次下载1M的数据
begin = 0
end = 1024*1024 - 1
flag = 0
while True:
# 添加请求头键值对,写上 range:请求字节范围
headers.update({'Range': 'bytes=' + str(begin) + '-' + str(end)})
# 获取视频分片
res = requests.get(url=url, headers=headers,verify=False)
if res.status_code != 416:
# 响应码不为416时有数据,由于我们不是b站服务器,最终那个数据包的请求range肯定会超出限度,所以传回来的http状态码是416而不是206
begin = end + 1
end = end + 1024*1024
else:
headers.update({'Range': str(end + 1) + '-'})
res = requests.get(url=url, headers=headers,verify=False)
flag=1
with open(filename, 'ab') as fp:
fp.write(res.content)
fp.flush()
if flag==1:
fp.close()
break
最后是主要功能:
def main():
print("欢迎来到bilibili爬资源小程序,接下来让我们开始吧\n(番剧爬取程序正在持续开发中...)")
bv=input("输入视频bv号: ")
judge = input("你想获得一系列(y)视频还是一个单一(N)视频?\n[y/N]\n")
if judge == "y":
max = int(input("输入该系列视频的总数: "))
for p in range(1,max+1):
baseurl = "https://www.bilibili.com/video/BV"+str(bv)+"?p="+str(p)
getVideo(baseurl,p)
else:
p=input("如果是系列视频,请任选一集输入视频p号如果不是系列视频,请输入'1'\n")
baseurl = "https://www.bilibili.com/video/BV"+str(bv)+"?p="+str(p)
getVideo(baseurl,p)
os.system("pause")
# 亲们main()肯定得调用啊,这是基本的语法知识昂,我之前没写不代表不用底下这两行话啊!各位记得自己加上这句啊2333
if __name__ == "__main__":
main()