初级版本
首先,我们先试着实现爬取一个视频保存到本地,随便在哔哩哔哩网站找一个视频,如我找的一个链接:https://www.bilibili.com/video/BV1Sz4y1Z7g9?from=search&seid=7693898795687978846
右键检查,点击network抓包,点击播放:
我们会看到很多的数据包,经过尝试分析我们点开带30280,会看到response响应部分就是很多我们看不懂的编码二进制数据,实际上30280数据包就是是一个纯音频文件(没有画面),而30080数据包是一个纯视频文件(没有声音),那么现在思路就有了,我们想办法把视频和音频文件都爬取下来,然后再想办法合成不就行了?
效率较低的方式,便是利用第三方库moviepy合成,准备工作需要安装moviepy,安装命令:pip install moviepy -i http://pypi.douban.com/simple/
代码实现如下:
import requests
from moviepy.editor import *
if __name__ == '__main__':
# 1.确认url
url_30080 = 'https://cn-jxnc-cmcc-bcache-06.bilivideo.com/upgcxcode/91/22/237582291/237582291_nb2-1-30080.m4s?e=ig8euxZM2rNcNbdlhoNvNC8BqJIzNbfqXBvEqxTEto8BTrNvN0GvT90W5JZMkX_YN0MvXg8gNEV4NC8xNEV4N03eN0B5tZlqNxTEto8BTrNvNeZVuJ10Kj_g2UB02J0mN0B5tZlqNCNEto8BTrNvNC7MTX502C8f2jmMQJ6mqF2fka1mqx6gqj0eN0B599M=&uipk=5&nbs=1&deadline=1605200882&gen=playurl&os=bcache&oi=3719332261&trid=aa176f00ecf448b294d69b0cf06670f8u&platform=pc&upsig=efa32de99996e526857889b34eca24a1&uparams=e,uipk,nbs,deadline,gen,os,oi,trid,platform&cdnid=4205&mid=440047762&orderid=0,3&agrr=0&logo=80000000'
url_30280 = 'https://upos-sz-mirrorcos.bilivideo.com/upgcxcode/91/22/237582291/237582291_nb2-1-30280.m4s?e=ig8euxZM2rNcNbdlhoNvNC8BqJIzNbfqXBvEqxTEto8BTrNvN0GvT90W5JZMkX_YN0MvXg8gNEV4NC8xNEV4N03eN0B5tZlqNxTEto8BTrNvNeZVuJ10Kj_g2UB02J0mN0B5tZlqNCNEto8BTrNvNC7MTX502C8f2jmMQJ6mqF2fka1mqx6gqj0eN0B599M=&uipk=5&nbs=1&deadline=1605200882&gen=playurl&os=cosbv&oi=3719332261&trid=aa176f00ecf448b294d69b0cf06670f8u&platform=pc&upsig=2ae2924502e57ee4ab092da37af359ba&uparams=e,uipk,nbs,deadline,gen,os,oi,trid,platform&mid=440047762&orderid=0,3&agrr=0&logo=80000000'
# 设置用户代理
headers_ = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36',
'Referer':'https://www.bilibili.com/video/BV1CT4y1w7Ed?from=search&seid=6176037271681914381'
}
# 2.发送请求得到响应
response_30080 = requests.get(url_30080,headers=headers_)
response_30280 = requests.get(url_30280,headers=headers_)
data_30080 = response_30080.content
data_30280 = response_30280.content
# 保存
with open('斗罗大陆_30080.mp4','wb') as f:
f.write(data_30080)
with open('斗罗大陆_30280.mp3','wb') as f:
f.write(data_30280)
"""
在拥有了一个纯视频文件,跟一个纯音频文件的情况下......
利用第三方库moviepy合成():速度很慢
"""
# 选择一个已经拥有的纯视频文件
video_obj = VideoFileClip('斗罗大陆_30080.mp4')
# 选一个对应已拥有的纯音频文件
audio_obj = AudioFileClip('斗罗大陆_30280.mp3')
# 往视频对象里面添加音频
movie_ = video_obj.set_audio(audio_obj)
# 保存
movie_.write_videofile('斗罗大陆_.mp4')
过程(效率极低,贼慢(大概5-10分钟),提前打招呼。。。建议趁这段时间听听音乐看看小视频打发一下时间~):
废了老大时间,终于跑完了,控制台结果是这样的:
在当前路径下,打开合成后的mp4文件,播放效果如下:
有声音有画面,证明我们已经成功实现了一个视频的爬取,但是使用moviepy合成音视频文件是不是太慢了,这么费时间怎么让人受的了?有没有更高效的方法?当然有,下面介绍一款强大的视频合成利器。
改进版本
准备工作:安装配置ffmpeg
那什么是ffmpeg呢?百度上给出了这样的定义:FFmpeg是一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源计算机程序。我们只需要知道ffmpeg是一款非常好用处理音视频的工具包就行了,如果想深入了解大家可以自行在网上找一些学习资料,我们今天的目的只有一个:合成音视频!
ffmpeg功能强大,但我们首先得学会对其进安装(windows下安装),很简单的,下载解压后配置好环境变量就OK了。
安装参考链接:Windows安装配置ffmpeg
安装好之后我们就可以用代码控制ffmpeg合成音视频文件了,代码编写如下:
import requests
import os
if __name__ == '__main__':
# 1.确认url
url_30080 = 'https://cn-jxnc-cmcc-bcache-06.bilivideo.com/upgcxcode/91/22/237582291/237582291_nb2-1-30080.m4s?e=ig8euxZM2rNcNbdlhoNvNC8BqJIzNbfqXBvEqxTEto8BTrNvN0GvT90W5JZMkX_YN0MvXg8gNEV4NC8xNEV4N03eN0B5tZlqNxTEto8BTrNvNeZVuJ10Kj_g2UB02J0mN0B5tZlqNCNEto8BTrNvNC7MTX502C8f2jmMQJ6mqF2fka1mqx6gqj0eN0B599M=&uipk=5&nbs=1&deadline=1605200882&gen=playurl&os=bcache&oi=3719332261&trid=aa176f00ecf448b294d69b0cf06670f8u&platform=pc&upsig=efa32de99996e526857889b34eca24a1&uparams=e,uipk,nbs,deadline,gen,os,oi,trid,platform&cdnid=4205&mid=440047762&orderid=0,3&agrr=0&logo=80000000'
url_30280 = 'https://upos-sz-mirrorcos.bilivideo.com/upgcxcode/91/22/237582291/237582291_nb2-1-30280.m4s?e=ig8euxZM2rNcNbdlhoNvNC8BqJIzNbfqXBvEqxTEto8BTrNvN0GvT90W5JZMkX_YN0MvXg8gNEV4NC8xNEV4N03eN0B5tZlqNxTEto8BTrNvNeZVuJ10Kj_g2UB02J0mN0B5tZlqNCNEto8BTrNvNC7MTX502C8f2jmMQJ6mqF2fka1mqx6gqj0eN0B599M=&uipk=5&nbs=1&deadline=1605200882&gen=playurl&os=cosbv&oi=3719332261&trid=aa176f00ecf448b294d69b0cf06670f8u&platform=pc&upsig=2ae2924502e57ee4ab092da37af359ba&uparams=e,uipk,nbs,deadline,gen,os,oi,trid,platform&mid=440047762&orderid=0,3&agrr=0&logo=80000000'
# 设置用户代理
headers_ = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36',
'Referer':'https://www.bilibili.com/video/BV1CT4y1w7Ed?from=search&seid=6176037271681914381'
}
# 2.发送请求得到响应
response_30080 = requests.get(url_30080,headers=headers_)
response_30280 = requests.get(url_30280,headers=headers_)
data_30080 = response_30080.content
data_30280 = response_30280.content
# 保存
with open('斗罗大陆30080.mp4','wb') as f:
f.write(data_30080)
print('纯视频文件下载完毕......')
with open('斗罗大陆30280.mp3','wb') as f:
f.write(data_30280)
print('纯音频文件下载完毕......')
# 利用第三方工具ffmpeg 合成视频, 需要执行终端命令
os.system('ffmpeg -i 斗罗大陆30280.mp3 -i 斗罗大陆30080.mp4 -c copy 斗罗大陆.mp4 -loglevel quiet')
# 后面的-loglevel quiet加不加无所谓,不加控制台会出现红色的log日志但不影响代码功能。
print('视频合成成功......')
可以看到瞬间就合成成功了,打开看看效果,完全不差:
我们再比较一下内存大小,同样的音视频文件使用moviepy合成的mp4文件大小为120404KB
而使用ffnpeg合成的mp4文件大小为74273KB,接近音视频大小加起来的总和,合成后的画质音质也是无损的,所以无论是从时间效率还是从空间损耗方便来讲,可以说ffmpeg完虐moviepy!
仔细想想我们只想要合成后的视频文件,分开的音视频文件没啥用,白白的占用了内存,想到这里代码还可以再优化一下,在合成后删除音视频文件即可,那我们只需要在以上代码最后加上两行删除指令的代码即可:
os.system('del 音频名称.mp3')
os.system('del 视频名称.mp4')
这两行代码调用的是操作系统的指令删除的,也可以用remove删除,即os.remove(‘音频名称.mp3’),这个无所谓,只要能达到删除目的就行。
升级版本
到目前为止,我们已经能爬取指定的视频了,但是爬一个视频我们就要解析30080,30280的数据包获取相关url对我们来说是非常不便的。我们想要达到的效果是输入目标视频的url直接就能下载视频。
开始尝试解决,
-
复制纯视频url进行全局搜索,右上角三个点,菜单栏选择search
-
把https删掉s 进行搜索,找到了该文件(存有我们纯视频url)
3. 点开那条搜索数据,点击pretty-print 格式化输出
4.点击文件区域,ctrl + f 继续搜索
发现纯视频文件的url 和纯音频文件的url都在同一个文件当中, 如果我们能够获取到这个文件的response,那么就能够提取出url进行请求,保存,合成…
该文件实际为url地址栏的url请求对应的response,那解析出视频音频文件url就简单了,我们可以使用用正则提取,其实仔细分析可以发现我们还可以指定清晰度版本,就是那个id,80,64,32等等就是对应的版本,我们这里不再赘述,直接爬取最高画质,要搞就搞最好的,编写代码如下:
import re
import os
if __name__ == '__main__':
url = input('请输入要下载的视频url:')
headers ={
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36",
"cookie": "_uuid=425019D2-6E06-C2BA-320C-989D73E268D379095infoc; buvid3=A7BFF32C-6475-484D-AD68-D6B1633AD9CE53937infoc; LIVE_BUVID=AUTO3815878009223382; rpdid=|(J~R~kuRY)l0J'ul)~)||mkJ; DedeUserID=440047762; DedeUserID__ckMd5=ad5ddee3544a770f; SESSDATA=d392227d%2C1604025506%2C3577b*51; bili_jct=883303557689d9fa49b97229f5837edd; CURRENT_QUALITY=64; PVID=1; CURRENT_FNVAL=80; blackside_state=1; bsource=search_baidu; sid=kjvz5hpk"
}
res = requests.get(url,headers =headers).text
html = etree.HTML(res)
str_url_list = html.xpath('//script[contains(text(),"window.__playinfo__")]/text()')[0]
audio_str = re.findall(r'"audio":\[{"id":30280,"baseUrl":"(.*?)"', str_url_list)[0]
vidio_str = re.findall(r'"video":\[{"id":80,"baseUrl":"(.*?)"',str_url_list)[0]
# print("音频:",audio_str)
# print("视频:", vidio_str)
headers = {
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36",
"referer":url
}
audio_response = requests.get( audio_str,headers=headers).content
vidio_response = requests.get(vidio_str, headers=headers).content
with open('音频.mp3','wb') as f:
f.write(audio_response)
with open('视频.mp4','wb')as f:
f.write(vidio_response)
os.system('ffmpeg -i 音频.mp3 -i 视频.mp4 -c copy 合成后的视频.mp4 -loglevel quiet')
time.sleep(2)
os.system('del 音频.mp3')
os.system('del 视频.mp4')
随便找一个视频复制url尝试一下,我用的这个url(斗罗大陆小姐姐):
https://www.bilibili.com/video/BV19C4y147GK?from=search&seid=7693898795687978846
下载成功:
打开合成好的视频,效果:
最终加强版本
优化:
- 问题1:我们下载下来的视频名称是否能够改成网页视频原名?
可行性分析: 网页里面的视频名称,已知网页为html数据,名称也可以使用xpath调试拿到。
- 问题2: 这个视频可以利用//span[@class=“tit”]/text() 取到视频名称
其他视频有的,xpath语法并不通用,改xpath取视频名称通用 //title/text()
需要切割后面多余的部分,可以通过xpath语法提取到名称,再进行一个切片,正则提取就可以了
-
问题3: 视频的名称中,如果包含 空格 / & 都会影响到视频的合成
解决办法, 去掉名称中的 空格 / & 等特殊字符 -
问题4:title_ = 斗罗大陆
纯视频文件: 斗罗大陆!.mp4
纯音频文件: 斗罗大陆!.mp3
合成的文件: 斗罗大陆.mp4
为了避免因为重名造成的问题,可以添加一个字符,改变一下视频名称,避免重名。 -
问题5:如何在控制台想显示文件的大小.
–1,发送请求的时候,参数添加stream=True
–2.获取大小
参考代码如下:
import requests
import os
from lxml import etree
import re
if __name__ == '__main__':
# 输入网页的url
url_ = input('请输入一个网址:')
# 设置用户代理,cookie
headers_ = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36',
'Cookie': "INTVER=1; _uuid=09E58359-9210-BAEE-1A60-4EE46A2B82C655513infoc; sid=5ytmp0pi; LIVE_BUVID=AUTO6515760552512495; stardustvideo=1; laboratory=1-1; rpdid=|(umuum~uku|0J'ul~Y|llkl); buvid3=684E90B9-B796-4512-AE61-CED715ED0D2B53931infoc; stardustpgcv=0606; finger=158939783; DedeUserID=246639322; DedeUserID__ckMd5=0de51babcf36bfe1; SESSDATA=0453e7c3%2C1613286482%2C848b7*81; bili_jct=05a501d5099630a42cb271cebdbc3470; blackside_state=1; CURRENT_FNVAL=80; CURRENT_QUALITY=80; bsource=search_baidu; PVID=4"
}
# 发送请求,得到响应对象
response_ = requests.get(url_, headers=headers_)
str_data = response_.text # 视频主页的html代码,类型是字符串
# 使用xpath解析html代码,,得到想要的url
html_obj = etree.HTML(str_data) # 转换格式类型
# 获取视频的名称
res_ = html_obj.xpath('//title/text()')[0]
# 视频名称的获取
title_ = re.findall(r'(.*?)_哔哩哔哩', res_)[0]
# 影响视频合成的特殊字符的处理,目前就遇到过这三个,实际上很有可能不止这三个,遇到了就用同样的方法处理就好了
title_ = title_.replace('/', '')
title_ = title_.replace(' ', '')
title_ = title_.replace('&', '')
# 使用xpath语法获取数据,取到数据为列表,索引[0]取值取出里面的字符串,即包含视频音频文件的url字符串
url_list_str = html_obj.xpath('//script[contains(text(),"window.__playinfo__")]/text()')[0]
# 纯视频的url
video_url = re.findall(r'"video":\[{"id":\d+,"baseUrl":"(.*?)"', url_list_str)[0]
# 纯音频的url
audio_url = re.findall(r'"audio":\[{"id":\d+,"baseUrl":"(.*?)"', url_list_str)[0]
# 设置跳转字段的headers
headers_ = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36',
'Referer': url_
}
# 获取纯视频的数据
response_video = requests.get(video_url, headers=headers_, stream=True)
bytes_video = response_video.content
# 获取纯音频的数据
response_audio = requests.get(audio_url, headers=headers_, stream=True)
bytes_audio = response_audio.content
# 获取文件大小, 单位为KB
video_size = int(int(response_video.headers['content-length']) / 1024)
audio_size = int(int(response_audio.headers['content-length']) / 1024)
# 保存纯视频的文件
title_1 = title_ + '!' # 名称进行修改,避免重名
with open(f'{title_1}.mp4', 'wb') as f:
f.write(bytes_video)
print(f'{title_1}纯视频文件下载完毕...,大小为:{video_size}KB, {int(video_size/1024)}MB')
with open(f'{title_1}.mp3', 'wb') as f:
f.write(bytes_audio)
print(f'{title_1}纯音频文件下载完毕...,大小为:{audio_size}KB, {int(audio_size/1024)}MB')
# 利用第三方工具ffmpeg 合成视频, 需要执行终端命令
os.system(f'ffmpeg -i {title_1}.mp3 -i {title_1}.mp4 -c copy {title_}.mp4 -loglevel quiet')
# 显示合成文件的大小
res_ = int(os.stat(f'{title_}.mp4').st_size / 1024)
print(f'{title_}视频合成成功...,大小为{res_}KB, {int(res_/1024)}MB......')
# 移除纯视频文件,
os.remove(f'{title_1}.mp4')
# 移除纯音频文件,
os.remove(f'{title_1}.mp3')
再随便找一个url 测试,https://www.bilibili.com/video/BV1kp4y1v7QM?from=search&seid=7693898795687978846
成功!
播放效果:
感谢各位小伙伴的阅读,如有任何问题可在评论区留言或者csdn app发私信给我,看到了一定知无不言言无不尽。。。
最后送出一些福利,感兴趣的自取:C++及Python人工智能500G+学习资源