【Python网络爬虫实战篇】在各大主流视频网站下载高清源视频:爬取m3u8链接解析为ts视频合并成mp4视频分析实战
标签: Python专栏 网络爬虫专栏 python 编程语言 爬虫 经验分享
这两天博主在摸鱼时,偶然间接触到了流媒体的概念,一时间来了兴致。再加上之前博主有着七、八年的视频制作经验,深知视频素材获取的不易。因此,打算利用自己所学的python网络爬虫的知识,通过编写代码实现获取视频解析m3u8链接完成视频的下载功能。
流媒体
流媒体的介绍
流媒体:从远程服务器传输过来的文件流(分段传输,例如:.ts视频)。
流媒体的分类
1.伪流媒体:边下边存,会保存下来,渐进式下载。
- 特征:能够暂停、看到时间(时长)、快进、后退。
- 协议:http/https。
2.实时流媒体:边下边播,不会保存,看不到时间。
- 协议:HLS(苹果端,流媒体的传输协议)/RTMP协议(Adobe,实时消息传输协议)
- 框架:ffmpeg(底层由纯C语言编写,用来进行解码的音视频播放器框架)
- 实时流媒体图示:

其中:
① ts:高清单独编辑码的视频文件。
② m3u8:记录文件的文件,ts的顺序记录在m3u8文件中。
- 视频合成指令(需要按照顺序命名,多个ts合并成一个ts):
cat *.ts>hecheng.mp4 # mac
copy /b *.ts hecheng.mp4 # windows
分析实战
编码过程
1.通过selenium的无头模式获取视频名称和m3u8长链接。
from selenium import webdriver
url = 'http://jx.618g.com/?url=https://v.qq.com/x/cover/0pj8vuntnocu797/o0034hawh6r.html'
# 开启无头模式提取视频m3u8路径地址
chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument('--headless')
browser = webdriver.Chrome(options=chrome_options)
videoAnalysis = browser.get(url)
videoName = browser.title # 获取当前页面title值
print(videoName)
videoSrcAll = browser.find_element_by_id('player').get_attribute('src')
print(videoSrcAll)
browser.close()

2.观察输出结果,发现我们所需要的m3u8链接的url应该是获取到的长链接去除http://jx.618g.com/m3u8-dp.php?url=的url,因此,我们只需要保留等于号后面的内容即可。
# 链接分割取出真正的m3u8
videoSrc = str(videoSrcAll).split('=')[1] # 取出“=”分割的右半部分
print(videoSrc) # 输出我们所需的url
res = requests.get(videoSrc).text
print(res) # 输出我们所需的url的内容

加上上述代码观察输出结果,发现我们已经成功解析出了我们所需要的m3u8链接的url。其中输出的url内容如下所示:
#EXTM3U
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=800000,RESOLUTION=1080x608
1000k/hls/index.m3u8
url的内容表示m3u8链接作了跳转,这个只是将文件设置为二次访问获取真实地址。因此我们只需将m3u8链接改为 https://iqiyi.cdn27-okzy.com/20200802/6686_1e6ee0d8/1000k/hls/index.m3u8 再次请求访问内容才能得到真正的m3u8文件内容。
3.将链接复制粘贴到浏览器,浏览器会自动下载m3u8文件,那么我们可以以此为模板,将刚刚第2步获取到的m3u8链接与其内容的第三行进行整合。
getShortSrc = res.split('\n')[2] # 获取文件第三行内容
print(getShortSrc)
noindexurl = url.replace('index.m3u8', '') # 删除url的index.m3u8
trueUrl = noindexurl + getShortSrc # 得到m3u8二次链接(真正的m3u8链接)
print(trueUrl)

4.整合成功后,我们对真正的m3u8链接进行请求。
res1 = requests.get(trueUrl).text
print(res1)

