天天看点

一个废物大学生对于视频爬取的小小的总结

爬取视频的总结

当时看到了与fate有关的视频感觉挺不错的,所以想把这些视频爬取下来。于是做了一个脚本进行爬取,废话不说上的代码。

1.网页分析

本次爬取数据来源与一个网站:

布米米

http://www.bumimi66.com/

网站内容还挺全的,里面基本上什么视频都有。

这里我们对其中一个视频进行分析

http://www.bumimi66.com/acg/2641/3.html

视频内容一般是动态加载的,对于动态加载的数据我们有

四种获取方法:

1.通过selenium来获取动态页面源码

2.xhr数据(json包,xhr包)

3.js数据

4.通过观察url的规律来获取

这里我们直接尝试抓包工具

打开抓包工具发现:

一个废物大学生对于视频爬取的小小的总结
一个废物大学生对于视频爬取的小小的总结

发现了这两个包,易知media包是由js包动态加载出来的并且js包也好拼接,所以我们选择js包进行获取

http://t.mtyee.com/ne2/s2641.js?1607175419

观察网页ur=http://www.bumimi66.com/acg/2641/3.html

与js的url,发现js网址是由前面的“http://t.mtyee.com/ne2/s+网页中acg/后的一段数字+.js?+任意数字 ”构成的。所以我们可以通过视频url,拼接出js的url。

有获取视频链接的函数:

def video_list(url_split,begin,end):
    list1=[]
    list4=[]
    list5=[]
    #利用正则获取网页中acg/后的一段数字
    p2 = re.compile(r'/([\d]+)/', re.S)
    url1 = re.findall(p2, url_split)[0]
    #拼接url
    url = 'http://t.mtyee.com/ne2/s' + url1 + '.js?123'
    #获取url的响应数据
    response = requests.get(url=url).text
    #这里提取分为两种情况:
    #在爬取的过程:有mp4格式的数据和m3u8格式的数据,用正则进行提取
    p1 = re.compile(r'"(https://[\w].+?[mp4,m3u8]),', re.S)
    #这里正则提取时出了一些问题:就是这个js数据里面有很多源的数据,十分有可能提取出多个代表一个视频的url所以有了list3就是提取http:后面的一段代表不同网址的数据
    p2=re.compile(r'https://(.+?)/',re.S)
    list2=re.findall(p1, response)
    list3=re.findall(p2,list2[0])
    print(list3[0])
    for i in range(len(list2)):
    #检验list3所代表的不同网址数据是否在list2的每个元里面
        if list3[0] in str(list2[i]):
            list4.append(list2[i])
    print(list4)
    print(begin,end)
    #这是提取指定集数的一个小小的封装for循环
    for i in range(int(begin),int(end)+1):
        list5.append(list4[int(i)])
    print('总共有' + str(len(list5)) + '集')
    return list5
           

还有就是搜索函数,这个很容易看到,不同的我们无法通过我们想爬取的内容,来获取指定的url,所以我们需要找一个跳板

这里我们看中了搜索页面:

http://www.bumimi66.com/search/%E9%AD%94%E6%B3%95%E5%B0%91%E5%A5%B3%E4%BC%8A%E8%8E%89%E9%9B%85

发现search/后面解码后就是我们搜索的内容,并且这个页面中也有我们获得的url,这里还是打开抓包工具找到json栏,这里笔者遭到了一些阻塞,就是每回用requests获取json数据时,发现403报错(服务器禁止访问),经过检查后发现是请求头的问题:

一个废物大学生对于视频爬取的小小的总结

加入请求头:

有获取页面数据的函数:

def inf_get(url):
    #加一个请求头就可以获取数据,并且获取当前页面所有信息,并且返回一个列表(里面是字典)
    #得到此页面的url,封面url,标题,出版时间,演员等
    list=[]
    header={
        'Accept': '*/*',
        'Accept-Encoding': 'gzip, deflate',
        'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
        'Connection': 'keep-alive',
        'Host': '118.89.59.239:8800',
        'Origin': 'http://www.bumimi77.com',
        'Referer': 'http://www.bumimi77.com/',
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 Edg/87.0.664.66',
    }
    response = requests.get(url=url,headers=header)
    #c=response[6:len(str(response))-1]
    response.encoding='utf-8'
    str_inf=response.text
    #print(str_inf)
    #用正则表达式提取括号内容
    p1 = re.compile(r'[{](.*?)[}]', re.S)
    for i in re.findall(p1, str_inf):
        #转化出来的没有外面括号。加上括号,使用json.load方法
        list_transf='{'+i+'}'
        list.append(json.loads(list_transf))
    return list
