网页视频抓取软件 格式工厂(本文哔哩视频抓取b站视频好好分析分析(图) )

优采云 发布时间: 2022-03-30 13:06

  网页视频抓取软件 格式工厂(本文哔哩视频抓取b站视频好好分析分析(图)

)

  本文仅作为学习笔记的参考:

  哔哩哔哩视频截图

  b站的视频还是比较难抓的。与其他网站视频相比,获取难度较大。也是因为我不怕死,因为我的好奇心。我打算对b站的视频进行分析分析。具体抓包流程如下:

  初步分析页面如下:

  

  这个页面有点诱人,所以让我们从那里开始。这个页面的请求和响应信息比较容易抓取和分析,主要是获取分页标记和视频跳转到指定播放界面的url地址(如下图)

  

  我使用的方法是在response中下载页框信息,然后和原来合并的Element页面对比,找到url。这一步不难找,就不多说了,直接上代码(恩...通用代码就行了,懒得分开一点了):

  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 格式请求都是相同的。一个请求,哪个被划掉肯定不是,因为响应为null,所以请求有3种类型(30080 / 30216 / 30232).

  

  三个请求,我需要哪一个?继续分析,找了很多资料,得出的结论是返回请求数据最多的是视频格式,返回数据较少的是音频文件(如下图)

  30080 所需请求的总数据大小

  

  30216 请求的所需总数据大小

  

  30232 所需请求的总数据大小

  

  相比之下,30080 > 30232 > 30216 个数据请求

  现在可以确定30080请求的数据是视频文件,那么30232和30216这两个请求的文件中,哪个是音频文件呢?

  别慌,下面我们来分析一下。

  这里有个问题,30080文件有这么多碎片,我不能全部下载然后合成⑧,虽然这个确实可行,但是比较复杂。还有一种更简单的方法来获取整个视频文件。这需要将请求头中的Range(如下图)参数改为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

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线