您的位置:首页 >聚焦 >

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

2022-03-04 19:59:56    来源:程序员客栈
前言

学了一段时间爬虫,终于把进程、线程、协程给搞明白了,还没搞明白的强烈推荐看一下蚂蚁老师B站的视频:https://www.bilibili.com/video/BV1bK411A7tV?p=1,思路很清晰。爬虫属于IO密集型任务,且资源开销少,对于爬虫来说很有优势,对于使用爬虫下载视频,那效率就更高了。我们以人人影视最新一集的国王排名为例:https://www.rryy123.com/play/40479-1-18.html

一,页面分析

通过抓包我们很容易发现index.m3u8这个包,但是响应内容却没显示可用的数据。为了验证一下我们对这个网址https://c2.monidai.com/20220225/yOm07IlJ/index.m3u8发起请求,并保存为m3u8格式,打开发现得到的结果如下:

但是发现里面的网址是以png结尾?于是我们对上面的一个网址发起请求进行验证,并以二进制写入文件,文件名后缀以.ts结尾。下载完成后打开正是我们想要的视频文件!

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

至此,第一步我们已经完成,通过正则匹配可以获得对应地址,代码如下:

defget_m3u8_url(self):#根据剧集网址,获取m3u8下载索引res=requests.get(url=self.url,headers=self.headers).textfirst_m3u8_url=re.findall(";varnow="(.*?)"",res,re.S)[0]#获取m3u8下载地址self.name=re.findall("
  • 正在播放:(.*?)
  • ",res,re.S)[0]#获取下载片名print(self.name+"准备开始下载")res_second=requests.get(url=first_m3u8_url,headers=self.headers).textwithopen(self.name+".m3u8","w",encoding="utf-8")asf:f.write(res_second)

    二,获取m3u8播放列表

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

    defget_ts_url(self):#根据m3u8下载索引,获取各个下载地址ts_url_list=[]withopen(self.name+".m3u8","r",encoding="utf-8")asf:forlineinf:ifline.startswith("#"):continueline=line.strip()ts_url_list.append(line)returnts_url_list

    三,编码实现

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

    #-*-coding:utf-8-*-importtimeimportreimportosimportrequestsimportasyncioimportaiohttpimportaiofilesimportnest_asyncionest_asyncio.apply()classRRyy123:def__init__(self):self.url=Noneself.name=Noneself.headers={"User-Agent":"Mozilla/5.0(WindowsNT10.0;Win64;x64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/95.0.4638.69Safari/537.36"}defget_m3u8_url(self):#根据剧集网址,获取m3u8下载索引res=requests.get(url=self.url,headers=self.headers).textfirst_m3u8_url=re.findall(";varnow="(.*?)"",res,re.S)[0]#获取m3u8下载地址self.name=re.findall("
  • 正在播放:(.*?)
  • ",res,re.S)[0]#获取下载片名print(self.name+"准备开始下载")res_second=requests.get(url=first_m3u8_url,headers=self.headers).textwithopen(self.name+".m3u8","w",encoding="utf-8")asf:f.write(res_second)defget_ts_url(self):#根据m3u8下载索引,获取各个ts文件地址ts_url_list=[]withopen(self.name+".m3u8","r",encoding="utf-8")asf:forlineinf:ifline.startswith("#"):continueline=line.strip()ts_url_list.append(line)returnts_url_listasyncdefdownload_ts(self,url,session,sem):#使用协程下载单个ts文件ts_name=os.path.splitext(url)[0].split("/")[-1]asyncwithsem:#控制协程信号量asyncwithsession.get(url=url)asres:ifnotos.path.exists(f"./temp_{self.name}"):os.makedirs(f"./temp_{self.name}/")asyncwithaiofiles.open(f"./temp_{self.name}/{ts_name}.ts","wb")asf:awaitf.write(awaitres.read())print(f"{ts_name}.ts下载完成")asyncdefmain(self):#根据m3u8文件中的ts网址,下载ts视频tasks=[]self.get_m3u8_url()ts_url_list=self.get_ts_url()sem=asyncio.Semaphore(5)#设置信号量,控制异步数量asyncwithaiohttp.ClientSession(headers=self.headers)assession:forts_urlints_url_list:tasks.append(asyncio.create_task(self.download_ts(url=ts_url,session=session,sem=sem)))awaitasyncio.wait(tasks)if__name__=="__main__":t=time.time()f=RRyy123()f.url="https://www.rryy123.com/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://ffmpeg.org/download.html#build-windows。这里要注意的是 将解压后的文件目录中 bin 目录(包含 ffmpeg.exe )添加进 path 环境变量中。使用 ffmpeg 合并ts 文件方法非常简单,只需要在终端输入一行命令:

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

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

    然后使用命令行进行合并,这里要注意文件路径,合并完成后删除ts文件,这里为了防止误删使用send2trash库,删除内容可以放进回收站,代码如下:

    importsend2trashimportosdefmerge_video(name):#合并视频ts_url_list=[]withopen(name+".m3u8","r",encoding="utf-8")asf:forlineinf:ifline.startswith("#"):continueline=line.strip()ts_url_list.append(line)f=open(f"./temp_{name}/filename.txt","w",encoding="utf-8")forts_urlints_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-fconcat-i./temp_{name}/filename.txt-ccopy{name}.mp4")print(f"{name}视频合成成功")defremove_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)

    最后,推荐蚂蚁老师的Python课程:

    如果购买课程,加微信:ant_learn_python

    找蚂蚁老师,进群、领资料

    关键词: 下载地址 正在播放 临时文件

    相关阅读