Python爬虫:从m3u8文件里提取小视频的正确操作

在网上爬取的小视频(.ts格式)打不开怎么搞?使用IDM下载有时候还会出现数据受法律保护,IDM无法下载该内容,如何解决?这篇博客就来聊聊如何正确提取m3u8文件里的.ts视频,并合成完整的.mp4格式视频。

Posted in Python onMay 14, 2021

在网上爬取的小视频(.ts格式)打不开怎么搞?使用IDM下载有时候还会出现数据受法律保护,IDM无法下载该内容,如何解决?这篇博客就来聊聊如何正确提取m3u8文件里的.ts视频,并合成完整的.mp4格式视频。

Python爬虫:从m3u8文件里提取小视频的正确操作
Python爬虫:从m3u8文件里提取小视频的正确操作

1. HLS协议与m3u8文件

HLS,即 H T T P   L i v e   S t r e a m i n g HTTP\ Live\ Streaming HTTP Live Streaming的缩写,是由苹果公司提出基于HTTP的流媒体网络传输协议。是苹果公司QuickTime X和iPhone软件系统的一部分。它的工作原理是把整个流分成一个个小的基于HTTP的文件来下载,每次只下载一些。当媒体流正在播放时,客户端可以选择从许多不同的备用源中以不同的速率下载同样的资源,允许流媒体会话适应不同的数据速率。在开始一个流媒体会话时,客户端会下载一个包含元数据的扩展 M3U (m3u8) 播放列表文件,用于寻找可用的媒体流。
  M3U8是 U n i c o d e Unicode Unicode 版本的 M3U,用 UTF-8 编码。"M3U"和"M3U8"文件都是苹果公司使用的 H T T P   L i v e   S t r e a m i n g HTTP\ Live\ Streaming HTTP Live Streaming 格式的基础,这种格式可以在 iPhone 和 Macbook 等设备播放。是一种播放多媒体列表的文件格式,文本内容是一系列媒体片段资源,顺序播放该片段资源,即可完整展示多媒体资源。其格式大致如下:

# 未加密
	#EXTM3U
	#EXT-X-VERSION:3
	#EXT-X-TARGETDURATION:8
	#EXT-X-MEDIA-SEQUENCE:0
	#EXTINF:4.000000,
	1af12fece7a000000.ts
	#EXTINF:4.320000,
	1af12fece7a000001.ts
	...
	#EXTINF:3.800000,
	1af12fece7a001155.ts
	#EXT-X-ENDLIST
	
	# 加密
	#EXTM3U
	#EXT-X-VERSION:3
	#EXT-X-TARGETDURATION:6
	#EXT-X-PLAYLIST-TYPE:VOD
	#EXT-X-MEDIA-SEQUENCE:0
	#EXT-X-KEY:METHOD=AES-128,URI="https://ts1.yuyuangewh.com:9999/20200808/1XdSSbTb/2000kb/hls/key.key"
	#EXTINF:3,
	https://ts1.yuyuangewh.com:9999/20200808/1XdSSbTb/2000kb/hls/EUtRrqJU.ts
	#EXTINF:4.72,
	https://ts1.yuyuangewh.com:9999/20200808/1XdSSbTb/2000kb/hls/HF90vrrN.ts
	...
	#EXTINF:0.24,
	https://ts1.yuyuangewh.com:9999/20200808/1XdSSbTb/2000kb/hls/b7ZLcRqT.ts
	#EXT-X-ENDLIST

  中文维基百科----HTTP Live Streaming
  中文维基百科----M3U

下面介绍几个m3u8文件中常见的标签:

标签 格式 作用
EXTM3U #EXTM3U 表明该文件是一个m3u8文件,每个m3u8文件必须将该标签放置在第一行
EXT-X-VERSION EXT-X-VERSION:<number> 表明该文件是一个m3u8文件,每个m3u8文件必须将该标签放置在第一行
EXT-X-TARGETDURATION #EXT-X-TARGETDURATION:<s> 表示每个视频分段最大的时长(单位秒)
EXT-X-PLAYLIST-TYPE #EXT-X-PLAYLIST-TYPE:<type-enum> 表明流媒体类型,VOD 表示该视屏流为点播源,因此服务器不能更改该m3u8文件;EVENT表示该视频流为直播源,因此服务器不能更改或删除该文件任意部分内容,但是可以在文件末尾添加新内容
EXT-X-MEDIA-SEQUENCE #EXT-X-MEDIA-SEQUENCE:<number> 表示播放列表第一个URL片段文件的序列号,每个媒体片段URL都拥有一个唯一的整型序列号,每个媒体片段序列号按出现顺序依次加 1,如果该标签未指定,则默认序列号从0开始
EXT-X-KEY #EXT-X-KEY:METHOD=AES-128,URI="http:xxxx",IV="xxxx" 表明视频流文件的加解密方法,METHOD表示加密方式,URI表示密钥路径,该密钥是一个 16 字节的数据,IV是一个128位的十六进制数值
EXTINF #EXTINF:<duration>,[<title>] 表示其后 URL 指定的媒体片段时长(单位为秒),duration可以为十进制的整型或者浮点型,其值必须小于或等于EXT-X-TARGETDURATION指定的值
EXT-X-ENDLIST #EXT-X-ENDLIST 表明m3u8文件的结束

  简书:m3u8 文件格式详解 作者:Whyn