def page_inf(url):                #这个函数的作用是封装你爬取的视频的信息的url(最主要是集数),和返回储存爬取的url的列表
    list=[]                          #获取响应数据的url
    header={
        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
        'Accept-Encoding': 'gzip, deflate',
        'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
        'Cache-Control': 'max-age=0',
        'Connection': 'keep-alive',
        'Cookie': 'PHPSESSID=p05d88u2h4f12elggcfgifsvv5; y_Cookie_v=%E9%AD%94%E6%B3%95%E5%B0%91%E5%A5%B3%E4%BC%8A%E8%8E%89%E9%9B%85%E7%AC%AC%E4%B8%89%E5%AD%A33%20%E9%AD%94%E6%B3%95%E5%B0%91%E5%A5%B3%E4%BC%8A%E8%8E%89%E9%9B%85%E7%AC%AC%E4%B8%89%E5%AD%A3%20%E7%AC%AC3%E9%9B%86%20%E5%9C%A8%E7%BA%BF%E8%A7%82%E7%9C%8B%20-%20%E5%B8%83%E7%B1%B3%E7%B1%B3%40http%3A%2F%2Fwww.bumimi77.com%2Facg%2F2641%2F3.html%24%401609341680; Hm_lvt_3ce85e1f6a94e19a4aa54b75941d61e9=1609341681; y_Cookie_v_search=%E9%AD%94%E6%B3%95%E5%B0%91%E5%A5%B3%E4%BC%8A%E8%8E%89%E9%9B%85%40http%3A%2F%2Fwww.bumimi77.com%2Fsearch%2F%25E9%25AD%2594%25E6%25B3%2595%25E5%25B0%2591%25E5%25A5%25B3%25E4%25BC%258A%25E8%258E%2589%25E9%259B%2585%24%401609381969; Hm_lpvt_3ce85e1f6a94e19a4aa54b75941d61e9=1609381976',
        'Host': 'www.bumimi77.com',
        'Referer': 'http://www.bumimi77.com/search/%E9%AD%94%E6%B3%95%E5%B0%91%E5%A5%B3%E4%BC%8A%E8%8E%89%E9%9B%85',
        'Upgrade-Insecure-Requests': '1',
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 Edg/87.0.664.66',
    }
    response = requests.get(url=url, headers=header).text
    html = etree.HTML(response)
    try:
        if html.xpath('//*[@id="qiyi-pl-list"]/div/ul/li[1]/a/@href'):
            rec = html.xpath('//*[@id="qiyi-pl-list"]/div/ul/li[1]/a/@href')[0]
            p1=re.compile(r'([\d]+)')
            c=re.findall(p1,rec)
            print(c[0])
            print('总共有'+c[0]+'集')
            for i in range(0,int(c[0])):
                list.append(url+str(i)+'.html')
            return list
    except:
        print("这个网页不能爬取或者是没有你想要的信息")
        pass
           

视频的保存:

来到了重点视频的保存:

视频的保存有两种形式mp4,m3u8,MP4形式的视频好处理,直接调用you-get模块即可

python(you-get):2分钟快速下载网站视频

而对于m3u8格式的下载笔者查阅了大量的资料终于搞懂了他其中的机制

Python下载加密m3u8视频

python-m3u8-协程-快速下载并合并MP4

【python爬虫】爬取网页视频,解析m3u8文件,获取ts并合成mp4

这里有m3u8的下载链接:https://mei.huazuida.com/20191021/11051_c691e197/index.m3u8

一个废物大学生对于视频爬取的小小的总结

得到一个m3u8格式的文件,用记事本打开该文件

有这样内容:

一个废物大学生对于视频爬取的小小的总结

根据资料查阅知:这个m3u8是第一层url,需要通过url拼接获得第二层url,把最后一行加上第一个url得第二个url

https://mei.huazuida.com/20191021/11051_c691e197/1000k/hls/index.m3u8

下载第二个url,用记事本打开

一个废物大学生对于视频爬取的小小的总结

得出第二个url内容,可以根据其合成ts的url

https://mei.huazuida.com/20191021/11051_c691e197/1000k/hls/602967b17ac000001.ts

然后我们可以获取每个ts片段,并且用系统中copy命令合并

代码如下:

def m3u8_download(url,path,video_num):
 def download1(url):
        download_path = os.getcwd() + "\download"
        if not os.path.exists(download_path):
            os.mkdir(download_path)

        # 新建日期文件夹
        download_path = os.path.join(download_path, datetime.datetime.now().strftime('%Y%m%d_%H%M%S'))
        # print download_path
        os.mkdir(download_path)
        response=requests.get(url=url).text
        p1=re.compile(r'(.*/.+/.*\.m3u8)')
        p2=re.compile(r'(https://.+)index\.m3u8')
        p3=re.compile(r'.*?\.ts')
        inf=re.findall(p1,response)[0]
        inf2=re.findall(p2,url)[0]
        url2=inf2+inf
        inf4=re.findall(p2,url2)[0]
        response1=requests.get(url=url2).text
        inf3=re.findall(p3,response1)
        #path='C:/Users/mzy/Desktop/新建文件夹'
        num = 0
        for line in inf3:  # 第二层
            url3=inf4+str(line)
            c=0
            while c!=1:
                try:
                    if requests.get(url=url3):
                        res=requests.get(url=url3)
                        print(url3)
                        with open(os.path.join(download_path,line), 'ab') as f:
                            f.write(res.content)
                            f.flush()
                            c=1
                            num=num+1
                            print(num / len(inf3) * 100)
                except:
                    print("error")
                    pass
        print("下载完成")
        merge_file(download_path)

           

