网页视频抓取软件 格式工厂(本文哔哩视频抓取b站视频好好分析分析(图) )
优采云 发布时间: 2021-11-29 02:02网页视频抓取软件 格式工厂(本文哔哩视频抓取b站视频好好分析分析(图)
)
本文仅作为学习笔记的参考:
哔哩哔哩视频截图
拍b站的视频还是比较困难的。与其他网站视频相比,获取难度相对较高。也正是因为好奇,才不怕死。我打算对b站的视频进行分析分析。具体抓包过程如下:
初步分析页面如下:
这个页面有点诱人,所以让我们从这个开始。这个页面的请求和响应信息比较容易抓取和分析,主要是获取视频的分页标记和url地址跳转到指定的播放界面(如下图)
我使用的方法是下载响应中的页框信息,然后与原创合并的Element页面进行比较,找到url。这一步不难找,就不多说了,直接上代码(en...general code)这样就懒得分开一点了):
html_str01 = self.get_request(self.index_url, self.index_headers).content.decode("utf-8")
seconed_url_list = set(
re.findall(r'''href="//(www\.bilibili\.com/video/.*?\?from=search)"''', html_str01, re.S))
接下来,找到初始 url 后,我们可以分析原创视频捕获:
下面来此页面进行分析:
通过抓包,我们无法获取完整的视频请求地址,而是获取了大量.m4s格式的文件请求。最初的猜测是.m4s格式的文件就是我们需要的视频文件,但是从数量上来说,它是一个视频片段。但是,并非所有 .m4s 格式请求都相同。它们大致分为(如下图)。对于每个请求,划掉哪个肯定不是,因为响应为空,所以剩下3种请求(30080 / 30216 / 30232).
三种请求,我需要哪一种?继续分析,查了很多资料,得出的结论是返回的请求数据是视频格式,返回的数据是音频文件(如下图)
30080 所需的请求总数据大小
30216 所需的总请求数据大小
30232 所需请求的总数据大小
相比之下,数据请求量30080>30232>30216
现在可以确定30080请求的数据是视频文件,那么请求的两个文件30232和30216中哪个是音频文件呢?
别慌,接下来继续分析。
这里有问题。30080文件里有这么多碎片。我不能全部下载然后合成它们。虽然这确实可行,但比较复杂。有一个更简单的方法来获取整个视频文件,这个需要请求头中的Range(如下图)把参数改成bytes 0-XXXXXXXX,这里的XXXXXXXX就是我们上面分析的url的总和数据请求类型的数量,根据我的分析,这个值只能大于等于数据请求总数,不能小于。所以有两种方法可以获得完整的 .m4s 格式。一种是将Range的值写在请求头中作为窃贼,另一种是请求0-5等比较短的数据并返回。探头。得到响应头后,
在此分析的基础上,对比刚刚不准确的30232和30216格式的url请求,分别测试获取两种请求得到的数据进行对比。如图所示:
我们下载了两个请求的数据并将它们保存为 .mp3 文件格式。通过本次测试,两个文件都是视频和音频文件,两者没有区别。因此,我们要求稍小一些的 30216。
这样就可以完成对视频和音频文件的获取了,但是这里是单独下载的,如果需要合成,这里是我试过的两种方法:
[1] 使用ffmpeg模块完成视音频合成
[2] 使用格式工厂
我在这个程序中没有使用ffmpeg进行合成有两个原因。一个是因为太慢了,在转换过程中我的笔记本cpu占用率上升到了不可思议的99%,另一个是因为格式工厂好用,合成速度极快。因此,我选择了手动格式工厂来合成视频和音频。
至此,整个分析过程就结束了,剩下的就是编写整个程序了。各个模块怎么写这里就不详细讲了,直接提供代码截图和运行截图。
运行截图如下:
部分代码正在进行保存结果显示:
视频播放显示: ✔ 插入一句话,就是高清,没错
因此,本程序的介绍到此结束:
部分代码如下:
部分源代码
def run(self):
# print("第一次请求开始。。。。。")
html_str01 = self.get_request(self.index_url, self.index_headers).content.decode("utf-8")
# file_name = re.findall(r'''(.*?)''', html_str01, re.S)[0] + ".mp4"
seconed_url_list = set(
re.findall(r'''href="//(www\.bilibili\.com/video/.*?\?from=search)"''', html_str01, re.S))
# print(seconed_url_list)
for seconed_url in seconed_url_list:
html_str02 = self.get_request("http://" + seconed_url, self.index_headers).content.decode("utf-8")
try:
m4s_30080 = re.findall(r'''"baseUrl":"(.*?)"''', html_str02, re.S)[0]
except Exception as e:
print(e)
continue
if self.audio_condition == 'Y':
mp3_30216 = re.findall(r'''"baseUrl":"(.*?)"''', html_str02, re.S)[-2]
# print(m4s_30080)
Referer_key = seconed_url
# 试探请求头大小
Range_key = 'bytes=0-5'
self.seconed_headers['Referer'] = 'https://' + Referer_key
self.seconed_headers['Range'] = Range_key
# 试探,取得total的值
html_bytes = self.get_request(m4s_30080, headers=self.seconed_headers).headers['Content-Range']
if self.audio_condition == 'Y':
audio_bytes = self.get_request(mp3_30216, headers=self.seconed_headers).headers['Content-Range']
# print(html_bytes)
total = re.findall(r"/(.*)", html_bytes, re.S)[0]
if self.audio_condition == 'Y':
audio_total = re.findall(r"/(.*)", audio_bytes, re.S)[0]
# print("total: " + str(total))
self.seconed_headers['Range'] = total
# print(total)
stream = True
chunk_size = 1024 # 每次块大小为1024
content_size = int(total)
if self.audio_condition == 'Y':
content_size_audio = int(audio_total)
print("文件大小:" + str(round(float((content_size + content_size_audio) / chunk_size / 1024), 4)) + "[MB]")
else:
print("文件大小:" + str(round(float(content_size / chunk_size / 1024), 4)) + "[MB]")
start = time.time()
m4s_bytes = self.get_request(m4s_30080, headers=self.seconed_headers, stream=stream)
self.write_data(str(self.num) + ".mp4", m4s_bytes, chunk_size, content_size)
if self.audio_condition == 'Y':
print("\n")
self.seconed_headers['Range'] = audio_total
mp3_bytes = self.get_request(mp3_30216, headers=self.seconed_headers, stream=stream)
self.write_data(str(self.num) + ".mp3", mp3_bytes, chunk_size, content_size_audio)
end = time.time()
print("总耗时:" + str(end - start) + "秒")
self.num = self.num + 1