0
点赞
收藏
分享

微信扫一扫

爬虫-python -(10) 协程操作 异步操作-复杂模板

WikongGuan 2022-01-12 阅读 75

文章目录

1.91视频-未用协程方式

需要找到m3u8文件,并找到对应视频片段。
91看剧
只能实现以下播放方式

'''
1.爬取http://91kanju2.com/vod-play/61282-1-1.html信息
2.提取m3u8下载url
3.下载m3u8
4.读取m3u8文件,下载视频
5.合并视频,写入的时候往后加,就可以叠加到一起
'''
from os import name
import requests
import re

# #获取网站的源码的m3u8 以及保存m3u8文件
# url = 'http://91kanju2.com/vod-play/60906-1-3.html'  #只需要修改对应的url 就可以获得响应的m3u8文件
# obj1 = re.compile(r"url:\s+'(?P<urler>.*?m3u8)'",re.S)
# obj2 = re.compile(r'正在播放.*?">(?P<name>.*?)</a></div>',re.S)
# heard = {
#      'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36 Edg/97.0.1072.55'
# }
# resp = requests.get(url,headers=heard)
# respconter= resp.text
# resp.close()
# m3u8Url = obj1.search(respconter).group('urler')
# m3u8name = obj2.search(respconter).group('name').replace('&nbsp;',' ')
# print(m3u8name,m3u8Url)
# #获取m3u8
# resp = requests.get(m3u8Url,headers=heard)
# with open(f'{m3u8name}.m3u8',mode='w') as f:
#     f.write(resp.text)
# resp.close()


#读取下载的m3u8 下载视频
conter=2
with open(f'凡人修仙传 第02集.m3u8',encoding='utf-8',mode='r') as f:
    for line in f:
        if line[0]=='#':continue
        if line =='\n' :continue
        print([line])
        resp = requests.get(line)
        f = open(f"./视频下载/{conter}.ts",mode='ab')  #保存视频
        f.write(resp.content)
        f.close   
        print(line,'over!!')    
        #conter+=1   #这行显示就会下载很多片段,如果选上就会把他们下载到一起

改进版

'''
1.爬取http://91kanju2.com/vod-play/61282-1-1.html信息
2.提取m3u8下载url
3.下载m3u8
4.读取m3u8文件,下载视频
5.合并视频
'''
from os import name
import requests
import re
from tqdm import tqdm
import os
from concurrent.futures import ThreadPoolExecutor

# #获取网站的源码的m3u8 以及保存m3u8文件 等一段时间就要更新 m3u8会失效
# url = 'http://91kanju2.com/vod-play/60906-1-3.html'  #只需要修改对应的url 就可以获得响应的m3u8文件
# obj1 = re.compile(r"url:\s+'(?P<urler>.*?m3u8)'",re.S)
# obj2 = re.compile(r'正在播放.*?">(?P<name>.*?)</a></div>',re.S)
# heard = {
#      'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36 Edg/97.0.1072.55'
# }
# resp = requests.get(url,headers=heard)
# respconter= resp.text
# resp.close()
# m3u8Url = obj1.search(respconter).group('urler')
# m3u8name = obj2.search(respconter).group('name').replace('&nbsp;',' ')
# print(m3u8name,m3u8Url)
# #获取m3u8
# resp = requests.get(m3u8Url,headers=heard)
# with open(f'{m3u8name}.m3u8',mode='w') as f:
#     f.write(resp.text)
# resp.close()


failure_list = []  # 保存下载失败的片段
def downloadTS(num,urlts, flag=0):
    #读取下载的m3u8 下载视频
    with open(f'./视频下载/{num}.ts', mode='wb') as f:
        try:
            r = requests.get(urlts, timeout=5)
            r.raise_for_status()
            print('正在下载第 {} 个片段。'.format(num))
            f.write(r.content)
            if flag == 1:
                failure_list.remove([num,urlts])
        except:
            print('请求失败!')
            if num not in failure_list:
                failure_list.append([num,urlts])

def downloadMV(m3u8name):
    #m3u8name ='凡人修仙传 第02集.m3u8'
    conter=1
    with open(m3u8name,encoding='utf-8',mode='r') as f:
        for line in f:
            if line[0]=='#':continue
            if line =='\n' :continue
            downloadTS(conter,line, flag=0)
            conter+=1
    check_ts()

def check_ts():
    print("开始检查:")
    while failure_list:
        for num,rults in failure_list:
            downloadTS(num,rults, 1)
    print("ts 文件下载完成!")

def get_video(name):
    files = os.listdir("./视频下载/")
    files.sort(key=lambda x:int(x.split('.')[0])) #对.进行切片,并取列表的第一个值(左边的文件名)转化整数型 排序
    for file in tqdm(files, desc="正在转换视频格式:"):
        if os.path.exists("./视频下载/" + file):
            with open("./视频下载/" + file, 'rb') as f1:
                with open('./'+name+".mp4", 'ab') as f2:
                    f2.write(f1.read())
        else:
            print("失败")

if __name__ =='__main__':
    m3u8name ='凡人修仙传 第01集.m3u8'
    downloadMV(m3u8name)
    get_video(m3u8name.split('.')[0])

2.文件打开模式-备用

3.协程方法下载-复杂

我可以将所有的片段先下载下来,然后再合并,
这里爬虫了
网吧电影

'''
1.拿到主页面的源代码,找到iframe
2.从iframe的页面找到m3u8文件地址
3.下载第一层m3u8文件 下载第二层m3u8文件
4.下载视频
5.下载密钥,进行解密 或不解密
6.合并ts文件为mp4文件
'''
import requests
from bs4 import BeautifulSoup
import re
import asyncio
import aiofiles
import aiohttp
from  Crypto.Cipher import AES
from requests.api import get
import time
import os
from tqdm import tqdm

