Python 爬虫 m3u8的下载及AES解密

本文详细介绍了如何使用Python爬虫下载AES加密的m3u8视频流,并提供了线程池和异步协程两种实现方式。代码中包含了请求m3u8文件、解析加密信息、下载TS片段、解密和合并视频的完整流程,同时支持代理和多线程下载。示例代码涵盖了从获取到解密m3u8视频流的关键步骤,适合学习和参考。
摘要由CSDN通过智能技术生成

python爬虫 m3u8的下载及AES加密的解密

前言

这里与hxdm分享一篇关于m3u8视频流的爬取下载合并成mp4视频的方法,并且支持AES加密后的ts文件解密。xdm懂的都懂,也不用感谢了,哈哈哈!!!至于m3u8_url的链接就自己去找了哈。
好了,废话不多说,直接上代码!
(注意:本篇文章只做学习思路交流,不做除此之外的任何用途!!!)
在这里插入图片描述
目前测试过m3u8文件的AES加密后的有:
在这里插入图片描述


2023.1.23更新

# -*- coding:utf-8 -*-
"""
m3u8文件 视频下载 多线程 支持代理
"""
import requests, threading
import os, shutil, time, random
from Crypto.Cipher import AES
from urllib.parse import urljoin
from concurrent.futures import ThreadPoolExecutor, as_completed

from anti_useragent import UserAgent
# pip install -U anti-useragent
def get_ua(platform='windows', browser_type='chrome', min_version=80, max_version=100):
    '''
    :param platform: 'windows', 'linux', 'android'
    :param browser_type: 'chrome', 'firefox'
    :param min_version:
    :param max_version:
    :return:
    '''
    ua = UserAgent(platform=platform, min_version=min_version, max_version=max_version)[browser_type]
    return ua

# IP代理池,可自行封装
def get_proxy():
    ip = "" #
    proxies = {
        "http": ip,
        "https": ip,
    }
    # return proxies if ip else None
    if ip:
        return proxies
    else:
        print("######未设置代理!!!######")
        return None



