在这篇文章中,我们将一步一步地讲解如何抓取和下载M3U8视频,介绍使用Python进行网络爬虫的基础知识,并提供一个完整的代码示例来帮助你轻松掌握这个技能。无论你是编程新手还是有经验的开发者,都能从中学到实用技巧。


你是否曾经遇到过这样的问题:在网上看到一个很棒的视频,却无法下载保存?特别是那些以M3U8格式提供的视频,下载起来更是复杂。今天,我们将通过一个实战案例,教你如何使用Python爬虫技术来抓取和下载M3U8视频。

什么是M3U8?

在开始之前,我们需要了解什么是M3U8。M3U8是一种播放列表文件格式,常用于在线视频流媒体。它包含了媒体文件的路径,可以是本地路径,也可以是网络路径。通过M3U8文件,播放器能够按照顺序播放各个视频片段,从而实现流畅的播放体验。

工具准备

在开始编写代码之前,我们需要安装一些必备的Python库:

pip install aiohttp aiofiles requests

这些库分别用于异步HTTP请求、异步文件操作和常规HTTP请求。

代码解析

接下来,我们将一步步解析完整的代码示例,帮助你更好地理解每一部分的功能。

1. 获取电影URL

首先,我们需要获取电影页面的真实URL。因为有些网站的电影链接会包含特殊的标记或结构,通过以下函数我们可以确保获取到正确的URL:

def get_movie_url(movie_url):
    if not movie_url.endswith('/1-1.html'):
        movie_url = movie_url.replace('.html', '/1-1.html')
    return movie_url

2. 提取第一个M3U8链接和电影名称

接下来,我们需要从电影页面中提取第一个M3U8链接和电影名称:

def get_first_m3u8_url(movie_url):
    resp = requests.get(movie_url, headers=headers)
    pattern = re.compile(r'url":"(.*?)"')
    pattern_name = re.compile(r'class="hl-infos-title" href="(.*?)" title="(.*?)">')
    result = pattern.search(resp.text)
    result_name = pattern_name.search(resp.text)
    movie_name = result_name.group(2)
    encoded_url = result.group(1)
    decoded_url = urllib.parse.unquote(encoded_url)
    first_m3u8_url = decoded_url.split('url=')[-1]
    return first_m3u8_url, movie_name

3. 处理第一个M3U8链接

我们需要下载第一个M3U8文件,并从中提取出第二个M3U8链接:

def deal_first_m3u8(first_m3u8_url, movie_name):
    if not os.path.exists(f'{movie_name}/2.m3u8'):
        if not os.path.exists(movie_name):
            os.makedirs(movie_name)
        if not os.path.exists(f'{movie_name}/1.m3u8'):
            file_name = '1.m3u8'
            file_path = f'{movie_name}/{file_name}'
            resp = requests.get(first_m3u8_url, headers=headers)
            with open(file_path, 'w', encoding='utf-8') as f:
                f.write(resp.text)
            second_m3u8_url = ''
            with open(file_path, 'r', encoding='utf-8') as f:
                for line in f:
                    if line.startswith('#') or line == '\n':
                        continue
                    second_m3u8 = line.strip()
        second_m3u8_url = urljoin(first_m3u8_url, second_m3u8)
        file_name = '2.m3u8'
        file_path = f'{movie_name}/{file_name}'
        resp = requests.get(second_m3u8_url, headers=headers)
        with open(file_path, 'w', encoding='utf-8') as f:
            f.write(resp.text)

4. 解析第二个M3U8文件

然后,我们解析第二个M3U8文件,获取电影片段的URL列表:

def deal_second_m3u8(movie_name):
    file_path = f'{movie_name}/2.m3u8'
    lst_movies = []
    with open(file_path, 'r', encoding='utf-8') as f:
        for line in f:
            if line.startswith('#') or line == '\n':
                continue
            lst_movies.append(line.strip())
    return lst_movies

5. 异步下载电影片段

使用异步方式下载电影片段,可以提高下载速度:

async def download(down_url, file_path, sem):
    file_name = file_path + '/' + down_url[-13:]
    if not os.path.exists(file_name):
        for i in range(5):
            async with sem:
                try:
                    print(f'{file_name}开始下载。')
                    async with aiohttp.ClientSession() as session:
                        async with session.get(down_url) as resp:
                            content = await resp.content.read()
                            async with aiofiles.open(file_name, 'wb') as f:
                                await f.write(content)
                    print(f'{file_name}下载完成。')
                    break
                except Exception as e:
                    print(f'{file_name}下载失败,已重新下载,错误信息为:{e}')
                    continue

6. 合并电影片段

最后,我们将所有下载好的电影片段合并成一个完整的电影文件:

def merge_movie(lst_movies, movie_name):
    temp = []
    n = 1
    now_path = os.getcwd()
    path = f'{movie_name}/before_synthesis'
    os.chdir(path)
    for i in range(len(lst_movies)):
        file_name = lst_movies[i][-13:]
        temp.append(file_name)
        if len(temp) == 20:
            cmd = f'copy /b {"+".join(temp)} {n}.ts'
            r = os.popen(cmd)
            print(r.read())
            n += 1
            temp = []
    cmd = f'copy /b {"+".join(temp)} {n}.ts'
    r = os.popen(cmd)
    print(r.read())
    last_temp = []
    for i in range(1, n + 1):
        last_temp.append(f'{i}.ts')
    cmd = f'copy /b {"+".join(last_temp)} {movie_name}.mp4'
    r = os.popen(cmd)
    print(r.read())
    os.chdir(now_path)

主函数

if __name__ == '__main__':
    movie_url = 'https://www.7qhb.com/vod/qthl2024.html'
    movie_url = get_movie_url(movie_url)
    first_m3u8_url, movie_name = get_first_m3u8_url(movie_url)
    second_m3u8_url = deal_first_m3u8(first_m3u8_url, movie_name)
    lst_movies = deal_second_m3u8(movie_name)
    asyncio.run(main(lst_movies, movie_name))
    merge_movie(lst_movies[:], movie_name)
    print(len(lst_movies))
    print('电影下载完成!')

总结

通过这篇文章,我们从零开始,详细讲解了如何使用Python抓取和下载M3U8视频。这个过程涉及到网络请求、正则表达式、文件操作以及异步编程等多个知识点。希望这篇文章能帮助你掌握这些技能,为你在编程道路上提供一些有用的启示。

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。