目录
加密类型
如下图所示,该m3u8文件采用了AES的加密方式,这个加密的密钥就在这个URI中(根据情况不同密码获取会有略微差别)
然后我们复制URI中的网址到浏览器,会发现他直接下载了一个key.key的文件,然后我们将后缀名进行修改,更改为txt类型,密码就在txt文件中显示出来了。
需要的库
然后就是去对每一个ts文件进行解码,一般m3u8文件的加密采取AES加密方式,在这里我们需要用到python中的几个库,分别去下载一下
# 这两个是AES解码
pip install pycryptodome
pip install pycrypto
# 这个是对文件夹进行操作
pip install shutilwhich
# 这个是爬取信息用
pip install requests
# 这个用来合并视频,注意:ffmpy的使用首先要安装ffmpeg环境
pip install ffmpy3
# 这个用来模拟进程,进度条显示的作用
pip install tqdm
总体思路
仅提供个人对处理加密型m3u8的思路
解码代码
from Crypto.Cipher import AES
import requests
key="你文件解析用的密码"
cryptor = AES.new(key.encode('utf-8'), AES.MODE_CBC)
# 以字节流形式写入ts文件
with open('你的ts文件名.ts', 'ab') as code:
headers={
"你的请求头信息"
}
# 也可以不添加请求头
datas = requests.get("你ts文件所在的url",headers=headers).content
code.write(cryptor.decrypt(datas))
# 如果你的ts在本地就先读取数据再重新写入
实例代码
本代码只是作为例子使用,并不能保证对所有的m3u8文件都起作用
import os
import re
import shutil
import requests
from ffmpy3 import FFmpeg
from urllib import request
from Crypto.Cipher import AES
from tqdm import tqdm #这个库用来反应进程,相当于进度条,可以不要
# 1.获取密码
def get_key(name):
"""
根据m3u8内容的不同,有的可能需要在网页中的js文件中寻找,正则匹配可能也会有所变化,请根据自己的实际代码进行操作
:param name: 自己的m3u8文件名称,不用加后缀“.m3u8”
:return: 返回这个AES的解码密码
"""
with open(f"Temporary\\{name}.m3u8","r") as m3u8:
text=m3u8.read()
keypath=re.findall('URI="(.+)"',text)
request.urlretrieve(keypath[0], f'{os.getcwd()}\\Temporary\\{name}.txt')
with open(f"Temporary\\{name}.txt","r") as key:
password=str(key.read())
return password
# 2.下载m3u8文件中的ts
def download_ts(key,name,headers,times):
"""
先下载部分ts文件到tslib文件夹中,然后先将这一部分内容进行合并存入together文件夹下,然后将together文件夹中的ts文件全部合并成MP4文件
:param key: 用来给ts文件解码用的密码
:param name: m3u8文件的名字,可以自己定
:param headers: 向网页发送的请求头信息,需要根据自己爬取的网页不同进行相应的调整
:param times: 分开循环的次数,由于自己下载的文件体量比较大,就进行了分批处理,防止在一次性合并中出现问题(暂时还不会多进程,之后会更新)
:return:
"""
cryptor = AES.new(key.encode('utf-8'), AES.MODE_CBC)
web_list = []
with open(fr"{os.getcwd()}\Temporary\{name}.m3u8", "r") as files:
lines_list = files.readlines()
for https in lines_list:
web = re.search("https://.+\.ts", https)
if web:
web_list.append(web.group())
os.remove(f"Temporary\\index.m3u8")
os.remove(f"Temporary\\index.txt")
length = len(web_list)
with open(f"Temporary\\filesname.txt", "a+") as to:
to.write("\n") #该格式请勿变动
for time in range(times):
remove_or_set(f"{os.getcwd()}\\tslib")
to.write(f"file '{os.getcwd()}\\together\\{time}.ts'\n") #该格式请勿变动
with open(f"Temporary\\filesname{time}.txt", "a+") as file:
file.write("\n")
for num in tqdm(range(int(length/times*time),int(length/times*(time+1)))):
file.write(f"file '{os.getcwd()}\\tslib\\{num}.ts'\n") #该格式请勿变动
with open(f'{os.getcwd()}\\tslib\\{num}.ts', 'ab') as code:
datas = requests.get(web_list[num],headers=headers).content
code.write(cryptor.decrypt(datas))
together_ts(time)
os.remove(f"Temporary\\filesname{time}.txt")
def together_ts(times):
"""
先合并一些ts放到together中的函数操作
:param times: 分批的次数,方便找对应的filename{times}.txt文件
:return:
"""
ff = FFmpeg(
inputs={
fr"{os.getcwd()}\Temporary\filesname{times}.txt": "-f concat -safe 0"
},
outputs={
fr"{os.getcwd()}\together\{times}.ts": "-c copy"
}
)
# print(ff.cmd)
ff.run()
# 3.将ts文件转化成MP4格式
def together_mp4(name):
"""
将together文件夹中的ts文件转化成MP4文件,也就是最后一步
:param name:
:return:
"""
ff = FFmpeg(
inputs={
fr"{os.getcwd()}\Temporary\filesname.txt": "-f concat -safe 0"
},
outputs={
fr"{os.getcwd()}\MP4\{name}.mp4": "-c copy"
}
)
# print(ff.cmd)
ff.run()
# 这个拿来清理使用
def remove_or_set(filepath):
"""
清理文件夹使用,如果没有这个文件夹,就创建,有就清楚该目录下的所有内容
:param filepath: 要清理或创建的文件夹
"""
if not os.path.exists(filepath):
os.mkdir(filepath)
else:
shutil.rmtree(filepath)
os.mkdir(filepath)
def clear(filepath,times):
remove_or_set(filepath)
try:
os.remove("Temporary\\index.m3u8")
os.remove("Temporary\\index.txt")
os.remove("Temporary\\filesname.txt")
except:
pass
for num in range(times):
try:
os.remove(f"Temporary\\filesname{num}.txt")
except:
continue
def main():
headers = {
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.134 Safari/537.36 Edg/103.0.1264.71"
}
m3u8_key_name='你在Temporary文件夹中的m3u8文件名称'
mp4_name='你要转换的MP4叫什么'
download_ts(get_key(m3u8_key_name), m3u8_key_name, headers, times=10)
together_mp4(mp4_name)
clear(f'{os.getcwd()}\\tslib', times=10)
运行结果
由于速度比较慢,这里给出了进度条画面
伪装类型
如下图所示,该m3u8文件本来的ts文件后缀全部变成了png格式
总体思路
仅提供个人对处理加密型m3u8的思路
修改头部代码
with open(f"没有修改的ts文件名.ts", "rb") as infile:
with open(f"将修改后的数据写入的ts的名称.ts", "wb") as outfile:
data = infile.read()
outfile.write(data)
outfile.seek(0x00)
outfile.write(b'\xff\xff\xff\xff')
outfile.flush()
outfile.close()
实例代码
本代码只是作为例子使用,并不能保证对所有的m3u8文件都起作用
from tqdm import tqdm
import requests
import os
import re
from ffmpy3 import FFmpeg
# 1.直接下载ts文件,不管后缀是什么直接改为ts后缀
def download_ts(name):
"""
以字节流形式下载ts文件
:param name: 我们下载过来的m3u8文件
:return: 返回尚未处理的ts文件
"""
web_list = []
with open(fr"{os.getcwd()}\{name}.m3u8", "r") as files:
lines_list = files.readlines()
for https in lines_list:
web = re.search("https://.+", https)
if web:
web_list.append(web.group())
files.close()
headers = {
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.134 Safari/537.36 Edg/103.0.1264.71"
}
# print("正在下载ts文件......")
print("ts视频加载进度:")
length = len(web_list)
with open("filesname.txt","a+") as file:
file.write("\n")
for url, num in tqdm(zip(web_list, range(1, length + 1))):
file.write(f"file '{os.getcwd()}\\tslib\{num}.ts'\n")
resp = requests.get(url, headers=headers)
with open(fr"tslib\{num}.ts", "wb") as codes:
codes.write(resp.content)
return length
# 2.处理这些经过伪装的ts文件,直接对ts文件的头部进行修改
def change(num):
"""
由于这个m3u8文件比较特殊,里面头部信息为png,我们要将它修改成ts
:param num: 这个就是总共的ts数
:return: 返回修改过后的ts文件
"""
with open(fr"{os.getcwd()}\filelist.txt", "a+") as file:
file.write("\n")
print("\n\n正在转化ts文件......")
print("ts视频转化进度:")
for i in tqdm(range(1, num + 1)): # num+1
file.write(f"file '{os.getcwd()}\\outlib\\{i}.ts'\n")
with open(f"{os.getcwd()}\\inlib\\{str(i)}.ts", "rb") as infile:
with open(f"{os.getcwd()}\\outlib\\{str(i)}.ts", "wb") as outfile:
data = infile.read()
outfile.write(data)
outfile.seek(0x00)
outfile.write(b'\xff\xff\xff\xff')
outfile.flush()
outfile.close()
# 3.将ts文件合并成MP4文件
def together(name,num):
"""
合并outlib列表中的所有ts文件
:param name: 爬取视频的名字
:return: 返回一个MP4文件
"""
ff = FFmpeg(
inputs={
fr"{os.getcwd()}\filesname.txt": "-f concat -safe 0"
},
outputs={
fr"{os.getcwd()}\{name}\{name}-第{num}集.mp4": "-c copy"
}
)
# print(ff.cmd)
ff.run()
def main():
m3u8_name='你的m3u8名称'
ts_num=download_ts(m3u8_name)
change(ts_num)
together('你要看的影视的名字','集数')
嵌套类型
如下图所示,该m3u8文件就只有这么短短的几行
这种情况还是比较简单的,首先我们要明确这个代码是哪里来的,那个网站,然后将后面这一串 /20220104/22Mp6rI2/1000kb/hls/index.m3u8 拼接到域名上重新回去一遍就行了
实例代码
本代码只是作为例子使用,并不能保证对所有的m3u8文件都起作用
import re
from urllib import parse
from urllib import request
url='下载嵌套m3u8文件的地址'
request.urlretrieve(url,f'下载下来存在嵌套的m3u8文件名.m3u8') # 下载m3u8文件
result = parse.urlparse(url) # 解析url
with open(f"下载下来存在嵌套的m3u8文件名.m3u8","r") as m3u8:
text=m3u8.read() # 读取m3u8文件中的数据
m3u8path=re.findall("/.+index\.m3u8",text) # 运用正则匹配url域名后的路径
true_m3u8_url=result.netloc+m3u8path[0] # 拼接域名和路径
request.urlretrieve(f'https://{true_m3u8_url}', f'你要存储的m3u8名.m3u8') # 重新发送请求获取真正的m3u8文件