分享

​Python爬虫:使用协程下载m3u8视频

 O听_海_轩O 2022-03-05

前言

学了一阵子,终于把进程、推荐进程、看协程给了明白了,执行任务完成的精彩视频:https://www.bilibili.com/video/BV1bK411A7tV?p =1。人人影视最新一集的国王人物:https://www./play/40479-1-18.html

一,页面分析

图片

通过抓包我们很容易发现这个包,但是内容却没有显示可用的数据。并保存为 m3u8 格式,打开发现结果如下:

图片

所以里面的这个是png吗?于是我们的一个发现请求视频验证,并以我们对文件的形式进行后,文件名后缀。结果结束下载完成。正是想要的文件打开!

那如何通过视频的URL定位到index.m3u8地址呢?通过网页代码,搜索关键字m3u8很容易定位到,如下图:

图片

至此,我们已经完成,通过正则匹配的地址,如下所示:

    def get_m3u8_url(self):
        # 根据剧集网址,获取m3u8下载索引
        res =requests.get(url=self.url,headers=self.headers).text
        first_m3u8_url = re.findall(';var now="(.*?)"',res,re.S)[0]     # 获取m3u8下载地址
        self.name = re.findall("<li>正在播放:(.*?)</li>",res,re.S)[0]   # 获取下载片名
        print(self.name+"准备开始下载")
        res_second = requests.get(url=first_m3u8_url,headers=self.headers).text
        with open(self.name+".m3u8",'w',encoding='utf-8'as f:
            f.write(res_second)

二,获取m3u8播放列表

根据上一步下载的m3u8文件,提取下载地址,代码如下:

    def get_ts_url(self):
        # 根据m3u8下载索引,获取各个下载地址
        ts_url_list = []
        with open(self.name+".m3u8",'r',encoding='utf-8'as f:
            for line in f:
                if line.startswith('#'):
                    continue
                line = line.strip()
                ts_url_list.append(line)
        return ts_url_list

三、编码实现

然后下一步我们就开始下载视频了,完整代码如下:

# -*- coding:utf-8 -*-
import time
import re
import os
import requests
import asyncio
import aiohttp
import aiofiles
import nest_asyncio
nest_asyncio.apply()

class RRyy123:
    def __init__(self):
        self.url = None
        self.name = None
        self.headers = {'User-Agent':'Mozilla / 5.0(Windows NT 10.0;Win64;x64) AppleWebKit / 537.36(KHTML, likeGecko) Chrome / 95.0.4638.69Safari / 537.36'}


    def get_m3u8_url(self):
        # 根据剧集网址,获取m3u8下载索引
        res =requests.get(url=self.url,headers=self.headers).text
        first_m3u8_url = re.findall(';var now="(.*?)"',res,re.S)[0]     # 获取m3u8下载地址
        self.name = re.findall("<li>正在播放:(.*?)</li>",res,re.S)[0]   # 获取下载片名
        print(self.name+"准备开始下载")
        res_second = requests.get(url=first_m3u8_url,headers=self.headers).text
        with open(self.name+".m3u8",'w',encoding='utf-8'as f:
            f.write(res_second)


    def get_ts_url(self):
        # 根据m3u8下载索引,获取各个ts文件地址
        ts_url_list = []
        with open(self.name+".m3u8",'r',encoding='utf-8'as f:
            for line in f:
                if line.startswith('#'):
                    continue
                line = line.strip()
                ts_url_list.append(line)
        return ts_url_list


    async def download_ts(self,url,session,sem):
        # 使用协程下载单个ts文件
        ts_name = os.path.splitext(url)[0].split("/")[-1]
        async with sem:     # 控制协程信号量
            async with session.get(url =url) as res:
                if not os.path.exists(f"./temp_{self.name}"):
                    os.makedirs(f"./temp_{self.name}/")
                async with aiofiles.open(f"./temp_{self.name}/{ts_name}.ts",'wb'as f:
                    await f.write(await res.read())
                    print(f"{ts_name}.ts下载完成")

    async def main(self):
        # 根据m3u8文件中的ts网址,下载ts视频
        tasks = []
        self.get_m3u8_url()
        ts_url_list = self.get_ts_url()
        sem = asyncio.Semaphore(5)  # 设置信号量,控制异步数量
        async with aiohttp.ClientSession(headers=self.headers) as session:
            for ts_url in ts_url_list:
                tasks.append(asyncio.create_task(self.download_ts(url=ts_url,session=session,sem=sem)))
            await asyncio.wait(tasks)



if __name__ == '__main__':
    t = time.time()
    f = RRyy123()
    f.url = "https://www.r/video/?40479-1-18.html"
    asyncio.run(f.main())
    print(f"{f.name}下载完成,总耗时{int(time.time()-t)}s.")

我测了一下,下载一个128M的文件只要15s!!!

图片

四,ts文件合并

下载完成后我们要对ts文件进行合并了,这里推荐使用ffmpeg,windows下载地址:http:///download.html#build-windows。这里要注意的是将解压后的文件目录中的bin目录(包含 ffmpeg.exe )添加使用进路径环境变量中。 ffmpeg 合并文件方法非常简单,只需要在终端输入命令行命令:

ffmpeg -f concat -i filename.txt -c copy output.mp4

首先是在ts文件相同的目录中,把所有要合并的ts文件名保存在filename.txt:如下图:图片

更好的替代方案替代文件,这里,要使用后修改文件的路径,在使用后,可以阻止发送到此处: 以便在此处草率提交文件。

import send2trash
import os

def merge_video(name):
    # 合并视频
    ts_url_list=[]
    with open(name + ".m3u8"'r', encoding='utf-8'as f:
        for line in f:
            if line.startswith('#'):
                continue
            line = line.strip()
            ts_url_list.append(line)

    f = open(f"./temp_{name}/filename.txt" ,'w' ,encoding="utf-8")
    for ts_url in ts_url_list:
        ts_name = os.path.splitext(ts_url)[0].split("/")[-1]
        f.write("file  "+ ts_name +".ts\n")
    f.close()
    print(f"{name}开始合并。。。。")
    os.system(f"ffmpeg -f concat -i ./temp_{name}/filename.txt -c copy {name}.mp4")
    print(f"{name}视频合成成功")



def remove_temp(name):
    # 删除ts文件,放入回收站
    send2trash.send2trash(f"./temp_{name}")
    send2trash.send2trash(f"{name}.m3u8")
    print("临时文件已删除。")

if __name__ == '__main__':
    name = "国王排名-第19集"
    merge_video(name)
    remove_temp(name)

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多