class M3u8Downloader:
    """
    save_path:视频文件存放路径
    save_file_name:视频文件名
    m3u8_url:m3u8_url链接
    isUseproxies:是否使用代理
    isUseThreadPool:是否开启线程池
    """
    def __init__(self, save_path:str='./', save_file_name:str="test.mp4", m3u8_url:str=None,isUseproxies=False,isUseThreadPool=False,threadPoolNum=8,proxies=None,timeOut=None, isShutilAllTsFile=True):
        if not m3u8_url: raise Exception("没有传入m3u8_url链接!!!")
        self.save_path= save_path
        self.save_file_name=str(save_file_name).replace(".mp4",'')+".mp4"
        self.save_file_name2=str(save_file_name).replace(".mp4",'')
        self.m3u8_url= m3u8_url
        self.isUseproxies= isUseproxies
        self.isUseThreadPool= isUseThreadPool
        self.threadPoolNum= threadPoolNum
        self.proxies= proxies
        self.proxies_usable_flag= False
        self.timeOut= timeOut if timeOut else 5
        if not isUseproxies: self.timeOut=None
        self.isShutilAllTsFile = isShutilAllTsFile
        self.proxies_usable_flag = False
        self.proxies_fail_num= 0
        self.download_ok_num = 0
        self.lock = threading.Lock()
        self.lock2 = threading.Lock()
        self.headers = {
            'Accept': '*/*',
            'Accept-Language': 'zh-CN,zh;q=0.9',
            'Cache-Control': 'no-cache',
            'Connection': 'keep-alive',
            'Pragma': 'no-cache',
            'Sec-Fetch-Dest': 'empty',
            'Sec-Fetch-Mode': 'cors',
            'Sec-Fetch-Site': 'cross-site',
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36",
            "sec-ch-ua": "\"Not?A_Brand\";v=\"8\", \"Chromium\";v=\"108\", \"Google Chrome\";v=\"108\"",
            "sec-ch-ua-mobile": "?0",
            "sec-ch-ua-platform": "\"Windows\""
        }
        self.session = None
        self.set_session()
        self.lock2.release()
        # 视频文件存放路径
        if not os.path.exists(fr'{self.save_path}/mp4'): os.mkdir(fr'{self.save_path}/mp4')
        if not os.path.exists(fr'{self.save_path}/ts'): os.mkdir(fr'{self.save_path}/ts')
        self.all_ts_path = fr'{self.save_path}/ts/{self.save_file_name2}'
        if not os.path.exists(self.all_ts_path):
            os.mkdir(self.all_ts_path)

    def set_session(self):
        self.lock2.acquire()
        if not self.proxies_usable_flag:
            try:
                self.session.close()
            except: pass
            self.session = requests.session()
            self.UA = get_ua()
            self.UA_V = self.UA.split("Chrome/")[1].split('.')[0]
            self.headers['User-Agent'] = self.UA
            self.headers['sec-ch-ua'] = f'"Not?A_Brand";v="8", "Chromium";v="{self.UA_V}", "Google Chrome";v="{self.UA_V}"'


    def send_request(self, url):
        while True:
            time.sleep(random.random()+0.2)
            if self.proxies_fail_num > 20:
                raise Exception(f"异常:{url} 连续请求次数超过20次失败>>>异常退出!!!")
            if self.isUseproxies:
                self.lock.acquire()
                if not self.proxies_usable_flag:
                    self.proxies = get_proxy() # 使用IP代理池
                    self.proxies_usable_flag = True
                self.lock.release()
                try: self.lock2.release()
                except: pass
            try:
                response = self.session.get(url=url, headers=self.headers, proxies=self.proxies, timeout=self.timeOut)
                if response.status_code == 200:
                    self.proxies_usable_flag = True
                    self.proxies_fail_num = 0
                    break
                else:
                    raise Exception(f'{response}>>>响应异常!')
            except Exception as e:
                print(f"异常:{url}请求异常!!!\n{e}")
                if self.isUseproxies:
                    self.proxies_usable_flag = False
                    self.proxies_fail_num += 1
                    self.set_session()
                else:
                    raise Exception(f"异常:{url} >>>异常退出!!!\n{e}")

        return response

    # 有加密则进行解析
    def parse_aes_encryption(self, key_content):
        encryption_method = None
        self.m3u8_key_url = None
        self.iv = None
        print('key_content:', key_content)
        try:
            split_result = key_content.split(',')
            encryption_method = split_result[0].split('=')[1]
            self.m3u8_key_url = urljoin(self.m3u8_url,split_result[1].split('"')[1])
            if 'IV' in key_content or 'iv' in key_content:
                iv = split_result[2].split('=')[1]
                print("m3u8文件里面的iv:", iv)
                self.iv = iv[2:18].encode()
            else:
                self.iv = b'0000000000000000'
        except Exception as e:
            print(e)
            print('加密方法未知!')
        print('加密方法:', encryption_method)
        print('m3u8_key_url:', self.m3u8_key_url)
        print('解密使用的iv:', self.iv)
        print()

    def get_m3u8_txt(self):
        txt = self.send_request(self.m3u8_url).text
        # print(txt)
        each_line_list = txt.strip('\n').split('\n')  # 对m3u8里面的内容提取出每一行数据
        self.all_ts_list = []
        video_time = []
        if '#EXTM3U' != each_line_list[0]:
            print(f"请求m3u8得到的内容:\n{txt}")
            raise Exception("异常:请求的m3u8文件数据存在异常!!!")
        for i in each_line_list:
            if '#EXT-X-KEY' in i:  # 判断是否加密
                self.parse_aes_encryption(i)
            elif not i.startswith('#') or i.startswith('http') or i.endswith('.ts'):
                each_ts_url = urljoin(self.m3u8_url, i)
                self.all_ts_list.append(each_ts_url)
            elif i.startswith('#EXTINF'):
                time_ = float(i.strip().split(':')[1][:-1])
                video_time.append(time_)
        print('视频时长约为:{:.2f}分钟\n'.format(sum(video_time) / 60))

    def get_m3u8_key_decode_data(self, key_url=None):
        try:
            key_url = key_url if key_url else self.m3u8_key_url
            key = self.send_request(key_url).content
            print("请求m3u8_key_url得到的加密密钥:", key)
            self.aes_decode_data = AES.new(key, AES.MODE_CBC, self.iv)
            print("解密后的密钥:", key)
            print()
        except Exception as e:
            self.aes_decode_data = None
            print(e)
            print("m3u8_key解密失败!!!\n")

    # 下载并保存ts
    def download_ts(self, i, ts_url):
        if self.aes_decode_data:
            ts_data = self.send_request(ts_url).content
            ts_data = self.aes_decode_data.decrypt(ts_data)
        else:
            ts_data = self.send_request(ts_url).content
        with open(fr'{self.save_path}/ts/{self.save_file_name2}/{i}.ts', mode='wb+') as f:
            f.write(ts_data)
            print(f'{i}.ts下载完成!')
            self.download_ok_num += 1
        return 0

    # 最后合并所有的ts文件
    def merge_all_ts_file(self):
        print('开始合并视频……')
        ts_file_list = os.listdir(self.all_ts_path)
        ts_file_list.sort(key=lambda x: int(x[:-3])) # 进行排序
        with open(self.save_path + f'/mp4/{self.save_file_name}', 'wb+') as fw:
            for i in range(len(ts_file_list)):
                fr = open(os.path.join(self.all_ts_path, ts_file_list[i]), 'rb')
                fw.write(fr.read())
                fr.close()
        if self.isShutilAllTsFile:
            shutil.rmtree(self.all_ts_path)  # 删除所有的ts文件
            print("所有的ts文件删除完成!")
        print('视频合并完成!')

    def main(self):
        self.get_m3u8_txt()
        self.get_m3u8_key_decode_data()
        print(f"######开始下载ts文件 数量:{len(self.all_ts_list)}######")
        t_future = []
        def handle_result(future):
            pass
        pool = ThreadPoolExecutor(max_workers=self.threadPoolNum)
        for i, ts_url in enumerate(self.all_ts_list):
            # if i > 3: # 测试
            #     break
            try:
                if not self.isUseThreadPool:
                    self.download_ts(i, ts_url)
                else:
                    future = pool.submit(self.download_ts, i, ts_url)
                    t_future.append(future)
            except Exception as e:
                print(e)
                print(f"<{i},{ts_url}> 获取失败!!!")
                break
        if self.isUseThreadPool:
            for future in as_completed(t_future):
                future.add_done_callback(handle_result)

        pool.shutdown()
        print(f"######{self.download_ok_num}个ts文件下载完成######")
        self.merge_all_ts_file()

        try:
            self.session.close()
        except:
            pass



