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 相关文章推荐
python3.0 字典key排序
Dec 24 Python
Python3 入门教程 简单但比较不错
Nov 29 Python
Python fileinput模块使用实例
May 28 Python
浅谈django model postgres的json字段编码问题
Jan 05 Python
为什么入门大数据选择Python而不是Java?
Mar 07 Python
Python爬虫之网页图片抓取的方法
Jul 16 Python
python高级特性和高阶函数及使用详解
Oct 17 Python
Python实现按逗号分隔列表的方法
Oct 23 Python
使用IDLE的Python shell窗口实例详解
Nov 19 Python
pycharm 设置项目的根目录教程
Feb 12 Python
python批量替换文件名中的共同字符实例
Mar 05 Python
python3+openCV 获取图片中文本区域的最小外接矩形实例
Jun 02 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你的验证码安全码?
2007/01/02 PHP
收藏的一个php小偷的核心程序
2007/04/09 PHP
Javascript Request获取请求参数如何实现
2012/11/28 Javascript
js自定义方法通过隐藏iframe实现文件下载
2013/02/21 Javascript
ExtJS[Desktop]实现图标换行示例代码
2013/11/17 Javascript
使用GruntJS构建Web程序之构建篇
2014/06/04 Javascript
使用node.js 获取客户端信息代码分享
2014/11/26 Javascript
JavaScript插件化开发教程 (三)
2015/01/27 Javascript
Jquery日期选择datepicker插件用法实例分析
2015/06/08 Javascript
jquery实现的V字形显示效果代码
2015/10/27 Javascript
javascript中eval解析JSON字符串
2016/02/27 Javascript
移动端H5开发 Turn.js实现很棒的翻书效果
2016/06/20 Javascript
jQuery的Cookie封装,与PHP交互的简单实现
2016/10/05 Javascript
nodejs使用express创建一个简单web应用
2017/03/31 NodeJs
js图片加载效果实例代码(延迟加载+瀑布流加载)
2017/05/12 Javascript
JS FormData上传文件的设置方法
2017/07/05 Javascript
解决AjaxFileupload 上传时会出现连接重置的问题
2017/07/07 Javascript
微信小程序实现下拉刷新和轮播图效果
2017/11/21 Javascript
详解Vue结合后台的列表增删改案例
2018/08/21 Javascript
vue webpack开发访问后台接口全局配置的方法
2018/09/18 Javascript
vue百度地图 + 定位的详解
2019/05/13 Javascript
实例讲解JavaScript 计时事件
2020/07/04 Javascript
解决echarts图表使用v-show控制图表显示不全的问题
2020/07/19 Javascript
vue3.0生命周期的示例代码
2020/09/24 Javascript
[05:48]DOTA2英雄梦之声vol21 屠夫
2014/06/20 DOTA
[00:44]TI7不朽珍藏III——军团指挥官不朽展示
2017/07/15 DOTA
Python的Flask框架中配置多个子域名的方法讲解
2016/06/07 Python
Tensorflow的可视化工具Tensorboard的初步使用详解
2018/02/11 Python
python训练数据时打乱训练数据与标签的两种方法小结
2018/11/08 Python
使用python 打开文件并做匹配处理的实例
2019/01/02 Python
Python绘图Matplotlib之坐标轴及刻度总结
2019/06/28 Python
自定义实现 PyQt5 下拉复选框 ComboCheckBox的完整代码
2020/03/30 Python
Python xlwings插入Excel图片的实现方法
2021/02/26 Python
工程造价专业大学生职业规划范文
2014/03/09 职场文书
学生退学证明
2015/06/23 职场文书
导游词之平津战役纪念馆
2019/11/04 职场文书