python+FFmpeg实现对m3u8文件内的ts视频多线程下载、解密、合并成MP4,并输出。

需求:把目标网站的电影下载到本地。

目标网站电影:《丛林奇航》电影完整版免费在线观看_天启影院

分析网站:

1,根据网页分析 该电影的m3u8是放在 index.m3u8里,网页里有两个 index.m3u8,选择第二个。(具体怎么分析网页,这里就不讨论了)

下载m3u8文件

# 下载m3u8
def down_m3u8(url):
    resp = requests.get(url, headers)
    text = resp.text
    with open("丛林奇航.m3u8", "w") as f:
        f.write(text)
    print("丛林奇航.m3u8下载完成")

2,对下载下来的 m3u8 分析,这里会发现是有经过AES-128加密,ts视频下载后会无法播放,所以还需要进行解密。

采用多线程下载ts视频

# 采用线程池下载m3u8里的ts
def down_ts():
    with open("./丛林奇航.m3u8", "r", encoding="utf-8") as f:
        # n = 0
        with ThreadPoolExecutor(10) as t:
            for n, line in enumerate(f):
                line = line.strip()
                if line.startswith("#"):
                    continue
                t.submit(ts_video, n, line)


# 下载ts视频 D:\\data\\movie_ts\\m1 为下载ts视频存放的路径
def ts_video(n, line):
    # 下载视频
    resp3 = requests.get(line)
    with open(f"D:\\data\\movie_ts\\m1\\{n}.ts", "wb") as f:
        f.write(resp3.content)
    print(n, "下载完成")

3,这里下载好的视频是有经过加密的,无法在播放器是直接看,接下来就要对下载好的加密ts视频进行解密

 解密ts视频

# 对下载t好的ts视频进行解密 D:\\data\\video\\m1\\ 为解密后ts存放的路径
def jiemi():
    f = open("./丛林奇航.m3u8", "r", encoding="utf-8")
    keys = []
    for file in f:
        key = re.findall(r'URI="(.*?)"', file) # 用正则匹配出 key
        if key:
            keys.append(key[0])
    key_url = keys[0]
    resp4 = requests.get(key_url)
    key = resp4.text
    key = key.encode("utf-8")
    aes = AES.new(key=key, IV=b"0000000000000000", mode=AES.MODE_CBC)
    download_path = "D:\\data\\movie_ts\\m1"
    all_ts = os.listdir(download_path)
    li = []
    for i in all_ts:
        i = i.split(".")[0]
        li.append(int(i))
    li.sort()
    for i in li:
        with open(f"D:\\data\\movie_ts\\m1\\{i}.ts", mode="rb") as f1, \
                open(f"D:\\data\\video\\m1\\2000{i}.ts", mode="wb") as f2:
            bs = f1.read()
            f2.write(aes.decrypt(bs))
        print(f"第{i}视频处理完毕")

解密好的ts视频

 解密好的ts视频是可以直接用播放器看的

4,对解密好的ts视频用 ffmpeg合成MP4视频

这里需要先安装 ffmpeg 

Windows:

1. 进入 http://ffmpeg.org/download.html#build-windows,点击 windows 对应的图标,进入下载界面点击 download 下载按钮,
2. 解压下载好的zip文件到指定目录
3. 将解压后的文件目录中 bin 目录(包含 ffmpeg.exe )添加进 path 环境变量中
4. DOS 命令行输入 ffmpeg -version, 出现以下界面说明安装完成:

Mac (打开终端(Terminal), 用 homebrew 安装):

brew install ffmpeg --with-libvorbis --with-sdl2 --with-theora

Linux:

apt-get install ffmpeg libavcodec-extra

ffmpeg合成MP4视频 

# 对解密好的ts视频用 ffmpeg合成MP4视频
def movie_video():
    filePath = "D:\\data\\video\\m1"
    file_list = os.listdir(filePath)
    li = []
    for file in file_list:
        file = file.split(".")[0]
        li.append(int(file))
        li.sort()
    ts_file_name = []
    for i in li:
        i = str(i) + ".ts"
        ts_file_name.append(i)
    with open("D:\\data\\video\\m1\\file_list.txt", "w+") as f:
        for file in ts_file_name:
            f.write("file '{}'\n".format(file))
    print("file_list.txt已生成")
    txt_file = "D:\\data\\video\\m1\\file_list.txt"
    mp4 = "D:\\data\\movies\\丛林奇航.mp4"
    cmd = f"ffmpeg -f concat -i {txt_file} -c copy {mp4}"
    try:
        os.system(cmd)
    except Exception as e:
        print(e)
    print("done")