if __name__ == '__main__':
    m3u8_url = ""
    m3u8_downloader = M3u8Downloader(m3u8_url=m3u8_url,isUseproxies=True, isUseThreadPool=True, threadPoolNum=10)
    m3u8_downloader.headers['Origin'] = '' # 可能需要根据实际情况添加
    m3u8_downloader.headers['Referer'] = ''
    m3u8_downloader.main()





线程池版


在这里插入图片描述


完整代码


import os,shutil,time,requests
from Crypto.Cipher import AES
from fake_useragent import UserAgent
from urllib.parse import urljoin
from concurrent.futures import ThreadPoolExecutor
video_download_path = './m3u8Download'
save_mp4_path = './m3u8Download/testVideo'
save_temporary_ts_path = './m3u8Download/temporary_ts'
if not os.path.exists(video_download_path):
    os.makedirs(save_mp4_path)
    os.mkdir(save_temporary_ts_path)
if not os.path.exists(save_temporary_ts_path):
    os.mkdir(save_temporary_ts_path)
#先定义一个发送请求方法,方便后面的重复调用
def send_request(url):
    headers = {
        'User-Agent': UserAgent().Chrome,
        'Accept': '*/*',
        'Accept-Encoding': 'gzip, deflate, br',
        'sec-ch-ua': '" Not A;Brand";v="99", "Chromium";v="100", "Google Chrome";v="100"',
        'sec-ch-ua-mobile': '?0',
        'sec-ch-ua-platform': '"Windows"',
        'Accept-Language': 'zh-CN,zh;q=0.9',
    }
    try:
        response = requests.get(url=url,headers=headers)
        if response.status_code == 200:
            return response
        else:
            print(response,'响应异常!')
            exit()
    except Exception as e:
        print('m3u8链接请求异常!!!')
        print(e)