2. 第三方库----m3u8

  m3u8是一个专门用于解析m3u8文件的解析器,有关库的详细操作请参阅官方示例

# 安装m3u8
	pip install m3u8
# 加载m3u8文件
	import m3u8

	# 返回一个M3U8对象
	playlist = m3u8.load(uri='http://videoserver.com/playlist.m3u8')	# url
	# playlist = m3u8.load(uri='playlist.m3u8')	# file
	print(playlist.segments)	# 打印EXT-X-KEY标签和所有的EXTINF标签:
	print(playlist.target_duration)	# 打印EXT-X-TARGETDURATION标签的值

	for key in playlist.keys:
		if key:
			# 如果视频文件加密,可以查看加密参数
			print(key.uri, key.method, key.iv)

3. 合成mp4文件

Python爬虫:从m3u8文件里提取小视频的正确操作
  本次就以这个视频为例,流程如下:
  1. 找到视频对应的.m3u8文件
  2. 解析.m3u8文件,从中提取.ts视频的url
  3. 下载.ts格式的视频
  4. 解密.ts格式的视频(如果视频流没有加密,则该步不是必须的)
  5. 合成.mp4或其他格式的视频

# 第1步,我载了m3u8文件,也可以直接使用m3u8文件对应的url
	playlist = m3u8.load(uri='./data/index.m3u8')

	# 第2步,提取URL
	for seg in playlist.segments:
        print(seg.uri)
    
    # 第3步,下载ts视频
    with open('xxxxx.ts', 'wb') as f:
        ts = get_ts(url)
        f.write(ts)
   
	# 第4步,解密
	cipher_text = pad(data_to_pad=cipher_text, block_size=AES.block_size)
    aes = AES.new(key=key, mode=AES.MODE_CBC, iv=iv)
    cipher_text = aes.decrypt(cipher_text)
    
	# 第5步,合成
	files = glob.glob(os.path.join('./video', '*.ts'))
    for file in files:
        with open(file, 'rb') as fr, open('./video_de/baitoushan.mp4', 'ab') as fw:
            content = fr.read()
            fw.write(content )

4. 完整代码

# -*- coding: utf-8 -*-
# @Time    : 2021/5/10 20:11
# @Author  : XiaYouRan
# @Email   : youran.xia@foxmail.com
# @File    : video.py
# @Software: PyCharm


from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from concurrent.futures import ThreadPoolExecutor
import requests
import m3u8
import glob
import os
import time
import logging


logging.getLogger("urllib3").setLevel(logging.WARNING)


def AESDecrypt(cipher_text, key, iv):
    cipher_text = pad(data_to_pad=cipher_text, block_size=AES.block_size)
    aes = AES.new(key=key, mode=AES.MODE_CBC, iv=iv)
    cipher_text = aes.decrypt(cipher_text)
    # clear_text = unpad(padded_data=cipher_text, block_size=AES.block_size)
    return cipher_text


headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) '
                         'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36'}


def get_ts(url):
    try:
        response = requests.get(url, verify=False)
        response.raise_for_status()
        response.encoding = 'utf-8'
        return response.content
    except Exception as err:
        print(err)
        return b''


def save_ts(url, index):
    filename = os.path.join('./video', str(index).zfill(5) + '.ts')
    with open(filename, 'wb') as f:
        ts = get_ts(url)
        f.write(ts)
    print(filename + ' is ok!')


if __name__ == '__main__':
    playlist = m3u8.load(uri='./data/index.m3u8')

	# 线程池,引入index可以防止合成时视频发生乱序
    with ThreadPoolExecutor(max_workers=10) as pool:
        for index, seg in enumerate(playlist.segments):
            pool.submit(save_ts, seg.uri, index)

    key = get_ts(playlist.keys[-1].uri)

    files = glob.glob(os.path.join('./video', '*.ts'))
    for file in files:
        with open(file, 'rb') as fr, open('./video_de/baitoushan.mp4', 'ab') as fw:
            content = fr.read()
            encontent = AESDecrypt(content, key=key, iv=key)
            fw.write(encontent)
        print(file + ' is ok!')

