一文看懂,python抓取m3u8里ts加密视频及合成多线程写入的问题
Posted m0_68242099
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一文看懂,python抓取m3u8里ts加密视频及合成多线程写入的问题相关的知识,希望对你有一定的参考价值。
花了几天时间搞m3u8里的ts视频,还有多线程协程的处理问题。看了大量的回答,发现大多数人讲的都是个大概,具体的怎么用、什么原理没讲。今天就来带大家讲解怎么爬取m3u8里的加密ts视频。
目标网址我就不说了,怕被和谐
以《丛林奇航DB》为例吧,首先找到目标网址,再F12检查,点击网络,找到m3u8结尾的链接(找不到的话点击刷新一下)。如下图:
点击预览后会看到一连串的ts链接和以AES-128加密的key链接。如下图:
如果链接不完整的话要手动补全链接。
看到这里的话已经所找到了视频资源,接下来写代码爬取了。
首先导入库:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import time
import os
import requests
import re
import aiohttp
import asyncio
from Crypto.Cipher import AES
crypto库的安装请前往crypto安装 - 百度文库自行了解。
再来说说AES的一个解密ts视频原理,这里以爬取一部ts为例:
from Crypto.Cipher import AES
# 提取key和ts的链接地址进行访问与获取文本
key_url = 'https://pps.shanshanku.com/20211127/g8V4A0hE/1000kb/hls/key.key'
ts_url = 'https://pps.shanshanku.com/20211127/g8V4A0hE/1000kb/hls/z8WfPVdF.ts'
key = requests.get(key_url).content
ts = requests.get(ts_url).content
with open('./date/video.ts', 'wb') as file:
# 用Crupto库里的AES进行解密
crypto = AES.new(key,AES.MODE_CBC, key)
# 解密完成之后就可以写入了
file.write(crypto.decrypt(ts))
file.close()
得到视频。解密成功。没解密直接写入是无法播放的。
解密原理已经了解,接下来就是如何爬取多个ts视频了。
下面来说说aiohttp库和asyncio库进行协程的用法
# 第一步:得到一个url列表
urls = ['https://baidu.com',
'https://baidu.com',
'https://baidu.com']
# 第一步:用asyncio创建一个函数线程,用aiohttp来创建一个支持异步的访问
async def get_status(url):
# 类似session = aiohttp.ClientSession,with 前面要加async,获取东西前用await
async with aiohttp.ClientSession() as session:
async with await session.get(url) as response:
# 如果是<str>类型的就用text().<byte>类型的就用read()
text = await response.text()
return text[:100]
# 第三步:定义一个执行模块
if __name__ == '__main__':
start = time.time()
# 把第一步的列表放入函数块里并导入到tasks列表
tasks = []
for url in urls:
c = get_status(url)
# 把函数块放入线程
task = asyncio.ensure_future(c)
tasks.append(task)
# 循环事件
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
# 循环完成后进行关闭
loop.close()
print('总耗时:', time.time()-start)
运行后查看三个线程同时启动到完成后的总用时:
我们可以看到,调用线程已经成功,接下来就是怎么获取和写入了:下面对写入进行分析:
......
# 如果是<str>类型的就用text().<byte>类型的就用read()
text = await response.text()
return text[:100]
# 这里只截取了一小部分
# 增加一个函数块
# 形参规定设为t,对线程运行返回的结果进行接收
def parsel(t):
tlt = t.result()
# 直接打印tlt返回的是一个元组类型,一个线程对应一个元组,这里有三个线程
print(tlt)
# 如果元组里有多个数据时,需要转换为列表才能提取
lis = list(tlt)
with open('./date/http.txt', 'a') as file:
# 对获取的text[:100]进行写入
file.write(str(tlt))
file.close()
......
c = get_status(url)
# 把函数块放入线程
task = asyncio.ensure_future(c)
# 把parsel函数添加进去。。。。。。。。。。。。。。。。。。。。
task.add_done_callback(parsel)
tasks.append(task)
我们来看看打印效果和写入效果:
到这一步,我们已经知道怎么调用协程和解密包括写入了
假设我们这一步已经提取了所有的ts视频文件
接下来就是对ts视频进行合并与删除源文件了
合并有调用cmd和利用ffmpeg(需要安装):
调用cmd和利用ffmpge进行合成:
# 导入所需模块
import os
# 用ffmpeg进行ts视频合成
cmd = 'ffmpeg -f concat -safe 0 -i complex.txt(合成路径与命名) -c copy output.mp4(输出路径与命名)'
# 调用cmd
os.system(cmd)
这样我们就完成了ts视频的合成。
接下来附上完整的代码
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# 导入所需的库
import time
import os
import requests
import re
import aiohttp
import asyncio
from Crypto.Cipher import AES
# 创建一个URL列表
m3u8_URL = 'https://pps.sd-play.com/20220424/RN3p7Bj5/1200kb/hls/index.m3u8'
resp = requests.get(url=m3u8_URL).text
# 这里只提取300个ts视频
rst = re.findall('https:(.*?).ts', resp)[:300]
# name = re.findall('1200kb/hls/(.*?).ts',resp)[:5]
# 给文件命名,用列表进行封装
tt = ''
names = [tt.format(num) for num in range(10001, 10301)]
# print(rst)
print(names)
# 把ts视频封装进列表里
ts_list = []
for i in rst:
ts_url = 'https:'+i+'.ts'
ts_list.append(ts_url)
# 限制最大协程数用Semaphore,时间原来这里不讲了,不懂再问
concurrency = 15
semaphore = asyncio.Semaphore(concurrency)
# 用asyncio和aiphttp结合创建线程
async def get_request(url, name):
async with semaphore:
# conn = aiohttp.TCPConnector(limit=15)
async with aiohttp.ClientSession() as session:
async with await session.get(url) as response:
# 因为是byte类型,所以用read()
page_text = await response.read()
return page_text, name
def parse(t):
page_text = t.result()
# 返回得到一个元组,转换为列表,再对文件名和内容进行提取
complex = list(page_text)
na = complex[1]
all = complex[0]
# print(na)
# print(all)
# 用crypto里的AES进行解密并保存
key = b'b7d463938dcfabff'
path = 'D:\\\\thead\\\\'
with open(f'pathna.ts', 'ab') as file:
cryptor = AES.new(key, AES.MODE_CBC, key)
file.write(cryptor.decrypt(all))
print('下载完成')
if __name__ == '__main__':
start = time.time()
# 定义列表,把元素放进去并开启协程
tasks = []
for url,name in zip(ts_list,names):
c = get_request(url, name)
task = asyncio.ensure_future(c)
task.add_done_callback(parse)
tasks.append(task)
loop = asyncio.get_event_loop()
try:
loop.run_until_complete(asyncio.wait(tasks))
except:
loop.close()
# 为方便ffmpeg合并,这里创建了一个txt文件列表里面包含每个ts的名称。
for j in range(10001, 10301):
with open('D:\\\\thead\\\\list.txt', 'a') as file:
file.write(f'file j.ts' + '\\n')
file.close()
time.sleep(1)
# 调用cmd并利用ffmpeg进行合并
cmd = f'ffmpeg -f concat -safe 0 -i D:\\\\thead\\\\list.txt -c copy D:\\\\thead\\\\theads\\\\new01.mp4'
# cmd = f'copy/b D:\\\\thead\\\\* D:\\\\thead\\\\theads\\\\new01.ts'
# 合并完成后再调用cmd进行ts文件删除
os.system(cmd)
delete = f'del D:\\\\thead\\\\*'
os.system(delete)
# 整个程序运行所需的时间
print('总用时:', time.time()-start)
时间原因,还有些限制协程数就不多说了,有问题可以互相交流学习。
最后看一下效果:
好,大功告成!!!!
python抓取m3u8文件,并提取.ts文件合成视频
本节抓取手机app视频,charles抓包部分就不演示了,抓包内容如下:
可以直接抓取到.ts视频文件,但全都是视频片段,如果要抓全部的视频,就要找m3u8文件,里边有所有的视频路径,在拼接url前缀,就可以拿到正确的视频url了。
以下是代码部分:
import requests import os,sys import re #读取m3u8文件并提取.ts文件路径 url="http://f1.thishs.com/578a7600fb83e8566227a90f3bd926b4/5E64C7E6/vod2/_definst_/mp4:2020/5/0227/STP12280/STP12280.mp4/chunklist.m3u8" res=requests.get(url).text print(res) url_prifix="http://f1.thishs.com" ts=re.findall(r"/.*?\\.ts",res,flags=re.S) print(len(ts),ts) #分别获取.ts文件并以二进制保存 for i in ts: u=url_prifix+i r=requests.get(u).content print(i,u) filename=u[-4:] if u[-5:].startswith("_") else u[-5:] with open(filename,mode="wb") as file: file.write(r) #利用cmd命令将.ts文件合成为mp4格式 os.system("copy /b *.ts hello.mp4") print("转换成功")
运行程序,已经可以看到.ts文件被合成为一个.mp4格式的视频了,可以正常播放,就是视频时长不准确
以上是关于一文看懂,python抓取m3u8里ts加密视频及合成多线程写入的问题的主要内容,如果未能解决你的问题,请参考以下文章
网页视频经过m3u8 技术切片且加密的TS文件在播放时有缓存文件,但提取出来就无法播放了,这种情况
.ts视频采用m3u8方式,已知是 AES-128 为加密方式,求教