然后我们开始下载,发现下载太慢了,大约一下午才下了一集,(虽说很大部分是河南某211网络巨差),于是我们可以尝试一波线程池:

def merge_file(path,video_num):  #合并ts文件
        os.chdir(path)
        cmd = "copy /b * new.tmp"
        os.system(cmd)
        os.system('del /Q *.ts')
        os.system('del /Q *.mp4')
        #now=str(datetime.datetime.now())[11:19]
        os.rename("new.tmp", "%d.mp4"%video_num)
    response = requests.get(url=url).text
    p1 = re.compile(r'(.*/.+/.*\.m3u8)')
    p2 = re.compile(r'(https://.+)index\.m3u8')
    p3 = re.compile(r'.*?\.ts')
    inf = re.findall(p1, response)[0]
    inf2 = re.findall(p2, url)[0]
    url2 = inf2 + inf
    inf4 = re.findall(p2, url2)[0]
    response1 = requests.get(url=url2).text
    inf3 = re.findall(p3, response1)
    num = 0
    filename = path + '/' + str(video_num) + '集'
    urls=[]
    for i in inf3:
        url3 = inf4 + str(i)
        #print(url3)
        key={
            'name':i,
            'url3':url3
        }
        urls.append(key)
            def get_video_data(url):
        c = 0
        url3=url['url3']
        name=url['name']
        while c != 1:
            try:
                if requests.get(url=url3):
                    c = 1
                    response = requests.get(url=url3)
                    #print(filename)
                    filename1=filename +'/'+ name
                    #print(filename1)
                    #'C:/Users/mzy/Desktop/gangtie/'
                    #检查文件夹是否被建立,建立则停止则证明这一集已经被爬过
                    os.makedirs(os.path.dirname(filename1), exist_ok=True)
                    with open(filename1, "wb") as f:
                        f.write(response.content)
                        f.flush()
                        print(filename1)
                    #print(num / len(inf3) * 100)
            except:
                print("error")
                pass
    # 使用线程池对视频数据进行请求
    pool = Pool(100)  # c参数放有几个需要爬取的对象
    pool.map(get_video_data, urls)
    # 关闭线程池
    pool.close()
    pool.join()
    merge_file(filename,video_num)
    print('success')
           

虽说,速度变快了好几倍,但是,一个代学生怎么会满足与这一点点速度呢,然后笔者,尝试了线程池函数

def merge_file(path,video_num):  #合并ts文件
        os.chdir(path)
        cmd = "copy /b * new.tmp"
        os.system(cmd)
        os.system('del /Q *.ts')
        os.system('del /Q *.mp4')
        #now=str(datetime.datetime.now())[11:19]
        os.rename("new.tmp", "%d.mp4"%video_num)
    response = requests.get(url=url).text
    p1 = re.compile(r'(.*/.+/.*\.m3u8)')
    p2 = re.compile(r'(https://.+)index\.m3u8')
    p3 = re.compile(r'.*?\.ts')
    inf = re.findall(p1, response)[0]
    inf2 = re.findall(p2, url)[0]
    url2 = inf2 + inf
    inf4 = re.findall(p2, url2)[0]
    response1 = requests.get(url=url2).text
    inf3 = re.findall(p3, response1)
    num = 0
    filename = path + '/' + str(video_num) + '集'
    urls = []
    for i in inf3:
        url3 = inf4 + str(i)
        # print(url3)
        key = {
            'name': i,
            'url3': url3
        }
        urls.append(key)

    async def get_page(url):
        url3 = url['url3']
        name = url['name']
        c = 0
        # get与post函数一样,可以添加headers和,params/data参数,但是代理ip时,要换参数:proxy后面加入字典非字符串
        while c != 1:
            try:
                filename1 = filename + '/' + name
                os.makedirs(os.path.dirname(filename1), exist_ok=True)
                async with aiohttp.ClientSession() as session:
                    async with await session.get(url3) as response:
                        # text()返回字符串形式的响应数据
                        # read()返回的二进制形式的响应数据
                        # json()返回的就是json对象
                        # 注意:湖区响应数据前一定要用await挂起
                        page_text = await response.read()
                        with open(filename1, "wb") as f:
                            f.write(page_text)
                            f.flush()
                            print(filename1, 'success')
                            c = 1
            except:
                print(url3, "error")
                pass
    #异步爬取部分
    tasks = []
    for url in urls:
        task_temp = get_page(url)
        task = asyncio.ensure_future(task_temp)
        tasks.append(task)
    loop = asyncio.get_event_loop()
    loop.run_until_complete(asyncio.wait(tasks))
    #loop.close()
    merge_file(filename,video_num)
    print('success')
           