观察m3u8文件内容,其中第一行 “#EXTM3U” 表明这是一个 m3u8 格式的视频文件,“#EXTINF”后面的一行链接便是每一个视频流的文件地址。如果将链接输入到浏览器中访问,你将得到一个以 .ts 结尾的几秒钟视频文件。这个m3u8文件中所有的链接全部请求合并得到的就是一个完整的视频。
有时候得到的 m3u8 文件不一样,会有一行 “#EXT-X-KEY” ,说明这个视频是经过加密的,需要我们去解密才能得到视频,否则得到的视频文件打开就会报错。本次实战的VIP视频解析网站解析出来的m3u8链接经过测试并未进行加密,因此不进行加密情况的讨论。
5.接下来,我们需要提取出每一个ts视频链接并保存到列表中。根据第4步观察的ts视频名称,我们需要按照第3步的整合方法ts视频名称前加上前缀“https://iqiyi.cdn27-okzy.com/20200802/6686_1e6ee0d8/1000k/hls/”,这样整合而成的ts链接才是真正下载下来能观看的高清单独编辑码的视频文件。(注意:ts链接前缀和m3u8链接前缀相同,不同的只是将index.m3u8改成ts名称)之后,将ts链接全部保存在一个列表中。
tslist = re.findall('EXTINF:(.*),\n(.*)\n#', res1) # 得到每一个ts视频名称
newlist = []
for i in tslist:
newlist.append(i[1]) # 将ts视频名称添加到列表中
# print(newlist) # 输出列表
noindextrueurl = trueUrl.replace('index.m3u8', '') # 删除trueurl的index.m3u8
tslisturl = [] # 构造链接空列表
for i in newlist:
tsurl = noindextrueurl + i
tslisturl.append(tsurl)
print(tslisturl) # 输出列表