#这里先发送m3u8链接请求,得到返回的响应数据
def get_m3u8_response_data():
    m3u8_data = send_request(m3u8_url).text
    return m3u8_data
#然后对得到的m3u8数据进行解析,得到每一个ts_url链接,和视频的时长,有加密则提取出
def parse_m3u8_data():
    m3u8_data = get_m3u8_response_data()
    each_line_list = m3u8_data.strip('\n').split('\n') #对m3u8里面的内容提取出每一行数据
    all_ts_list = []
    video_time = []
    AES_decode_data = None
    if '#EXTM3U' in each_line_list:
        for i in each_line_list:
            if '#EXT-X-KEY' in i: #判断是否加密
                encryption_method,key_url, iv = parse_AES_encryption(i)
                print('加密方法:',encryption_method)
                key_url = urljoin(m3u8_url,key_url)
                AES_decode_data = AES_decode(key_url,iv)
            if not i.startswith('#') or i.startswith('http') or i.endswith('.ts'):
                each_ts_url = urljoin(m3u8_url, i)
                all_ts_list.append(each_ts_url)
            if i.startswith('#EXTINF'):
                time_ = float(i.strip().split(':')[1][:-1])
                video_time.append(time_)
    print('视频时长约为:{:.2f}分钟'.format(sum(video_time) / 60))
    return all_ts_list,AES_decode_data
#再对每一个ts_url链接发送请求(用线程池)
def get_each_ts_response_data():
    print('开始下载视频……')
    all_ts_list,AES_decode_data = parse_m3u8_data()
    ###初始化一个线程池,并设置最大线程数为30
    with ThreadPoolExecutor(max_workers=30) as executor:
        for i,ts_url in enumerate(all_ts_list):
            executor.submit(download_ts, i,ts_url,AES_decode_data)
    '''这里可以使用单线程来下载3个ts文件做测试'''
    # i = 0
    # for ts_url in all_ts_list:
    #     download_ts(i,ts_url,AES_decode_data)
    #     i += 1
    #     if i > 3:
    #         break
    print('视频下载结束!')
    return True
#下载并保存ts
def download_ts(i,ts_url,AES_decode_data):
    if AES_decode_data:
        ts_data = send_request(ts_url).content
        ts_data = AES_decode_data.decrypt(ts_data)
    else:
        ts_data = send_request(ts_url).content
    with open(f'{save_temporary_ts_path}/{i}.ts',mode='wb+') as f:
        f.write(ts_data)
        print(f'{i}.ts下载完成!')
#解析加密内容
def parse_AES_encryption(key_content):
    if 'IV' in key_content or 'iv' in key_content:
        parse_result = key_content.split('=')
        encryption_method = parse_result[1].split(',')[0]
        key_url = parse_result[2].split('"')[1]
        iv = parse_result[3]
        iv = iv[2:18].encode()
    else:
        parse_result = key_content.split('=')
        encryption_method = parse_result[1].split(',')[0]
        key_url = parse_result[2].split('"')[1]
        iv = None
    return encryption_method, key_url, iv
#AES解密
def AES_decode(key_url,iv):
	print("key_url:", key_url)
    print("iv:",iv)
    key = send_request(key_url).content
    if iv:
        AES_decode_data = AES.new(key, AES.MODE_CBC, iv)
    else:
        AES_decode_data = AES.new(key, AES.MODE_CBC, b'0000000000000000')
    return AES_decode_data