速度是几倍,完美。

然后这里附上,笔者初学多线程的笔记

# #在爬虫中使用异步实现高性能的数据爬取操作
# #异步爬虫的方法:
# #多线程,
# #可以为相关的操作单独开启线程
# #使用线程池的方式执行
# #线程池的爬取原则:线程池处理的阻塞且耗时的操作
#
#
# ###############################
# #导入模块
# # import time
# # import re
# # from lxml import etree
# # import requests
# # from multiprocessing.dummy import Pool
# # url="https://www.bilibili.com/v/kichiku/"
# # headers={
# # "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36 Edg/87.0.664.75"
# # }
# # page_text=requests.get(url=url,headers=headers).text
# # html=etree.HTML(page_text)
# #注意这一步定位不到xpath是因为他是动态加载的,需要用抓包工具找js,xhr,php,甚至用selenium来获去数据这里只是想说一下另类的xpath定位的方法
# #通过要把xpath,re,beautifulsoup理解成一个文本处理的库,不过特殊在他们处理的是html数据
# #如果有一个模型,一个大的div里面有很多小的a标签(里面的src的属性值是我们要的),
# #可以先定位这个这个大的div,然后后面加入/a,这表示的是定位大的div里面所有的a标签有,返回一个列表
# #列表循环,对每个返回的list值用xpath定位
# #然后在用xpath定位其相对的路径用./@src来定位src的属性值
# #############################测试代码#######################################################
# # url_list=html.xpath('//*[@id="kichiku_guide"]/div[2]/div[1]/div[2]/div[1]/a')
# # print(url_list)
# # with open('url.html','w',encoding='utf-8') as fp:
# #     fp.write(page_text)
# #########################################################################################
# #正则永远的神xpath,beautifulsoup定位不到的正则能提取
# #selenium得出的源码数据,xpath,beautifulsoup处理不了测,正则能提取
# #正则yyds
# ################################线程池的使用###########################################################
# # from multiprocessing.dummy import Pool
# # #放入需要爬取的url
# # urls=[]
# # #定义线程池函数
# import requests
# def get_video_data(url):
#     data=requests.get(url=url).content
#     #持久化存储操作
#     with open("url"+'.html','wb') as fp:
#         fp.write(data)
#         print("下载完成")
# get_video_data('https://www.bilibili.com/v/kichiku/guide/#/all/click/0/1/2020-1-01,2021-01-01')
# # #使用线程池对视频数据进行请求
# # pool=Pool(4)  #c参数放有几个需要爬取的对象
# # pool.map(get_video_data,urls)
# # #关闭线程池
# # pool.close()
# # pool.join()
# ###########################################################################
# #单线程+异步协程
# #event_loop:事件循环,相当于无限循环,我们可以将一些函数注册到这个事件循环中,当满足某些条件则会执行
# #coroutine:协程对象,我们可以将协程对象注册到事件循环中,它会被事件循环调用,我们可以使用async关键字来定义一个方法,这个方法在调用时不会立刻执行,
# #而是返回协程对象
# #task:任务,它是对协程的进一步封装,包含了任务的各个状态
# #future:代表将来执行或者还没有执行的任务,本质和task一样
# #async定义一个协程
# #awit用来挂起阻塞方法的执行
# #########################################################################
# # import asyncio
# # import time
# # async def request(url):
# #     print("正在下载",url)
# #     #在异步协程中出现了同步模块相关的代码,那么就无法实现异步
# #     #time.sleep(2)
# #     #当asynico中遇到阻塞操作必须手动挂起
# #     #一下代码代表time.sleep(2)
# #     await asyncio.sleep(2)
# #     print("下载完毕",url)
# # urls=[
# #     'a',
# #     'b',
# #     'c'
# # ]
# # stasks=[]
# # for url in urls:
# #     c=request(url)
# #     #把c函数封装成协程任务
# #     task=asyncio.ensure_future(c)
# #     #把协程任务封装到stasks列表中
# #     stasks.append(task)
# # #封装线程池
# # loop=asyncio.get_event_loop()
# # #固定格式,将任务列表储存在wait函数中
# # loop.run_until_complete(asyncio.wait(stasks))
# #######################################################################################
# ##异步函数中不能出现同步模块代码
# #我们可以通过aiohttp:模块 来实现网路的请求
# #使用该模块中clientsession来实现异步爬取
#
# import aiohttp
# import asyncio
# import os
# import requests
# import re
# response = requests.get(url=url).text
# p1 = re.compile(r'(.*/.+/.*\.m3u8)')
# p2 = re.compile(r'(https://.+)index\.m3u8')
# p3 = re.compile(r'.*?\.ts')
# inf = re.findall(p1, response)[0]
# inf2 = re.findall(p2, url)[0]
# url2 = inf2 + inf
# inf4 = re.findall(p2, url2)[0]
# response1 = requests.get(url=url2).text
# inf3 = re.findall(p3, response1)
# num = 0
# filename = path + '/' + str(video_num) + '集'
# urls = []
# for i in inf3:
#     url3 = inf4 + str(i)
#     # print(url3)
#     key = {
#         'name': i,
#         'url3': url3
#     }
#     urls.append(key)
# async def get_page(url):
#     url3 = url['url3']
#     name = url['name']
#     c=0
# #get与post函数一样,可以添加headers和,params/data参数,但是代理ip时,要换参数:proxy后面加入字典非字符串
#     while c!=1:
#         try:
#             filename1 = filename + '/' + name
#             os.makedirs(os.path.dirname(filename1), exist_ok=True)
#             async with aiohttp.ClientSession() as session:
#                 async with await session.get(url3) as response:
#                     #text()返回字符串形式的响应数据
#                     #read()返回的二进制形式的响应数据
#                     #json()返回的就是json对象
#                     #注意:湖区响应数据前一定要用await挂起
#                     page_text=await response.read()
#                     with open(filename1, "wb") as f:
#                         f.write(page_text)
#                         f.flush()
#                         print(filename1,'success')
#                         c=0
#         except:
#             print(url3,"error")
#             pass
# tasks=[]
# for url in urls:
#     task_temp=get_page(url)
#     task=asyncio.ensure_future(task_temp)
#     tasks.append(task)
# loop=asyncio.get_event_loop()
# loop.run_until_complete(asyncio.wait(tasks))
#
           

