Python正式课15_协程案例_爬取m3u8的视频

笔记:介绍cdn是什么

笔记2:什么时候用协程?大批量任务的时候

import requests
import re
from urllib.parse import urljoin

import asyncio
import aiohttp
import aiofiles

# 本次作为练习,代码还需要填参数,整理
# 建议将网页看一遍截图,方便下次回顾一下为什么re来正则匹配更方便啥的

session = requests.session()
head = {...}

session.headers = head


def get_m3u8_url(url):
    resp = session.get(url)
    obj = re.compile(r'"link_pre":"","url":"(?P<m3u8_url>.*?)",', re.S)
    m3u8_url = obj.search(resp.text).group("m3u8_url")
    m3u8_url = m3u8_url.replace("\\", "")  # 去掉\

    # 问题分析(为什么re匹配不到)
    # 1、页面源代码不对
    # 2、正则写的不对

    """"
    <script type="text/javascript">
        var player_aaaa = {
            "flag": "play",
            "encrypt": 0,
            "trysee": 0,
            "points": 0,
            "link": "\/vodplay\/37759-1-1.html",
            "link_next": "\/vodplay\/37759-1-2.html",
            "link_pre":"","url":?????????????????????????????????????????????????????"https:\/\/v7.tlkqc.com\/wjv7\/202309\/12\/tQe5up7W5v1\/video\/index.m3u8",
            "url_next": "https:\/\/v7.tlkqc.com\/wjv7\/202309\/12\/gNjMb1dMF91\/video\/index.m3u8",
            "from": "wjm3u8",
            "server": "no",
            "note": "",
            "id": "37759",
            "sid": 1,
            "nid": 1
        }
    </script>

    """
    return m3u8_url
    # 加快速度
    # return "https://v7.tlkqc.com/wjv7/202309/12/tQe5up7W5v1/video/index.m3u8"


def download_m3u8(url):
    resp = session.get(url)
    with open('index.m3u8', mode='wb') as f:
        f.write(resp.content)


def has_next_m3u8():
    with open('index.m3u8', mode='r', encoding="utf-8") as f:
        for line in f:
            if line.startwith("EXT-X-STREAM-INF"):
                return f.readline().strip()  # 读取下一行,作为下一层M3U8的地址

    return False  # ????  A:也就是读的到东西就有,没有读到就没有


# 下载单独一个ts
async def download_one(ts_url, file_name, sem):
    print(file_name, "开始下载")
    async with sem:  # 控制并发量的, 你的windows是有上限的..

        while 1:
            try:
                # 下载
                # 不能所有的任务都傻等这,不知道什么时候才能回得来
                tm = aiohttp.ClientTimeout(total=10)  # 设置10秒的等到时间,超过了就报错
                async with aiohttp.ClientSession(headers=head, timeout=tm) as sess:
                    async with sess.get(ts_url) as resp:
                        content = await resp.content.read()
                        async with aiofiles.open(f"source/{file_name}", mode="wb") as f:
                            await f.write(content)
                            break
            except Exception as e:
                print(file_name, "下载失败,即将重新下载")
    print(file_name, "下载完毕")


async def download_all_ts(m3u8_url):
    tasks = []

    # 信号量,控制并发量
    sem = asyncio.Semaphore(100)

    f2 = open("new_index.m3u8", mode="w", encoding="utf-8")

    with open("index_m3u8", mode='r', encoding="utf-8") as f:
        for line in f:
            line = line.strip()  # 一定要有,否则文件下载地址是不对的
            if line.startwith("#"):
                f2.write(line)
                f2.write("\n")
                continue
            if not line.startwith("http"):
                line = urljoin(m3u8_url, line)

            # line就是ts下载地址了
            # 这里取文件名字
            file_name = line.split("/")[-1]
            f2.write(file_name)
            f2.write("\n")
            # 去下载就可以了,每一个line就是一个协程任务
            t = asyncio.create_task(download_one(line, file_name, sem))
            tasks.append(t)
        await asyncio.wait(tasks)

    f2.flush()
    f2.close()


def merge():
    # 1. 可以直接用py代码合并... 参考4期的协程案例
    # 2. 用ffmpeg第三方工具来合并
    # 2.1 安装
    #    github已经开源的工具. 需要自己去编译. 很麻烦
    #    直接用樵夫提供的安装包即可
    #    1. 下载, 解压缩到硬盘的任何为止
    #    2. 复制bin路径
    #    3. 丢到环境变量的Path变量中.
    #    4. 重启黑窗口. 输入ffmpeg. 有反应就成功了.
    #    5. pycharm的terminal可以充当黑窗口的角色, 但是需要
    #    更换terminal的引擎
    #       files -> settings -> tools -> terminal-> 把powershell更换成cmd
    # 2.2 使用
    #    ffmpeg -i xxx.m3u8 -c copy xxx.MP4

    # 在合并之前, 把所有的jpeg, 更换成ts. 重新生成一个新的m3u8文件

    print("开始合并, 第一件事儿, 处理M3U8")
    print("正常情况下, 你应该先去判断是否是jpeg, 如果不是jpeg, 直接合并就可以了")
    with open("new_index.m3u8", mode="r", encoding="utf-8") as f1, \
            open("ok/new_new_index.m3u8", mode="w", encoding="utf-8") as f2:
        for line in f1:
            # 生成新的m3u8
            f2.write(line.replace(".jpeg", ".ts"))

            # 处理文件
            # 所有的jpeg, 需要把前面4个字节更换成\xff
            if line.startswith("#"):
                continue

            line = line.strip()

            # jpeg文件
            with open("source/" + line, mode="rb") as f3,\
                open("ok/" + line.replace(".jpeg", ".ts"), mode="wb") as f4:  # ts文件
                f4.write(f3.read())
                f4.seek(0)  # 鼠标光标重新回到起点的意思
                f4.write(b"\xff\xff\xff\xff")
    print("jpeg处理完毕")

    # 执行合并命令
    # 进入到ok文件夹
    import os
    os.chdir("ok")  # 让程序进入ok文件夹
    # 执行合并命令
    rt = os.popen("ffmpeg -i new_new_index.m3u8 -c copy xxx.mp4").read()
    print(rt)

    # 回到原来的文件夹
    os.chdir("../")
    print("合并成功")


def main():
    url = "https://www.nangua2025.com/vodplay/37759-1-1.html"

    # 访问url获取m3u8下载地址
    m3u8_url = get_m3u8_url(url)

    print("m3u8地址为:", m3u8_url)

    # 2、 下载m3u8文件
    download_m3u8(m3u8_url)
    print("下载m3u8成功", m3u8_url)

    # 3、判断是否有下一层 ??????
    m3u8_url_2 = has_next_m3u8()
    while m3u8_url_2:
        # 有下一层
        m3u8_url = urljoin(m3u8_url, m3u8_url_2)
        # 下载文件
        download_m3u8(m3u8_url)
        print("下载m3u8成功", m3u8_url)
        # 判断是否还有下一层
        m3u8_url_2 = has_next_m3u8()
        print("是否有下一层")

    # 4、下载所有的ts/???文件
    loop = asyncio.get_event_loop()
    loop.run_until_complete(download_all_ts(m3u8_url))

    # 5、ts文件的合并
    merge()


if __name__ == '__main__':
    main()

注意的点1:br压缩问题,导致这个返回内容乱码。

比如15行这一段(注释的这段,一定要小心!):

不然就会如下乱码:

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值