#最后合并所有的ts文件
def merge_all_ts_file():
    print('开始合并视频……')
    ts_file_list = os.listdir(save_temporary_ts_path)
    ts_file_list.sort(key=lambda x: int(x[:-3]))
    with open(save_mp4_path+'/video.mp4', 'wb+') as fw:
        for i in range(len(ts_file_list)):
            fr = open(os.path.join(save_temporary_ts_path, ts_file_list[i]), 'rb')
            fw.write(fr.read())
            fr.close()
    shutil.rmtree(save_temporary_ts_path) #删除所有的ts文件
    print('视频合并完成!')

def begin():
    if get_each_ts_response_data():
        merge_all_ts_file()
if __name__ == '__main__':
    start_time = time.time()
    ###m3u8_url链接自己找哈!
    m3u8_url = 'https://xxx.m3u8'
    begin()
    end_time = time.time()
    print(f'总共耗时:{end_time-start_time}秒')


异步协程版


import os,shutil,time
import asyncio,aiohttp,aiofiles
from Crypto.Cipher import AES
from fake_useragent import UserAgent
from urllib.parse import urljoin
class asyncioDownloadM3u8:
    def __init__(self,m3u8_url):
        self.m3u8_url = m3u8_url
        self.AES_decode_data = None #AES解密数据
        self.video_download_path = './m3u8Download'
        self.save_mp4_path = './m3u8Download/testVideo'
        self.save_temporary_ts_path = './m3u8Download/temporary_ts'
        if not os.path.exists(self.video_download_path):
            os.makedirs(self.save_mp4_path)
            os.mkdir(self.save_temporary_ts_path)
        if not os.path.exists(save_temporary_ts_path):
    		os.mkdir(save_temporary_ts_path)
    async def send_request(self,url):
        async with aiohttp.ClientSession() as session:
            try:
                headers = {
                    'User-Agent': UserAgent().Chrome,
                    'Accept': '*/*',
                    'Accept-Encoding': 'gzip, deflate, br',
                    'sec-ch-ua': '" Not A;Brand";v="99", "Chromium";v="100", "Google Chrome";v="100"',
                    'sec-ch-ua-mobile': '?0',
                    'sec-ch-ua-platform': '"Windows"',
                    'Accept-Language': 'zh-CN,zh;q=0.9',
                }
                async with session.get(url, headers=headers) as response:
                        assert response.status == 200
                        content = await response.read()
                        return content
            except Exception as e:
                print('m3u8链接请求异常!!!')
                print(e)
                exit()
    async def parse_m3u8_data(self):
        content = await self.send_request(self.m3u8_url)
        m3u8_data = content.decode('utf-8')
        each_line_list = m3u8_data.strip('\n').split('\n')
        all_ts_list = []
        video_time = []
        AES_decode_data = None
        if '#EXTM3U' in each_line_list:
            for i in each_line_list:
                if '#EXT-X-KEY' in i:  # 判断是否加密
                    encryption_method, key_url, iv = await self.parse_AES_encryption(i)
                    print('加密方法:', encryption_method)
                    key_url = urljoin(m3u8_url, key_url)
                    self.AES_decode_data = await self.AES_decode(key_url, iv)
                if not i.startswith('#') or i.startswith('http') or i.endswith('.ts'):
                    each_ts_url = urljoin(m3u8_url, i)
                    all_ts_list.append(each_ts_url)
                if i.startswith('#EXTINF'):
                    time_ = float(i.strip().split(':')[1][:-1])
                    video_time.append(time_)
        print('视频时长约为:{:.2f}分钟'.format(sum(video_time) / 60))
        return all_ts_list, AES_decode_data
    async def get_each_ts_response_data(self):
        print('开始下载视频……')
        all_ts_list, AES_decode_data =  await self.parse_m3u8_data()
        tasks = []
        i = 0
        for ts_url in all_ts_list:
            task = asyncio.create_task(self.download_ts(i,ts_url))
            tasks.append(task)
            i += 1
            #await asyncio.sleep(1) #担心爬取太快,可以使用异步休眠
            # if i > 2:
            #     break
        await asyncio.wait(tasks)
        print('视频下载结束!')
    async def download_ts(self,i,ts_url):
        if self.AES_decode_data:
            ts_data = await self.send_request(ts_url)
            ts_data = self.AES_decode_data.decrypt(ts_data)
        else:
            ts_data = await self.send_request(ts_url)
        async with aiofiles.open(f'{self.save_temporary_ts_path}/{i}.ts', mode="wb") as f:
            await f.write(ts_data)
            print(f'{i}.ts下载完成!')
    async def parse_AES_encryption(self,key_content):
        if 'IV' in key_content or 'iv' in key_content:
            parse_result = key_content.split('=')
            encryption_method = parse_result[1].split(',')[0]
            key_url = parse_result[2].split('"')[1]
            iv = parse_result[3]
            iv = iv[2:18].encode()
        else:
            parse_result = key_content.split('=')
            encryption_method = parse_result[1].split(',')[0]
            key_url = parse_result[2].split('"')[1]
            iv = None
        return encryption_method, key_url, iv
    async def AES_decode(self,key_url, iv):
	    print("key_url:", key_url)
	    print("iv:",iv)
        key = await self.send_request(key_url)
        if iv:
            AES_decode_data = AES.new(key, AES.MODE_CBC, iv)
        else:
            AES_decode_data = AES.new(key, AES.MODE_CBC, b'0000000000000000')
        return AES_decode_data
    async def merge_all_ts_file(self):
        print('开始合并视频……')
        ts_file_list = os.listdir(self.save_temporary_ts_path)
        ts_file_list.sort(key=lambda x: int(x[:-3]))
        async with aiofiles.open(self.save_mp4_path + '/video.mp4', 'wb+') as fw:
            for i in range(len(ts_file_list)):
                fr = open(os.path.join(self.save_temporary_ts_path, ts_file_list[i]), 'rb')
                await fw.write(fr.read())
                fr.close()
        shutil.rmtree(self.save_temporary_ts_path)
        print('视频合并完成!')
    async def begin(self):
        await self.get_each_ts_response_data()
        await self.merge_all_ts_file()