在来个函数来封装下载方法:

def download(url, path,video_num):
    print("hello")
    if '.mp4' in url:
        sys.argv = ['you-get', '-o', path, url]
        you_get.main()
        print("hello")
    elif '.m3u8' in url:
        m3u8_download(url,path,video_num)
    else:
        print(url)
        print('这个视频下载不了请复制页面在浏览器上下载')
           

最后来个main_f函数来封装:

def main_in():
    num=0
    list1=[]    #的到url转页面的网址
    list2=[]    #得到封面
    list3=[]     #得到名字
    quest=input("你要爬取的页面:")
    quest1=quote(quest)
    print(quest1)
    url='http://159.75.7.49:7211/sssv.php?top=10&q='+quest1
    #download(url, path)
    for i in inf_get(url):#得到页面中的字典
        list1.append(i['url'])
        list2.append(i['title'])
        #list3.append(i['thumb'])         #保存封面的函数回来在封装
    print("总共%d条信息"%len(list2))
    while num<len(list2):
        while True:
            print(list2[num])
            path = 'C:/Users/mzy/Desktop/'+list2[num]  # 保存目录,没有则建立
            c=input("你要爬取这个页面么?(1表示是,0表示否)")
            page_inf(list1[num])
            if c=='1':
                begin = input("你要获取第几集到第几集的信息:(开头)")
                end = input("你要获取第几集到第几集的信息:(结尾)")
                #page_inf(list1[num],begin,end)
                num1=int(begin)
                for k in video_list(list1[num],begin,end):
                    print(str(k))
                    download(k,path,num1)
                    num = num + 1
                    num1=num1+1
            elif c=='0':
                num = num + 1
                break
                #魔法少女伊莉雅
            else:
                print('inputerror')
           

最后总的代码如下:

import sys
import os
import re
import you_get
import requests
import json
import datetime
import asyncio
import aiohttp
from lxml import etree
from urllib.parse import unquote,quote
from multiprocessing.dummy import Pool
from Crypto.Cipher import AES
from time import sleep
from tqdm import tqdm
def m3u8_download(url,path,video_num):
    #########################单线程方法#######################################
    # def download1(url):
    #     download_path = os.getcwd() + "\download"
    #     if not os.path.exists(download_path):
    #         os.mkdir(download_path)
    #
    #     # 新建日期文件夹
    #     download_path = os.path.join(download_path, datetime.datetime.now().strftime('%Y%m%d_%H%M%S'))
    #     # print download_path
    #     os.mkdir(download_path)
    #     response=requests.get(url=url).text
    #     p1=re.compile(r'(.*/.+/.*\.m3u8)')
    #     p2=re.compile(r'(https://.+)index\.m3u8')
    #     p3=re.compile(r'.*?\.ts')
    #     inf=re.findall(p1,response)[0]
    #     inf2=re.findall(p2,url)[0]
    #     url2=inf2+inf
    #     inf4=re.findall(p2,url2)[0]
    #     response1=requests.get(url=url2).text
    #     inf3=re.findall(p3,response1)
    #     #path='C:/Users/mzy/Desktop/新建文件夹'
    #     num = 0
    #     for line in inf3:  # 第二层
    #         url3=inf4+str(line)
    #         c=0
    #         while c!=1:
    #             try:
    #                 if requests.get(url=url3):
    #                     res=requests.get(url=url3)
    #                     print(url3)
    #                     with open(os.path.join(download_path,line), 'ab') as f:
    #                         f.write(res.content)
    #                         f.flush()
    #                         c=1
    #                         num=num+1
    #                         print(num / len(inf3) * 100)
    #             except:
    #                 print("error")
    #                 pass
    #     print("下载完成")
    #     merge_file(download_path)
    #
    # def merge_file(path):
    #     os.chdir(path)
    #     cmd = "copy /b * new.tmp"
    #     os.system(cmd)
    #     os.system('del /Q *.ts')
    #     os.system('del /Q *.mp4')
    #     os.rename("new.tmp", "new.mp4")
    #
    # download1(url)
    ##########################线程池方法###########################
    # def merge_file(path,video_num):  #合并ts文件
    #     os.chdir(path)
    #     cmd = "copy /b * new.tmp"
    #     os.system(cmd)
    #     os.system('del /Q *.ts')
    #     os.system('del /Q *.mp4')
    #     #now=str(datetime.datetime.now())[11:19]
    #     os.rename("new.tmp", "%d.mp4"%video_num)
    # response = requests.get(url=url).text
    # p1 = re.compile(r'(.*/.+/.*\.m3u8)')
    # p2 = re.compile(r'(https://.+)index\.m3u8')
    # p3 = re.compile(r'.*?\.ts')
    # inf = re.findall(p1, response)[0]
    # inf2 = re.findall(p2, url)[0]
    # url2 = inf2 + inf
    # inf4 = re.findall(p2, url2)[0]
    # response1 = requests.get(url=url2).text
    # inf3 = re.findall(p3, response1)
    # num = 0
    # filename = path + '/' + str(video_num) + '集'
    # urls=[]
    # for i in inf3:
    #     url3 = inf4 + str(i)
    #     #print(url3)
    #     key={
    #         'name':i,
    #         'url3':url3
    #     }
    #     urls.append(key)
    # # 定义线程池函数
    # def get_video_data(url):
    #     c = 0
    #     url3=url['url3']
    #     name=url['name']
    #     while c != 1:
    #         try:
    #             if requests.get(url=url3):
    #                 c = 1
    #                 response = requests.get(url=url3)
    #                 #print(filename)
    #                 filename1=filename +'/'+ name
    #                 #print(filename1)
    #                 #'C:/Users/mzy/Desktop/gangtie/'
    #                 #检查文件夹是否被建立,建立则停止则证明这一集已经被爬过
    #                 os.makedirs(os.path.dirname(filename1), exist_ok=True)
    #                 with open(filename1, "wb") as f:
    #                     f.write(response.content)
    #                     f.flush()
    #                     print(filename1)
    #                 #print(num / len(inf3) * 100)
    #         except:
    #             print("error")
    #             pass
    # # 使用线程池对视频数据进行请求
    # pool = Pool(100)  # c参数放有几个需要爬取的对象
    # pool.map(get_video_data, urls)
    # # 关闭线程池
    # pool.close()
    # pool.join()
    # merge_file(filename,video_num)
    # print('success')
    ###########################多任务异步协程方法#####################################
    def merge_file(path,video_num):  #合并ts文件
        os.chdir(path)
        cmd = "copy /b * new.tmp"
        os.system(cmd)
        os.system('del /Q *.ts')
        os.system('del /Q *.mp4')
        #now=str(datetime.datetime.now())[11:19]
        os.rename("new.tmp", "%d.mp4"%video_num)
    response = requests.get(url=url).text
    p1 = re.compile(r'(.*/.+/.*\.m3u8)')
    p2 = re.compile(r'(https://.+)index\.m3u8')
    p3 = re.compile(r'.*?\.ts')
    inf = re.findall(p1, response)[0]
    inf2 = re.findall(p2, url)[0]
    url2 = inf2 + inf
    inf4 = re.findall(p2, url2)[0]
    response1 = requests.get(url=url2).text
    inf3 = re.findall(p3, response1)
    num = 0
    filename = path + '/' + str(video_num) + '集'
    urls = []
    for i in inf3:
        url3 = inf4 + str(i)
        # print(url3)
        key = {
            'name': i,
            'url3': url3
        }
        urls.append(key)

    async def get_page(url):
        url3 = url['url3']
        name = url['name']
        c = 0
        # get与post函数一样,可以添加headers和,params/data参数,但是代理ip时,要换参数:proxy后面加入字典非字符串
        while c != 1:
            try:
                filename1 = filename + '/' + name
                os.makedirs(os.path.dirname(filename1), exist_ok=True)
                async with aiohttp.ClientSession() as session:
                    async with await session.get(url3) as response:
                        # text()返回字符串形式的响应数据
                        # read()返回的二进制形式的响应数据
                        # json()返回的就是json对象
                        # 注意:湖区响应数据前一定要用await挂起
                        page_text = await response.read()
                        with open(filename1, "wb") as f:
                            f.write(page_text)
                            f.flush()
                            print(filename1, 'success')
                            c = 1
            except:
                print(url3, "error")
                pass
    #异步爬取部分
    tasks = []
    for url in urls:
        task_temp = get_page(url)
        task = asyncio.ensure_future(task_temp)
        tasks.append(task)
    loop = asyncio.get_event_loop()
    loop.run_until_complete(asyncio.wait(tasks))
    #loop.close()
    merge_file(filename,video_num)
    print('success')