failure_list = []  # 保存下载失败的片段
def get_iframe_src(url):   #得到网站的iframe 就是视频网站内嵌套播放链接
    resp = requests.get(url)
    respconter = resp.text
    resp.close()
    page = BeautifulSoup(respconter,'html.parser')
    iframe_src = page.find('iframe').get('src')
    return '/'.join(url.split('/')[:3])+iframe_src
def get_first_m3u8_src(url): #从视频网站 找到对应的m3u8文件 这只是第一层
    obj = re.compile(r'"(?P<m3u8_url>.*?.m3u8)",')
    resp = requests.get(url)
    resp.close()
    reapconter = resp.text
    m3u8_url = obj.search(reapconter).group('m3u8_url')
    return m3u8_url
def down_m3u8(url):   #从第二层m3u8文件得到最终的1.m3u8文件
    resp = requests.get(url)
    second_m3u8 = '/'.join(url.split('/')[:3])+resp.text.split('\n')[-2]
    resp.close()
    resp= requests.get(second_m3u8)
    #print(resp.text)
    with open('1.m3u8',mode='wb') as f:
        f.write(resp.content)
    resp.close()    

async def aio_down_ts(url,name,seesion,key,flag=0):    #下载加密的ts文件 并解密
    if key:cryptor = AES.new(key, AES.MODE_CBC,key)                            # 利用密钥进行解密操作
    async with aiofiles.open(f'.\视频下载\{name}.ts',mode='wb') as f: #需要利用aiofiles来打开文件,也是异步操作
        try:
            async with seesion.get(url,timeout=20) as resp:                     #打开下载网站
                print('正在下载第 {} 个片段。'.format(name))
                t = await resp.content.read()                                  #使用协程时候需要使用read() 平常不用
                if key:await f.write(cryptor.decrypt(t))                        #利用密钥解密并写入文件
                else:  await f.write(t)                                         #不需要解密的直接写入
                if flag == 1:
                   failure_list.remove([name,url])
        except:
            print('第{}个片段请求失败!'.format(name))
            if [name,url] not in failure_list:
                failure_list.append([name,url])

async def aio_down_video(filename,key):   #下载加密的视频 嵌套上面
    tasks=[]
    name=1
    async with aiohttp.ClientSession() as seesion:
        async with aiofiles.open(filename,mode='r',encoding='utf-8') as f:
            async for line in f:
               if line[0]=='#' or line=='\n' :continue
               line= line[:-1]
               task = asyncio.create_task(aio_down_ts(line,name,seesion,key))    #创建任务
               tasks.append(task)                                                #添加任务
               name+=1
            await asyncio.wait(tasks)                                            #等待任务的完成
def get_key(filename):                   #从m3u8文件找到key 如果找不到key 说明不需要key
     with open(filename,mode='r',encoding='utf-8') as f:
         for line in f:  #只找前10行 没必要找太多
             if '#EXT-X-KEY' in line: 
                 key_url = line.split('"')[1]
                 resp= requests.get(key_url)
                 key = resp.content
                 resp.close()
                 #cryptor = AES.new(key, AES.MODE_CBC,key)
                 print('Get key!!!')
                 return key
         print('视频没有加密!!')
         return None  #找不到 就不存在key

async def aio_check_download(failure_list,key): #嵌套在链接失败内,作为协程去访问失败链接
    tasks=[]
    async with aiohttp.ClientSession() as seesion:    
        for name,url in failure_list:
            task = asyncio.create_task(aio_down_ts(url,name,seesion,key,1))    #创建任务
            tasks.append(task)                                                #添加任务
        await asyncio.wait(tasks)                                            #等待任务的完成  

def check_ts(key):   #将链接失败的从新下载
    print("开始检查:")
    while failure_list:
        loop = asyncio.get_event_loop()
        loop.run_until_complete(aio_check_download(failure_list,key))
    print("ts 文件下载完成!")
def get_video(name): #合并视频碎片 并保存在主目录下
    files = os.listdir("./视频下载/")
    files.sort(key=lambda x:int(x.split('.')[0])) #对.进行切片,并取列表的第一个值(左边的文件名)转化整数型 排序
    for file in tqdm(files, desc="正在转换视频格式:"):
        if os.path.exists("./视频下载/" + file):
            with open("./视频下载/" + file, 'rb') as f1:
                with open('./'+name+".mp4", 'ab') as f2:
                    f2.write(f1.read())
        else:
            print("失败")
def main(url):
    #1.拿到主页面的源代码,找到iframe
    iframe_src = get_iframe_src(url)
    #2.从iframe的页面找到m3u8文件地址
    first_m3u8_src = get_first_m3u8_src(iframe_src)
    #3.下载第一层m3u8文件 下载第二层m3u8文件
    down_m3u8(first_m3u8_src)
    key =get_key('1.m3u8')
    #4.下载视频 异步操作 同时异步解码
    loop = asyncio.get_event_loop()
    loop.run_until_complete(aio_down_video('1.m3u8',key))
    #5.将超时链接从新下载
    check_ts(key)  
    #6.合并视频文件
    get_video('雪中悍刀行 第3集')

#注意开始要删除视频下载下的文件 或者通过程序改进 这里暂时不做改进了
if __name__ == "__main__":
    url = 'http://www.wbdy.tv/play/65474_2_3.html'
    t1 = time.time()
    main(url)
    t2 = time.time()
    print('总共用时:'+str(t2-t1)+'s.')

在这里插入图片描述

4.总结

看了两天终于把它看完了。。。

举报

相关推荐

0 条评论