if __name__ == '__main__':
    start_time = time.time()
    ###m3u8_url链接自己找哈!
    m3u8_url = 'https://xxx.m3u8'
    adm = asyncioDownloadM3u8(m3u8_url=m3u8_url)
    loop = asyncio.get_event_loop()
    loop.run_until_complete(adm.begin())
    end_time = time.time()
    print(f'总共耗时:{end_time - start_time}秒')

在这里插入图片描述


### 回答1: Python可以使用pycryptodome库来解密AES-128加密的m3u8文件。具体步骤如下: 1. 读取m3u8文件内容,可以使用requests库或者urllib库来获取m3u8文件内容。 2. 解析m3u8文件,获取所有的ts文件链接。 3. 遍历所有的ts文件链接,使用requests库或者urllib库获取ts文件内容。 4. 对每个ts文件进行AES-128解密,可以使用pycryptodome库中的AES模块来进行解密。 5. 将解密后的ts文件内容保存到本地。 6. 合并所有解密后的ts文件,可以使用ffmpeg或者其他视频处理工具来进行合并。 需要注意的是,解密m3u8文件需要获取密钥,密钥一般在m3u8文件中的EXT-X-KEY标签中指定。如果密钥是通过HTTPS协议获取的,需要使用ssl模块来进行证书验证。 ### 回答2: 在开始回答此问题之前,我们需要了解一些基础知识。M3U8是一个播放列表文件格式,常用于存储视频或音频片段,而AES(Advanced Encryption Standard)是一种对称加密算法。许多视频网站使用M3U8格式和AES 128位加密来保护其视频内容。 在Python解密AES加密的M3U8文件需要使用pycryptodome库。该库提供了一些加密算法的实现,包括AES加密和解密。 以下是解密AES 128位加密的M3U8文件的步骤: 1. 从M3U8文件中获取密钥。在M3U8文件中,如果使用了AES 128位加密,则应该有一个URI指定密钥的位置。 2. 使用requests库获取该URI指定的密钥。 3. 将密钥转换为字节格式,并使用base64解码。 4. 使用pycryptodome库创建一个AES解密器,并使用从步骤3中获取到的密钥进行初始化。 5. 逐个解密M3U8文件中的每个片段。在解密每个片段之前,需要先去掉它前面的所有注释行。 6. 将每个解密后的片段写入一个新的M3U8文件中。 在代码实现方面,以下是一个Python的示例函数,可用于对AES 128位加密的M3U8文件进行解密: ``` import requests from Crypto.Cipher import AES def decrypt_m3u8_file(m3u8_url): # 从M3U8文件中获取密钥 r = requests.get(m3u8_url) key_uri = None for line in r.text.splitlines(): if line.startswith("#EXT-X-KEY"): key_uri = line.split(",")[1].split("=")[1].strip('"') break if not key_uri: raise ValueError("No key URI found in M3U8 file") # 获取密钥内容 key_response = requests.get(key_uri) key_bytes = key_response.content key = key_bytes.decode('utf-8') # 将密钥转换为字节格式并使用base64解码 key_bytes = bytes(key, 'utf-8') key_bytes = base64.decodebytes(key_bytes) # 创建AES解密器 decryptor = AES.new(key_bytes, AES.MODE_CBC) # 解密每个片段 m3u8_decrypted = "" for line in r.text.splitlines(): if line.startswith("#"): m3u8_decrypted += line + "\n" else: encrypted = requests.get(line) fragment = decryptor.decrypt(encrypted.content) m3u8_decrypted += fragment.decode('utf-8') + "\n" return m3u8_decrypted ``` 使用此函数,您可以通过传递M3U8文件的URL调用它,然后函数将返回解密后的M3U8文件的内容,以供您进一步使用。 综上所述,解密AES 128位加密的M3U8文件需要从M3U8文件中获取密钥,将密钥转换为字节格式并使用base64解码,使用pycryptodome库创建AES解密器,并逐个解密M3U8文件中的每个片段。这些都可以在Python中通过一些库和API来实现。 ### 回答3: Python实现m3u8解密主要使用PyCryptodome库中的AES模块,通过加密密钥对m3u8文件中的每个ts片段进行解密,得到原始的视频数据。 AES是一种对称加密算法,分为AES-128、AES-192和AES-256。在AES加密过程中,需要确定加密模式、填充方式和加解密密钥。 对于m3u8加密,我们需要获得密钥。一般情况下,密钥被加密后存储在m3u8文件中的EXT-X-KEY标签中。这个标签包括了密钥的URI、加密方式和密钥IV。URI是密钥的下载地址,如"http://example.com/abc.key";加密方式一般为AES-128;密钥IV是初始化向量,用于加密中的初始位置。 在Python中,我们可以使用requests库来下载密钥,通过requests.get(url)获取密钥的内容,或者使用urllib库中的urllib.request.urlopen(url)方法。获取到密钥后,需要对密钥进行解密: ``` from Crypto.Cipher import AES import binascii def decrypt_key(key_cipher, iv, key): aes = AES.new(key, AES.MODE_CBC, iv) key_text = aes.decrypt(key_cipher) return key_text.decode('utf-8') ``` 解密后得到原始的密钥文本,然后使用PyCryptodome库进行解密: ``` from Crypto.Cipher import AES def decrypt_ts(data, key, iv): aes = AES.new(key, AES.MODE_CBC, iv) return aes.decrypt(data) ``` 通过以上两个函数,我们可以对m3u8文件中的ts片段进行解码,并将解码后的数据拼接到一起,实现视频的解码。 总的来说,Python实现m3u8 AES-128解密需要通过下载密钥、解密密钥和解密ts片段等步骤,需要熟练掌握加密算法的相关知识和相关库的使用方法,才能够进行有效地解密
评论 29
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值