一文看懂,python抓取m3u8里ts加密视频及合成、多线程、写入的问题
花了幾天時(shí)間搞m3u8里的ts視頻,還有多線程協(xié)程的處理問(wèn)題??戳舜罅康幕卮?#xff0c;發(fā)現(xiàn)大多數(shù)人講的都是個(gè)大概,具體的怎么用、什么原理沒(méi)講。今天就來(lái)帶大家講解怎么爬取m3u8里的加密ts視頻。
目標(biāo)網(wǎng)址我就不說(shuō)了,怕被和諧
以《叢林奇航DB》為例吧,首先找到目標(biāo)網(wǎng)址,再F12檢查,點(diǎn)擊網(wǎng)絡(luò),找到m3u8結(jié)尾的鏈接(找不到的話點(diǎn)擊刷新一下)。如下圖:
點(diǎn)擊預(yù)覽后會(huì)看到一連串的ts鏈接和以AES-128加密的key鏈接。如下圖:
如果鏈接不完整的話要手動(dòng)補(bǔ)全鏈接。
看到這里的話已經(jīng)所找到了視頻資源,接下來(lái)寫代碼爬取了。
首先導(dǎo)入庫(kù):
#!/usr/bin/env python3 # -*- coding: utf-8 -*-import time import os import requests import re import aiohttp import asyncio from Crypto.Cipher import AES?crypto庫(kù)的安裝請(qǐng)前往crypto安裝 - 百度文庫(kù)自行了解。
再來(lái)說(shuō)說(shuō)AES的一個(gè)解密ts視頻原理,這里以爬取一部ts為例:
from Crypto.Cipher import AES# 提取key和ts的鏈接地址進(jìn)行訪問(wèn)與獲取文本 key_url = 'https://pps.shanshanku.com/20211127/g8V4A0hE/1000kb/hls/key.key' ts_url = 'https://pps.shanshanku.com/20211127/g8V4A0hE/1000kb/hls/z8WfPVdF.ts' key = requests.get(key_url).content ts = requests.get(ts_url).contentwith open('./date/video.ts', 'wb') as file:# 用Crupto庫(kù)里的AES進(jìn)行解密crypto = AES.new(key,AES.MODE_CBC, key)# 解密完成之后就可以寫入了file.write(crypto.decrypt(ts))file.close()得到視頻。解密成功。沒(méi)解密直接寫入是無(wú)法播放的。
?解密原理已經(jīng)了解,接下來(lái)就是如何爬取多個(gè)ts視頻了。
下面來(lái)說(shuō)說(shuō)aiohttp庫(kù)和asyncio庫(kù)進(jìn)行協(xié)程的用法
# 第一步:得到一個(gè)url列表 urls = ['https://baidu.com','https://baidu.com','https://baidu.com'] # 第一步:用asyncio創(chuàng)建一個(gè)函數(shù)線程,用aiohttp來(lái)創(chuàng)建一個(gè)支持異步的訪問(wèn) async def get_status(url):# 類似session = aiohttp.ClientSession,with 前面要加async,獲取東西前用awaitasync with aiohttp.ClientSession() as session:async with await session.get(url) as response:# 如果是<str>類型的就用text().<byte>類型的就用read()text = await response.text()return text[:100]# 第三步:定義一個(gè)執(zhí)行模塊 if __name__ == '__main__':start = time.time()# 把第一步的列表放入函數(shù)塊里并導(dǎo)入到tasks列表tasks = []for url in urls:c = get_status(url)# 把函數(shù)塊放入線程task = asyncio.ensure_future(c)tasks.append(task)# 循環(huán)事件loop = asyncio.get_event_loop()loop.run_until_complete(asyncio.wait(tasks))# 循環(huán)完成后進(jìn)行關(guān)閉loop.close()print('總耗時(shí):', time.time()-start)運(yùn)行后查看三個(gè)線程同時(shí)啟動(dòng)到完成后的總用時(shí):
我們可以看到,調(diào)用線程已經(jīng)成功,接下來(lái)就是怎么獲取和寫入了:下面對(duì)寫入進(jìn)行分析:
...... # 如果是<str>類型的就用text().<byte>類型的就用read()text = await response.text()return text[:100]# 這里只截取了一小部分 # 增加一個(gè)函數(shù)塊# 形參規(guī)定設(shè)為t,對(duì)線程運(yùn)行返回的結(jié)果進(jìn)行接收 def parsel(t):tlt = t.result()# 直接打印tlt返回的是一個(gè)元組類型,一個(gè)線程對(duì)應(yīng)一個(gè)元組,這里有三個(gè)線程print(tlt)# 如果元組里有多個(gè)數(shù)據(jù)時(shí),需要轉(zhuǎn)換為列表才能提取lis = list(tlt)with open('./date/http.txt', 'a') as file:# 對(duì)獲取的text[:100]進(jìn)行寫入file.write(str(tlt))file.close()......c = get_status(url)# 把函數(shù)塊放入線程task = asyncio.ensure_future(c)# 把parsel函數(shù)添加進(jìn)去。。。。。。。。。。。。。。。。。。。。task.add_done_callback(parsel)tasks.append(task)?我們來(lái)看看打印效果和寫入效果:
?
到這一步,我們已經(jīng)知道怎么調(diào)用協(xié)程和解密包括寫入了
假設(shè)我們這一步已經(jīng)提取了所有的ts視頻文件
接下來(lái)就是對(duì)ts視頻進(jìn)行合并與刪除源文件了
合并有調(diào)用cmd和利用ffmpeg(需要安裝):
調(diào)用cmd和利用ffmpge進(jìn)行合成:
# 導(dǎo)入所需模塊 import os# 用ffmpeg進(jìn)行ts視頻合成 cmd = 'ffmpeg -f concat -safe 0 -i complex.txt(合成路徑與命名) -c copy output.mp4(輸出路徑與命名)'# 調(diào)用cmd os.system(cmd)這樣我們就完成了ts視頻的合成。
接下來(lái)附上完整的代碼
#!/usr/bin/env python3 # -*- coding: utf-8 -*-# 導(dǎo)入所需的庫(kù) import time import os import requests import re import aiohttp import asyncio from Crypto.Cipher import AES# 創(chuàng)建一個(gè)URL列表 m3u8_URL = 'https://pps.sd-play.com/20220424/RN3p7Bj5/1200kb/hls/index.m3u8' resp = requests.get(url=m3u8_URL).text# 這里只提取300個(gè)ts視頻 rst = re.findall('https:(.*?).ts', resp)[:300] # name = re.findall('1200kb/hls/(.*?).ts',resp)[:5] # 給文件命名,用列表進(jìn)行封裝 tt = '{}' names = [tt.format(num) for num in range(10001, 10301)] # print(rst) print(names) # 把ts視頻封裝進(jìn)列表里 ts_list = [] for i in rst:ts_url = 'https:'+i+'.ts'ts_list.append(ts_url)# 限制最大協(xié)程數(shù)用Semaphore,時(shí)間原來(lái)這里不講了,不懂再問(wèn) concurrency = 15 semaphore = asyncio.Semaphore(concurrency)# 用asyncio和aiphttp結(jié)合創(chuàng)建線程 async def get_request(url, name):async with semaphore:# conn = aiohttp.TCPConnector(limit=15)async with aiohttp.ClientSession() as session:async with await session.get(url) as response:# 因?yàn)槭莃yte類型,所以用read()page_text = await response.read()return page_text, namedef parse(t):page_text = t.result()# 返回得到一個(gè)元組,轉(zhuǎn)換為列表,再對(duì)文件名和內(nèi)容進(jìn)行提取complex = list(page_text)na = complex[1]all = complex[0]# print(na)# print(all)# 用crypto里的AES進(jìn)行解密并保存key = b'b7d463938dcfabff'path = 'D:\\thead\\'with open(f'{path}{na}.ts', 'ab') as file:cryptor = AES.new(key, AES.MODE_CBC, key)file.write(cryptor.decrypt(all))print('下載完成')if __name__ == '__main__':start = time.time()# 定義列表,把元素放進(jìn)去并開啟協(xié)程tasks = []for url,name in zip(ts_list,names):c = get_request(url, name)task = asyncio.ensure_future(c)task.add_done_callback(parse)tasks.append(task)loop = asyncio.get_event_loop()try:loop.run_until_complete(asyncio.wait(tasks))except:loop.close()# 為方便ffmpeg合并,這里創(chuàng)建了一個(gè)txt文件列表里面包含每個(gè)ts的名稱。for j in range(10001, 10301):with open('D:\\thead\\list.txt', 'a') as file:file.write(f'file {j}.ts' + '\n')file.close()time.sleep(1)# 調(diào)用cmd并利用ffmpeg進(jìn)行合并cmd = f'ffmpeg -f concat -safe 0 -i D:\\thead\\list.txt -c copy D:\\thead\\theads\\new01.mp4'# cmd = f'copy/b D:\\thead\\* D:\\thead\\theads\\new01.ts'# 合并完成后再調(diào)用cmd進(jìn)行ts文件刪除os.system(cmd)delete = f'del D:\\thead\\*'os.system(delete)# 整個(gè)程序運(yùn)行所需的時(shí)間print('總用時(shí):', time.time()-start)時(shí)間原因,還有些限制協(xié)程數(shù)就不多說(shuō)了,有問(wèn)題可以互相交流學(xué)習(xí)。
最后看一下效果:
好,大功告成!!!!?
總結(jié)
以上是生活随笔為你收集整理的一文看懂,python抓取m3u8里ts加密视频及合成、多线程、写入的问题的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: gcc编译c++文件
- 下一篇: websocket python爬虫_p