焱子 發表於 2023-5-30 15:11:00

Python 实现 m3u8 视频下载

<h1 id="python-实现-m3u8-视频下载">Python 实现 m3u8 视频下载</h1>
<p>m3u8 是一种<strong>基于文本的媒体播放列表文件格式</strong>,通常用于指定流媒体播放器播放在线媒体流。它是一个简单的文本文件,其中包含多个由 URI 引用的媒体资源文件的 URL。m3u8 文件通常包含多个 ts 文件的链接,这些 ts 文件是实际的视频和音频数据文件,通常是通过 HTTP 协议传输。</p>
<p>ts 文件是一种<strong>流媒体传输格式</strong>,是 MPEG-2 传输流(MPEG-2 Transport Stream)的缩写。ts 文件通常用于存储视频、音频和字幕等媒体数据,是流媒体传输的基本单位。在 m3u8 文件中,ts 文件通常是通过 URI 引用的方式来指定的,播放器会根据 m3u8 文件中的 ts 文件链接,依次请求并下载 ts 文件,然后将其组合成完整的视频流进行播放。</p>
<p>因此,m3u8 文件和 ts 文件在流媒体播放领域密切相关,m3u8 文件是<strong>流媒体的播放列表</strong>,而 ts 文件是<strong>实际的媒体数据文件</strong>。m3u8 文件中包含了多个 ts 文件的链接,播放器会根据 m3u8 文件中的 ts 文件链接,依次请求并下载 ts 文件,然后将其组合成完整的视频流进行播放。这种方式可以充分利用网络带宽,提高流媒体的播放效率和质量。同时,m3u8 文件还可以通过定义不同的码率和分辨率等参数,实现适应不同网络环境和设备的自适应流媒体播放。</p>
<p><img src="https://img2023.cnblogs.com/blog/642487/202305/642487-20230530161615679-1035213229.png" alt="img" loading="lazy"></p>
<h2 id="基础实现">基础实现</h2>
<p>以下是使用 Python 下载 m3u8 视频并保存为 mp4 的示例代码:</p>
<pre><code class="language-python">import requests
import os

def download_m3u8_video(url, file_path):
    r = requests.get(url)
    if r.status_code != 200:
      print('m3u8视频下载链接无效')
      return False

    m3u8_list = r.text.split('\n')
    m3u8_list = != '#']

    ts_list = []
    for ts_url in m3u8_list:
      ts_url = url.rsplit('/', 1) + '/' + ts_url
      ts_list.append(ts_url)

    with open(file_path, 'wb') as f:
      for ts_url in ts_list:
            r = requests.get(ts_url)
            if r.status_code == 200:
                f.write(r.content)
    print('m3u8视频下载完成')
    return True

def convert_ts_to_mp4(ts_file_path, mp4_file_path):
    os.system(f'ffmpeg -i {ts_file_path} -c copy {mp4_file_path}')

if __name__ == '__main__':
    url = '输入m3u8流媒体播放列表文件下载链接'
    ts_file_path = '输入ts文件保存路径'
    mp4_file_path = '输入mp4文件保存路径'

    download_m3u8_video(url, ts_file_path)
    convert_ts_to_mp4(ts_file_path, mp4_file_path)
</code></pre>
<p>在这个示例中,<code>download_m3u8_video</code> 函数用于下载 m3u8 视频,<code>convert_ts_to_mp4</code> 函数用于将下载的 ts 文件转换为 mp4 文件。首先,使用 requests 库下载 m3u8 文件,并解析出其中的 ts 文件链接。然后,遍历 ts 文件链接列表,使用 requests 库下载每个 ts 文件,并将其写入到一个文件中。最后,使用 ffmpeg 工具将下载的 ts 文件转换为 mp4 文件。需要注意的是,为了使用 ffmpeg 工具,需要在系统中安装 ffmpeg,并将其添加到环境变量中。</p>
<p>实际应用中,可以根据具体情况对代码进行调整和优化,例如增加异常处理、优化下载速度等。同时,由于 m3u8 视频格式的特殊性,下载过程可能会较为耗时,需要耐心等待一段时间。</p>
<h2 id="使用多线程来优化下载速度">使用多线程来优化下载速度</h2>
<p>为了优化下载速度,可以使用多线程或异步 IO 的方式来下载 m3u8 视频。以下是使用多线程下载 m3u8 视频的示例代码:</p>
<pre><code class="language-python">import requests
import os
import threading