全部参考代码如下:

import re
import time
import os
import requests
from Crypto.Cipher import AES
from concurrent.futures import ThreadPoolExecutor

# 下载m3u8
def down_m3u8(url):
    resp = requests.get(url, headers)
    text = resp.text
    with open("丛林奇航.m3u8", "w") as f:
        f.write(text)
    print("丛林奇航.m3u8下载完成")


# 采用线程池下载m3u8里的ts
def down_ts():
    with open("./丛林奇航.m3u8", "r", encoding="utf-8") as f:
        # n = 0
        with ThreadPoolExecutor(10) as t:
            for n, line in enumerate(f):
                line = line.strip()
                if line.startswith("#"):
                    continue
                t.submit(ts_video, n, line)


# 下载ts视频
def ts_video(n, line):
    # 下载视频
    resp3 = requests.get(line)
    with open(f"D:\\data\\movie_ts\\m1\\{n}.ts", "wb") as f:
        f.write(resp3.content)
    # n += 1
    print(n, "下载完成")


# 对下载t好的ts视频进行解密
def jiemi():
    f = open("./丛林奇航.m3u8", "r", encoding="utf-8")
    keys = []
    for file in f:
        key = re.findall(r'URI="(.*?)"', file)
        if key:
            keys.append(key[0])
    key_url = keys[0]
    resp4 = requests.get(key_url)
    key = resp4.text
    key = key.encode("utf-8")
    aes = AES.new(key=key, IV=b"0000000000000000", mode=AES.MODE_CBC)
    download_path = "D:\\data\\movie_ts\\m1"
    all_ts = os.listdir(download_path)
    li = []
    for i in all_ts:
        i = i.split(".")[0]
        li.append(int(i))
    li.sort()
    for i in li:
        with open(f"D:\\data\\movie_ts\\m1\\{i}.ts", mode="rb") as f1, \
                open(f"D:\\data\\video\\m1\\2000{i}.ts", mode="wb") as f2:
            bs = f1.read()
            f2.write(aes.decrypt(bs))
        print(f"第{i}视频处理完毕")


# 对解密好的ts视频用 ffmpeg合成MP4视频
def movie_video():
    filePath = "D:\\data\\video\\m1"
    file_list = os.listdir(filePath)
    li = []
    for file in file_list:
        file = file.split(".")[0]
        li.append(int(file))
        li.sort()
    ts_file_name = []
    for i in li:
        i = str(i) + ".ts"
        ts_file_name.append(i)
    with open("D:\\data\\video\\m1\\file_list.txt", "w+") as f:
        for file in ts_file_name:
            f.write("file '{}'\n".format(file))
    print("file_list.txt已生成")
    txt_file = "D:\\data\\video\\m1\\file_list.txt"
    mp4 = "D:\\data\\movies\\丛林奇航.mp4"
    cmd = f"ffmpeg -f concat -i {txt_file} -c copy {mp4}"
    try:
        os.system(cmd)
    except Exception as e:
        print(e)
    print("done")


if __name__ == '__main__':
    url = "https://pps.sd-play.com/20211127/g8V4A0hE/1000kb/hls/index.m3u8"
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36",
    }
    t_start = time.time()
    print("------------------开始下载m3u8----------------------------")
    down_m3u8(url)
    time.sleep(1)
    print("------------------开始下载ts视频----------------------------")
    down_ts()
    time.sleep(1)
    print("------------------开始下载解密ts视频----------------------------")
    jiemi()
    time.sleep(1)
    print("------------------开始下载合成MP4----------------------------")
    movie_video()
    t_end = time.time()
    t = t_end-t_start
    print(t)

提示:当代码运行到 如图 所示,这里是在合成视频,需要一定的时间,耐心等待完成就行。

 python 小白一个,不喜勿喷。希望对大家有所帮助 。。。。。。

最后,python大法好!

 

