天天看點

一個廢物大學生對于視訊爬取的小小的總結

爬取視訊的總結

當時看到了與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