class Downloader(threading.Thread):
    def __init__(self, url, ts_url, file_path):
      threading.Thread.__init__(self)
      self.url = url
      self.ts_url = ts_url
      self.file_path = file_path

    def run(self):
      r = requests.get(self.ts_url, stream=True)
      if r.status_code == 200:
            with open(self.file_path, 'wb') as f:
                for chunk in r.iter_content(chunk_size=1024):
                  if chunk:
                        f.write(chunk)

def download_m3u8_video(url, file_path):
    r = requests.get(url)
    if r.status_code != 200:
      print('m3u8视频下载链接无效')
      return False

    m3u8_list = r.text.split('\n')
    m3u8_list = != '#']

    ts_list = []
    for ts_url in m3u8_list:
      ts_url = url.rsplit('/', 1) + '/' + ts_url
      ts_list.append(ts_url)

    threads = []
    for i, ts_url in enumerate(ts_list):
      ts_file_path = file_path.rsplit('.', 1) + f'_{i}.ts'
      thread = Downloader(url, ts_url, ts_file_path)
      thread.start()
      threads.append(thread)

    for thread in threads:
      thread.join()

    print('m3u8视频下载完成')
    return True

def convert_ts_to_mp4(ts_file_path, mp4_file_path):
    os.system(f'ffmpeg -i {ts_file_path} -c copy {mp4_file_path}')

if __name__ == '__main__':
    url = '输入m3u8流媒体播放列表文件下载链接'
    ts_file_path = '输入ts文件保存路径'
    mp4_file_path = '输入mp4文件保存路径'

    download_m3u8_video(url, ts_file_path)
    convert_ts_to_mp4(ts_file_path, mp4_file_path)
</code></pre>
<p>在这个示例中,定义了一个 <code>Downloader</code> 类,用于下载每个 ts 文件。在 <code>Downloader</code> 类中,使用 requests 库的 <code>stream</code> 参数将下载进度分块,每次下载 1024 个字节,然后写入到文件中。在 <code>download_m3u8_video</code> 函数中,使用多线程的方式同时下载多个 ts 文件,并等待所有线程下载完成后再将其合并成一个 mp4 文件。这样可以大大缩短下载时间。</p>
<p>需要注意的是,多线程下载可能会导致网络瓶颈,从而降低下载速度。因此,在实际应用中,需要根据具体情况选择合适的下载方式,并进行调整和优化。例如,可以使用异步 IO、协程等技术来优化下载速度。另外,为了提高下载速度,还可以使用 CDN、负载均衡、网络加速等技术来优化下载环节。</p>
<h2 id="使用异步-io-和协程来优化下载速度">使用异步 IO 和协程来优化下载速度</h2>
<p><strong>协程</strong>(Coroutine)是一种轻量级的线程,可以在单线程中实现多个任务的并发执行,从而提高程序的效率和性能。Python 中的协程是通过 async/await 关键字来实现的,可以使用 asyncio 库来进行协程编程。</p>
<p><strong>异步 IO</strong>(Asynchronous IO)是一种非阻塞式 IO 模型,可以在进行 IO 操作时不会阻塞程序的执行,从而提高程序的效率和响应速度。Python 中的异步 IO 是通过 asyncio 库来实现的,可以使用 async/await 关键字和协程来实现异步 IO 操作。</p>
<p><strong>异步 IO 和协程</strong>的结合可以实现高效的<strong>并发编程</strong>,通过异步 IO 可以充分利用 CPU 和网络带宽等资源,提高程序的效率和性能;而通过协程可以在单线程中实现多个任务的并发执行,避免了线程切换的开销,从而提高程序的响应速度和并发性能。在实际应用中,我们可以根据具体情况选择和优化异步 IO 和协程的使用方式,以达到最佳的效果和性能。</p>
<p>为了使用异步 IO 和协程来优化下载速度,可以使用 aiohttp 和 asyncio 库来实现。以下是使用异步 IO 和协程下载 m3u8 视频的示例代码:</p>
<pre><code class="language-python">import aiohttp
import asyncio
import os