def download(url, path,video_num):
    print("hello")
    if '.mp4' in url:
        sys.argv = ['you-get', '-o', path, url]
        you_get.main()
        print("hello")
    elif '.m3u8' in url:
        m3u8_download(url,path,video_num)
    else:
        print(url)
        print('这个视频下载不了请复制页面在浏览器上下载')
#注意:有些网址下不了例如魔法少女莉莉娅剧场版只有网址但拒绝下载,可以用selenium来解决等明年有时间在尝试
def inf_get(url):
    #加一个请求头就可以获取数据,并且获取当前页面所有信息,并且返回一个列表(里面是字典)
    #得到此页面的url,封面url,标题,出版时间,演员等
    list=[]
    header={
        'Accept': '*/*',
        'Accept-Encoding': 'gzip, deflate',
        'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
        'Connection': 'keep-alive',
        'Host': '118.89.59.239:8800',
        'Origin': 'http://www.bumimi77.com',
        'Referer': 'http://www.bumimi77.com/',
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 Edg/87.0.664.66',
    }
    response = requests.get(url=url,headers=header)
    #c=response[6:len(str(response))-1]
    response.encoding='utf-8'
    str_inf=response.text
    #print(str_inf)
    #用正则表达式提取括号内容
    p1 = re.compile(r'[{](.*?)[}]', re.S)
    for i in re.findall(p1, str_inf):
        #转化出来的没有外面括号。加上括号,使用json.load方法
        list_transf='{'+i+'}'
        list.append(json.loads(list_transf))
    return list
def page_inf(url):                #这个函数的作用是封装你爬取的视频的信息的url(最主要是集数),和返回储存爬取的url的列表
    list=[]                          #获取响应数据的url
    header={
        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
        'Accept-Encoding': 'gzip, deflate',
        'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
        'Cache-Control': 'max-age=0',
        'Connection': 'keep-alive',
        'Cookie': 'PHPSESSID=p05d88u2h4f12elggcfgifsvv5; y_Cookie_v=%E9%AD%94%E6%B3%95%E5%B0%91%E5%A5%B3%E4%BC%8A%E8%8E%89%E9%9B%85%E7%AC%AC%E4%B8%89%E5%AD%A33%20%E9%AD%94%E6%B3%95%E5%B0%91%E5%A5%B3%E4%BC%8A%E8%8E%89%E9%9B%85%E7%AC%AC%E4%B8%89%E5%AD%A3%20%E7%AC%AC3%E9%9B%86%20%E5%9C%A8%E7%BA%BF%E8%A7%82%E7%9C%8B%20-%20%E5%B8%83%E7%B1%B3%E7%B1%B3%40http%3A%2F%2Fwww.bumimi77.com%2Facg%2F2641%2F3.html%24%401609341680; Hm_lvt_3ce85e1f6a94e19a4aa54b75941d61e9=1609341681; y_Cookie_v_search=%E9%AD%94%E6%B3%95%E5%B0%91%E5%A5%B3%E4%BC%8A%E8%8E%89%E9%9B%85%40http%3A%2F%2Fwww.bumimi77.com%2Fsearch%2F%25E9%25AD%2594%25E6%25B3%2595%25E5%25B0%2591%25E5%25A5%25B3%25E4%25BC%258A%25E8%258E%2589%25E9%259B%2585%24%401609381969; Hm_lpvt_3ce85e1f6a94e19a4aa54b75941d61e9=1609381976',
        'Host': 'www.bumimi77.com',
        'Referer': 'http://www.bumimi77.com/search/%E9%AD%94%E6%B3%95%E5%B0%91%E5%A5%B3%E4%BC%8A%E8%8E%89%E9%9B%85',
        'Upgrade-Insecure-Requests': '1',
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 Edg/87.0.664.66',
    }
    response = requests.get(url=url, headers=header).text
    html = etree.HTML(response)
    try:
        if html.xpath('//*[@id="qiyi-pl-list"]/div/ul/li[1]/a/@href'):
            rec = html.xpath('//*[@id="qiyi-pl-list"]/div/ul/li[1]/a/@href')[0]
            p1=re.compile(r'([\d]+)')
            c=re.findall(p1,rec)
            print(c[0])
            print('总共有'+c[0]+'集')
            for i in range(0,int(c[0])):
                list.append(url+str(i)+'.html')
            return list
    except:
        print("这个网页不能爬取或者是没有你想要的信息")
        pass