### 如何使用 Python 实现 Blob 类型视频下载 Blob 是一种二进制数据对象,通常用于表示多媒体资源(如图片、音视频)。由于其特殊性,直接通过 URL 访问可能不可行。以下是实现 Blob 视频下载的方法: #### 方法概述 为了从浏览器或网络中下载 Blob 类型的视频文件,可以按照以下思路操作: 1. **分析目标页面**:利用浏览器开发者工具 (F12),找到 `window.__playinfo__` 或类似的 JavaScript 变量,提取实际的媒体流地址。 2. **处理加密链接**:如果视频源是 m3u8 格式的分片播放列表,则需要进一步抓取 ts 文件片段拼接完整的 MP4 文件[^3]。 3. **模拟请求头**:某些网站会验证用户的登录状态或者设置防盗链机制,因此需附加必要的 Cookies 和 Headers 来伪装合法访问行为。 --- #### 关键技术点详解 ##### 一、获取真实媒体流地址 对于 Bilibili 这样的平台来说,可以通过查看网页加载过程中的 XHR 请求来定位到真正的 mp4m3u8 地址。这些信息往往隐藏于 JSON 数据结构之中,例如 `data.dash.video.base_url` 字段即指向高清画质下的视频部分[^1]。 ```python import json from urllib.parse import unquote def extract_media_urls(html_content): start_marker = 'window.__playinfo__=' end_marker = '</script>' play_info_start = html_content.find(start_marker) + len(start_marker) play_info_end = html_content.find(end_marker, play_info_start) raw_data = html_content[play_info_start:play_info_end].strip() decoded_json = json.loads(unquote(raw_data)) dash_videos = decoded_json['data']['dash']['video'] highest_quality_stream = max(dash_videos, key=lambda v: int(v.get('id', 0))) return highest_quality_stream['base_url'] # 返回最高分辨率的视频URL ``` 上述代码展示了如何解析 HTML 文档内的嵌入式 JSON 对象,筛选出最优质量级别的视频流路径[^2]。 ##### 二、解决 blob 加密问题 当遇到无法直接复制粘贴的有效 URL 形态时,很可能是因为采用了动态生的技术方案——比如 base64 编码后的字符串形式表达的实际 URI Scheme (`blob:http`) 。此时应考虑如下策略之一: - 如果存在对应的解密算法逻辑公开披露,则可以直接调用相应的库函数完转换; - 否则就需要借助第三方插件录制整个回放周期内产生的临时缓存记录作为最终产物导出。 ##### 三、实施批量下载任务 一旦明确了所有的子资源构要素之后,就可以着手编写自动化脚本来逐一拉取它们的内容了。这里推荐采用多线程发模式提升效率的同时也要注意控制速率以免触发服务器防护措施。 ```python import os import threading import requests class Downloader(threading.Thread): def __init__(self, url_list, output_dir='./downloads'): super().__init__() self.url_list = url_list self.output_dir = output_dir if not os.path.exists(output_dir): os.makedirs(output_dir) def run(self): session = requests.Session() for idx, segment_url in enumerate(self.url_list): filename = f'{idx}.ts' filepath = os.path.join(self.output_dir, filename) with open(filepath, 'wb') as fp: resp = session.get(segment_url, stream=True) total_length = int(resp.headers.get('content-length')) downloaded_size = 0 for chunk in resp.iter_content(chunk_size=1024*1024): if chunk: written_bytes = fp.write(chunk) downloaded_size += written_bytes print(f'Segment {filename} completed ({downloaded_size}/{total_length}) bytes.') if __name__ == '__main__': segments_to_fetch = [...] # List of TS URLs extracted earlier. worker_threads = [] thread_count = min(len(segments_to_fetch), 5) # Limit to five simultaneous downloads at most. per_thread_load = len(segments_to_fetch)//thread_count for i in range(thread_count): lower_bound = i * per_thread_load upper_bound = None if i==thread_count-1 else (i+1)*per_thread_load current_segment_set = segments_to_fetch[lower_bound : upper_bound] new_worker = Downloader(current_segment_set) new_worker.start() worker_threads.append(new_worker) for t in worker_threads: t.join() print("All threads finished successfully.") ``` 最后一步便是运用 FFmpeg 工具将分离出来的音频轨道与视觉画面重新组合起来形标准格式封装容器文件: ```bash ffmpeg -i input_audio.aac -i input_video.mp4 -c copy final_output.mp4 ``` --- ###
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值