async def download_ts_file(ts_url, ts_file_path):
    # 防止ssl报错:
    # aiohttp.client_exceptions.ClientConnectorCertificateError: Cannot connect to host ***.****.com:443 ssl:True
    # certificate verify failed: unable to get local
    # issuer certificate (_ssl.c:1123)')]
    conn = aiohttp.TCPConnector(ssl=False)
    async with aiohttp.ClientSession(connector=conn) as session:
      async with session.get(ts_url) as response:
            if response.status != 200:
                print(f'{ts_url} 下载失败')
                return False
            with open(ts_file_path, 'wb') as f:
                while True:
                  chunk = await response.content.read(1024)
                  if not chunk:
                        break
                  f.write(chunk)
    print(f'{ts_url} 下载完成')
    return True

async def download_m3u8_video(url, file_path):
    # 防止ssl报错
    conn = aiohttp.TCPConnector(ssl=False)
    async with aiohttp.ClientSession(connector=conn) as session:
      async with session.get(url) as response:
            if response.status != 200:
                print('m3u8视频下载链接无效')
                return False

            m3u8_text = await response.text()
            m3u8_list = m3u8_text.split('\n')
            m3u8_list = != '#']

            tasks = []
            for i, ts_url in enumerate(m3u8_list):
                ts_url = url.rsplit('/', 1) + '/' + ts_url
                ts_file_path = file_path.rsplit('.', 1) + f'_{i}.ts'
                task = asyncio.ensure_future(
                  download_ts_file(ts_url, ts_file_path))
                tasks.append(task)

            await asyncio.gather(*tasks)

    print('m3u8视频下载完成')
    return True

def convert_ts_to_mp4(ts_file_path, mp4_file_path):
    os.system(f'ffmpeg -i {ts_file_path} -c copy {mp4_file_path}')

if __name__ == '__main__':
    url = '输入m3u8流媒体播放列表文件下载链接'
    ts_file_path = '输入ts文件保存路径'
    mp4_file_path = '输入mp4文件保存路径'

    loop = asyncio.get_event_loop()
    loop.run_until_complete(download_m3u8_video(url, ts_file_path))
    convert_ts_to_mp4(ts_file_path, mp4_file_path)
</code></pre>
<p>在这个示例中,使用了 aiohttp 和 asyncio 库来实现异步 IO 和协程。定义了两个协程函数:<code>download_m3u8_video</code> 和 <code>download_ts_file</code>。在 <code>download_m3u8_video</code> 函数中,使用 aiohttp 库的 <code>ClientSession</code> 类异步获取 m3u8 文件,并解析出其中的 ts 文件链接。然后,使用协程和异步 IO 的方式异步下载每个 ts 文件,并将其写入到本地文件中。在下载过程中,使用了异步 IO 和协程的方式,可以充分利用网络带宽,提高下载速度。</p>
<p>在 <code>download_m3u8_video</code> 函数中,使用了 <code>async for</code> 循环来遍历 m3u8 文件中的 ts 文件链接,并创建了一个任务列表 <code>tasks</code>,用于存储异步下载的任务。然后,使用 <code>asyncio.ensure_future</code> 方法将每个任务添加到任务列表中。最后,使用 <code>asyncio.gather</code> 方法同时运行所有异步任务,等待所有任务完成后,即可完成整个 m3u8 视频的下载。</p>
<p>最后,使用 <code>ffmpeg</code> 工具将下载的 ts 文件转换为 mp4 格式的视频文件。这个步骤并不涉及异步 IO 和协程,只是为了将下载的 ts 文件转换为可用的视频文件格式。</p>
<p>使用协程可以充分利用网络带宽,提高下载速度。需要注意的是,在使用协程时,需要考虑到 CPU 和内存等资源的占用,避免出现资源耗尽或者死锁等问题。同时,协程的使用需要掌握一定的异步编程技巧,例如使用 <code>async/await</code> 关键字、协程调度等。因此,在实际应用中,需要根据具体情况进行调整和优化,以获取最佳的性能和效果。</p><br><br>
来源:https://www.cnblogs.com/yuzhihui/p/17443299.html
頁: [1]
查看完整版本: Python 实现 m3u8 视频下载