下载.ts文件效果如下:

Python爬虫:从m3u8文件里提取小视频的正确操作
  合成.mp4文件效果如下:

Python爬虫:从m3u8文件里提取小视频的正确操作

Python爬虫:从m3u8文件里提取小视频的正确操作

5. 结束语

Musicer开源代码仓库


Musicer音乐爬虫已经开源了哦,持续更新中,欢迎来踩来Star哦!ヾ(≧∇≦*)ヾ

Python 相关文章推荐
python re正则表达式模块(Regular Expression)
Jul 16 Python
详解Python3中yield生成器的用法
Aug 20 Python
python使用 HTMLTestRunner.py生成测试报告
Oct 20 Python
python将回车作为输入内容的实例
Jun 23 Python
python 接口实现 供第三方调用的例子
Aug 13 Python
详解使用django-mama-cas快速搭建CAS服务的实现
Oct 30 Python
opencv python在视屏上截图功能的实现
Mar 05 Python
Python3+selenium配置常见报错解决方案
Aug 28 Python
Python用摘要算法生成token及检验token的示例代码
Dec 01 Python
如何通过python检查文件是否被占用
Dec 18 Python
pdf论文中python画的图Type 3 fonts字体不兼容的解决方案
Apr 24 Python
Python IO文件管理的具体使用
Mar 20 Python
MATLAB 全景图切割及盒图显示的实现步骤
使用pandas或numpy处理数据中的空值(np.isnan()/pd.isnull())
May 14 #Python
PyQt5爬取12306车票信息程序的实现
python flask框架快速入门
如何将numpy二维数组中的np.nan值替换为指定的值
May 14 #Python
使用numpy nonzero 找出非0元素
May 14 #Python
Python机器学习之KNN近邻算法
May 14 #Python
You might like
用PHP读取IMAP邮件
2006/10/09 PHP
php实现从ftp服务器上下载文件树到本地电脑的程序
2009/02/10 PHP
php学习之function的用法
2012/07/14 PHP
CI(CodeIgniter)框架介绍
2014/06/09 PHP
php文件上传类的分享
2017/07/06 PHP
PHP设计模式之适配器模式原理与用法分析
2018/04/25 PHP
XAMPP升级PHP版本实现步骤解析
2020/09/04 PHP
jquery 图片上传按比例预览插件集合
2011/05/28 Javascript
ie8 不支持new Date(2012-11-10)问题的解决方法
2013/07/31 Javascript
触屏中的JavaScript事件分析
2015/02/06 Javascript
jQuery平滑旋转幻灯片特效代码分享
2015/09/07 Javascript
jQuery的each循环用法简单示例
2016/06/12 Javascript
JS中传递参数的几种不同方法比较
2017/01/20 Javascript
vue 里面使用axios 和封装的示例代码
2017/09/01 Javascript
vue2实现数据请求显示loading图
2017/11/28 Javascript
node(koa2) web应用模块介绍详解
2019/03/29 Javascript
vue中watch和computed为什么能监听到数据的改变以及不同之处
2019/12/27 Javascript
python装饰器使用方法实例
2013/11/21 Python
python生成tensorflow输入输出的图像格式的方法
2018/02/12 Python
python range()函数取反序遍历sequence的方法
2018/06/25 Python
python路径的写法及目录的获取方式
2019/12/26 Python
Python面向对象编程基础实例分析
2020/01/17 Python
Python sqlalchemy时间戳及密码管理实现代码详解
2020/08/01 Python
Python爬虫获取op.gg英雄联盟英雄对位胜率的源码
2021/01/29 Python
美国折扣网站:jClub
2017/08/07 全球购物
德国街头和运动文化高品质商店:BSTN Store
2017/08/26 全球购物
德国运动营养和健身网上商店:Myprotein.de
2018/07/18 全球购物
请解释一下webService? 如何用.net实现webService
2014/06/09 面试题
使用C#编写创建一个线程的代码
2013/01/22 面试题
学校食堂食品安全责任书
2014/07/28 职场文书
2014乡镇班子个人对照检查材料思想汇报
2014/09/26 职场文书
安全月宣传标语
2014/10/07 职场文书
刑事辩护授权委托书格式
2014/10/13 职场文书
我的兄弟姐妹观后感
2015/06/15 职场文书
人事部:年度述职报告范文
2019/07/12 职场文书
详解TS数字分隔符和更严格的类属性检查
2021/05/06 Javascript