6.获取所有的ts链接并合并成mp4视频。这一步在本次实战中由于我是用普通文件二进制追加(ab+)的形式合并的,因此速度较慢。建议有能力的同学尝试改用多线程,因为我还没学习多线程这一块暂时就这样了,未来会进行多线程的改写。
def downloadvideo(urllist, videoName):
header = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36'
}
path = './' + videoName.strip() + ".mp4"
videolen = len(urllist)
for i in urllist:
print("视频下载中...剩余" + str(videolen) + "个ts视频未下载!")
videolen = videolen - 1
r = requests.get(i, header).content
# tsname = i[-9:-3] + '.ts'
with open(path, 'ab+') as f:
f.write(r)
# f.close()
print("视频下载完毕!")
整体代码
from selenium import webdriver
import requests
import re
def getvideourl(url):
# 开启无头模式提取视频m3u8路径地址
chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument('--headless')
browser = webdriver.Chrome(options=chrome_options)
browser.get(url)
videoName = browser.title # 获取当前页面title值
print(videoName) # 输出视频名称
videoSrcAll = browser.find_element_by_id('player').get_attribute('src')
# print(videoSrcAll) # 输出视频m3u8长链接(带前缀)
browser.close()
# 链接分割取出第一个m3u8链接
videoSrc = str(videoSrcAll).split('=')[1] # 取出“=”分割的右半部分
# print(videoSrc) # 输出视频m3u8第一个链接
res = requests.get(videoSrc).text
# print(res) # 输出所请求的m3u8第一个链接中的内容
getShortSrc = res.split('\n')[2]
# print(getShortSrc) # 获取m3u8第一个链接中的内容的第三行
noindexurl = videoSrc.replace('index.m3u8', '') # 删除第一个m3u8链接的index.m3u8
# 得到m3u8二次链接(真正的m3u8链接)
trueUrl = noindexurl + getShortSrc
# print(trueUrl) # 输出真正的m3u8链接(第二个m3u8链接)
res1 = requests.get(trueUrl).text
# print(res1) # 输出真正的m3u8链接的内容(第二个m3u8链接)
tslist = re.findall('EXTINF:(.*),\n(.*)\n#', res1) # 得到每一个ts视频链接
newlist = []
for i in tslist:
newlist.append(i[1])
# print(newlist) # 输出列表
noindextrueurl = trueUrl.replace('index.m3u8', '') # 删除trueurl的index.m3u8
tslisturl = []
for i in newlist:
tsurl = noindextrueurl + i
tslisturl.append(tsurl)
print(tslisturl) # 输出列表
return tslisturl, videoName
def downloadvideo(urllist, videoName):
header = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36'
}
path = './' + videoName.strip() + ".mp4"
videolen = len(urllist)
for i in urllist:
print("视频下载中...剩余" + str(videolen) + "个ts视频未下载!")
videolen = videolen - 1
r = requests.get(i, header).content
# tsname = i[-9:-3] + '.ts'
with open(path, 'ab+') as f:
f.write(r)
# f.close()
print("视频下载完毕!")
if __name__ == '__main__':
getVideoUrl = input("请输入视频链接:") # https://v.qq.com/x/cover/0pj8vuntnocu797/o0034hawh6r.html
url = 'http://jx.618g.com/?url=' + getVideoUrl
tslisturl,videoName = getvideourl(url) # 获取ts视频列表
downloadvideo(tslisturl, videoName) # 下载视频
总结
本次实战由于采用普通文件二进制追加(ab+)的形式将ts视频合并成mp4视频,因而耗费时间较长,所以不再展示最终效果图。但是经过亲身测试,已经证明了代码的可行性,只是实用性不强(下载太慢),未来会更新为多线程下载方式。本次对视频的爬取下载,只是为了学习了解m3u8文件的构造。总体来说较为成功。
智能推荐
mp4视频分片生成m3u8流文件并加密
目录 场景描述 加密准备 视频分片 Java代码实现 场景描述 相信大家都有这样的经历,一个视频太大,放到服务器上面,播放的时候,受服务器宽带和自己网络的影响访问会很慢。 经常看视频的小伙伴肯定看到过下面的场景,网页上视频播放的时候,...
python实现将m3u8格式转换为mp4视频格式
开发动机:最近用手机QQ浏览器下载了一些视频,视频越来越多,占用了手机内存,于是想把下载的视频传到电脑上保存,可后来发现这些视频都是m3u8格式的,且这个格式的视频都切成了碎片,存在电脑里不方便查看,于是想把它转换为其他可以直接打开播放的完整视频,到网上找了一些工具,都不怎么好用,后来发现一个手机端的&ldquo...
web安全简易规范123
web安全,大公司往往有专门的安全开发流程去保证,有专门的安全团队去维护,而对于中小网络公司,本身体量小,开发同时兼带运维工作,时间精力有限,但是,同样需要做一些力所能及的必要的事情。有时候,安全威胁并不是因为你的防盗窗被人撬开了,而是你晚上睡觉的时候忘了关门,而关上门对开发来说也许只是举手之劳。 1、不要用root,确定使用的中间件和框架是否默认打开了后门 我们总会在线上使用部署一些中间件、开源...
html5拖放--15行js代码实现两个div内容互换
本文首发于我的个人博客:http://cherryblog.site/ ,欢迎大家前去参观 本文项目地址,sortable插件地址:https://github.com/sunshine940326/sortable demo地址:https://github.com/sunshine940326/drag 在写我们后台的管理程序中需要有一个拖放的功能,然后我们有一个这样的功能,实现11个固定且大...
猜你喜欢
git切换分支报错,不管什么标题名字,都报非法字符,所以就不起名字了。
切换分支的时候,报了标题这么个错误,error: ”xxx did not match any file(s) known to git. 看见不能切换分支,我首先 git status 查看了一下当前状态,如下图 然后,就会发现,其实我的这个错误非常明显,就是在我的 beat 分支下有文件修改,所以切换不了。ok,解决方法: 1. 如果修改的这些文件没什么用,完全可以删除。(我这儿的...
Oracle分析函数之LEAD和LAG实际应用
Oracle分析函数之LEAD和LAG实际应用 在前几天的工作中按照客户的需求,需要对客户信息进行数据分析,即某人存在多个状态的账号,将客户信息账号状态分析出结果,和客户确认汇报,根据保留规则,保留唯一账号,以保证程序可用性。起初,根据聚合函数进行查询分析,需要写一大串的SQL,即不美观又复杂,很容易产生错误。后续想到Oracle分析函数中的lead和lag,SQL简洁了很多且容易产生报告数据。 ...
小知识积累(不断更新中)
判断变量的类型(数组,对象) tyopof:不推荐,因为无法区别数组与对象,数组是对象的子对象 instanceof:可以使用 还可以用来判断是否属于函数 Object.prototype.toString.call():最兼容,推荐使用 定时器的执行顺序或机制 js是单线程的,浏览器遇到setTimeout或者setInterval会把定时器推入浏览器的待执行事件队列里面但是不执行,先执行完当前...
ROS自学实践(6):ROS进行激光SLAM建图——gmapping
本节主要记录运行ROS自带的SLAM建模包gmapping方法,为后续理解这些代码,建立自己的SLAM算法打下基础。 基于粒子滤波算法 二维栅格地图 需要里程计信息 1.通过命令行安装gmapping包 2.配置gmapping节点 3.运行gazebo模型及gmapping节点 4.打开rviz 添加laserscan、map、robotmodel模型 5.移动小车,建立模型 6.保存当前地图 ...
face-api.js中加入MTCNN:进一步支持使用JS实时进行人脸跟踪和识别
如果你现在正在阅读这篇文章,那么你可能已经阅读了我的介绍文章(JS使用者福音:在浏览器中运行人脸识别)或者之前使用过face-api.js。如果你还没有听说过face-api.js,我建议你先阅读介绍文章再回来阅读本文。 和往常一样,本文中为你准备了一个代码示例。我们将解析一个小的应用程序,这个程序将在浏览器中访问摄像头图像执行实时人脸检测和人脸识别,让我们开始吧! 使用face-api.js进行...