def video_list(url_split,begin,end):
    #3124
    list1=[]
    list4=[]
    list5=[]
    p2 = re.compile(r'/([\d]+)/', re.S)
    url1 = re.findall(p2, url_split)[0]
    #print(url1)
    url = 'http://t.mtyee.com/ne2/s' + url1 + '.js?123'
    response = requests.get(url=url).text
    #print(response)
    # p1 = re.compile(r'"(http+.*?)"', re.S)
    p1 = re.compile(r'"(https://[\w].+?[mp4,m3u8]),', re.S)
    p2=re.compile(r'https://(.+?)/',re.S)
    list2=re.findall(p1, response)
    list3=re.findall(p2,list2[0])
    print(list3[0])
    for i in range(len(list2)):
        if list3[0] in str(list2[i]):
            list4.append(list2[i])
    print(list4)
    print(begin,end)
    for i in range(int(begin),int(end)+1):
        list5.append(list4[int(i)])
    print('总共有' + str(len(list5)) + '集')
    return list5
def main_in():
    num=0
    list1=[]    #的到url转页面的网址
    list2=[]    #得到封面
    list3=[]     #得到名字
    quest=input("你要爬取的页面:")
    quest1=quote(quest)
    print(quest1)
    url='http://159.75.7.49:7211/sssv.php?top=10&q='+quest1
    #download(url, path)
    for i in inf_get(url):#得到页面中的字典
        list1.append(i['url'])
        list2.append(i['title'])
        #list3.append(i['thumb'])         #保存封面的函数回来在封装
    print("总共%d条信息"%len(list2))
    while num<len(list2):
        while True:
            print(list2[num])
            path = 'C:/Users/mzy/Desktop/'+list2[num]  # 保存目录,没有则建立
            c=input("你要爬取这个页面么?(1表示是,0表示否)")
            page_inf(list1[num])
            if c=='1':
                begin = input("你要获取第几集到第几集的信息:(开头)")
                end = input("你要获取第几集到第几集的信息:(结尾)")
                #page_inf(list1[num],begin,end)
                num1=int(begin)
                for k in video_list(list1[num],begin,end):
                    print(str(k))
                    download(k,path,num1)
                    num = num + 1
                    num1=num1+1
            elif c=='0':
                num = num + 1
                break
                #魔法少女伊莉雅
            else:
                print('inputerror')
if __name__ == '__main__':
    main_in()
    # file=open("aa.html",'a', encoding='utf-8')
    #  file.write(response)
           

补充:

M3U8文件是指UTF-8编码格式的M3U文件。M3U文件是记录了一个索引纯文本文件,打开它时播放软件并不是播放它,而是根据它的索引找到对应的音视频文件的网络地址进行在线播放。

m3u8标签与属性说明

#EXTM3U

每个M3U文件第一行必须是这个tag,请标示作用

#EXT-X-VERSION:3

该属性可以没有

#EXT-X-MEDIA-SEQUENCE:140651513

每一个media URI在PlayList中只有唯一的序号,相邻之间序号+1,

一个media URI并不是必须要包含的,如果没有,默认为0

#EXT-X-TARGETDURATION

指定最大的媒体段时间长(秒)。所以#EXTINF中指定的时间长度必须小于或是等于这

个最大值。这个tag在整个PlayList文件中只能出现一 次(在嵌套的情况下,一般有

真正ts url的m3u8才会出现该tag)

#EXT-X-PLAYLIST-TYPE

提供关于PlayList的可变性的信息,这个对整个PlayList文件有效,是可选的,格式

如下:#EXT-X-PLAYLIST-TYPE::如果是VOD,则服务器不能改变PlayList 文件;

如果是EVENT,则服务器不能改变或是删除PlayList文件中的任何部分,但是可以向该

文件中增加新的一行内容。

#EXTINF

duration指定每个媒体段(ts)的持续时间(秒),仅对其后面的URI有效,title是

下载资源的url

#EXT-X-KEY

表示怎么对media segments进行解码。其作用范围是下次该tag出现前的所有media

URI,属性为NONE 或者 AES-128。NONE表示 URI以及IV(Initialization

Vector)属性必须不存在, AES-128(Advanced EncryptionStandard)表示URI

必须存在,IV可以不存在。

#EXT-X-PROGRAM-DATE-TIME

将一个绝对时间或是日期和一个媒体段中的第一个sample相关联,只对下一个meida

URI有效,格式如#EXT-X-PROGRAM-DATE-TIME:

For example: #EXT-X-PROGRAM-DATETIME:2010-02-19T14:54:23.031+08:00

#EXT-X-ALLOW-CACHE

是否允许做cache,这个可以在PlayList文件中任意地方出现,并且最多出现一次,作

用效果是所有的媒体段。格式如下:#EXT-X-ALLOW-CACHE:

#EXT-X-ENDLIST

表示PlayList的末尾了,它可以在PlayList中任意位置出现,但是只能出现一个,格

式如下:#EXT-X-ENDLIST

https://blog.csdn.net/Ronadlo7/article/details/81029229?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522161399303416780271562425%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=161399303416780271562425&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allfirst_rank_v2~rank_v29-1-81029229.first_rank_v2_pc_rank_v29&utm_term=m3u8%E6%96%87%E4%BB%B6%E8%AF%A6%E7%BB%86