赞
踩
花了几天时间搞m3u8里的ts视频,还有多线程协程的处理问题。看了大量的回答,发现大多数人讲的都是个大概,具体的怎么用、什么原理没讲。今天就来带大家讲解怎么爬取m3u8里的加密ts视频。
目标网址我就不说了,怕被和谐
以《丛林奇航DB》为例吧,首先找到目标网址,再F12检查,点击网络,找到m3u8结尾的链接(找不到的话点击刷新一下)。如下图:
点击预览后会看到一连串的ts链接和以AES-128加密的key链接。如下图:
如果链接不完整的话要手动补全链接。
看到这里的话已经所找到了视频资源,接下来写代码爬取了。
首先导入库:
- #!/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库的安装请前往crypto安装 - 百度文库自行了解。
再来说说AES的一个解密ts视频原理,这里以爬取一部ts为例:
- from Crypto.Cipher import AES
-
- # 提取key和ts的链接地址进行访问与获取文本
- 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).content
-
- with open('./date/video.ts', 'wb') as file:
- # 用Crupto库里的AES进行解密
- crypto = AES.new(key,AES.MODE_CBC, key)
- # 解密完成之后就可以写入了
- file.write(crypto.decrypt(ts))
- file.close()
得到视频。解密成功。没解密直接写入是无法播放的。
解密原理已经了解,接下来就是如何爬取多个ts视频了。
下面来说说aiohttp库和asyncio库进行协程的用法
- # 第一步:得到一个url列表
- urls = ['https://baidu.com',
- 'https://baidu.com',
- 'https://baidu.com']
- # 第一步:用asyncio创建一个函数线程,用aiohttp来创建一个支持异步的访问
- async def get_status(url):
- # 类似session = aiohttp.ClientSession,with 前面要加async,获取东西前用await
- async with aiohttp.ClientSession() as session:
- async with await session.get(url) as response:
- # 如果是<str>类型的就用text().<byte>类型的就用read()
- text = await response.text()
- return text[:100]
-
- # 第三步:定义一个执行模块
- if __name__ == '__main__':
- start = time.time()
- # 把第一步的列表放入函数块里并导入到tasks列表
- tasks = []
- for url in urls:
- c = get_status(url)
- # 把函数块放入线程
- task = asyncio.ensure_future(c)
- tasks.append(task)
- # 循环事件
- loop = asyncio.get_event_loop()
- loop.run_until_complete(asyncio.wait(tasks))
- # 循环完成后进行关闭
- loop.close()
- print('总耗时:', time.time()-start)
运行后查看三个线程同时启动到完成后的总用时:
我们可以看到,调用线程已经成功,接下来就是怎么获取和写入了:下面对写入进行分析:
- ......
- # 如果是<str>类型的就用text().<byte>类型的就用read()
- text = await response.text()
- return text[:100]
-
- # 这里只截取了一小部分
- # 增加一个函数块
-
- # 形参规定设为t,对线程运行返回的结果进行接收
- def parsel(t):
- tlt = t.result()
- # 直接打印tlt返回的是一个元组类型,一个线程对应一个元组,这里有三个线程
- print(tlt)
- # 如果元组里有多个数据时,需要转换为列表才能提取
- lis = list(tlt)
- with open('./date/http.txt', 'a') as file:
- # 对获取的text[:100]进行写入
- file.write(str(tlt))
- file.close()
-
- ......
-
- c = get_status(url)
- # 把函数块放入线程
- task = asyncio.ensure_future(c)
- # 把parsel函数添加进去。。。。。。。。。。。。。。。。。。。。
- task.add_done_callback(parsel)
- tasks.append(task)
我们来看看打印效果和写入效果:
到这一步,我们已经知道怎么调用协程和解密包括写入了
假设我们这一步已经提取了所有的ts视频文件
接下来就是对ts视频进行合并与删除源文件了
合并有调用cmd和利用ffmpeg(需要安装):
调用cmd和利用ffmpge进行合成:
- # 导入所需模块
- import os
-
- # 用ffmpeg进行ts视频合成
- cmd = 'ffmpeg -f concat -safe 0 -i complex.txt(合成路径与命名) -c copy output.mp4(输出路径与命名)'
-
- # 调用cmd
- os.system(cmd)
这样我们就完成了ts视频的合成。
接下来附上完整的代码
- #!/usr/bin/env python3
- # -*- coding: utf-8 -*-
-
- # 导入所需的库
- import time
- import os
- import requests
- import re
- import aiohttp
- import asyncio
- from Crypto.Cipher import AES
-
-
-
- # 创建一个URL列表
- m3u8_URL = 'https://pps.sd-play.com/20220424/RN3p7Bj5/1200kb/hls/index.m3u8'
- resp = requests.get(url=m3u8_URL).text
-
- # 这里只提取300个ts视频
- rst = re.findall('https:(.*?).ts', resp)[:300]
- # name = re.findall('1200kb/hls/(.*?).ts',resp)[:5]
- # 给文件命名,用列表进行封装
- tt = '{}'
- names = [tt.format(num) for num in range(10001, 10301)]
- # print(rst)
- print(names)
- # 把ts视频封装进列表里
- ts_list = []
- for i in rst:
- ts_url = 'https:'+i+'.ts'
- ts_list.append(ts_url)
-
- # 限制最大协程数用Semaphore,时间原来这里不讲了,不懂再问
- concurrency = 15
- semaphore = asyncio.Semaphore(concurrency)
-
- # 用asyncio和aiphttp结合创建线程
- 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:
- # 因为是byte类型,所以用read()
- page_text = await response.read()
- return page_text, name
-
- def parse(t):
- page_text = t.result()
- # 返回得到一个元组,转换为列表,再对文件名和内容进行提取
- complex = list(page_text)
- na = complex[1]
- all = complex[0]
- # print(na)
- # print(all)
-
- # 用crypto里的AES进行解密并保存
- 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()
- # 定义列表,把元素放进去并开启协程
- 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合并,这里创建了一个txt文件列表里面包含每个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)
- # 调用cmd并利用ffmpeg进行合并
- 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'
-
- # 合并完成后再调用cmd进行ts文件删除
- os.system(cmd)
- delete = f'del D:\\thead\\*'
- os.system(delete)
-
- # 整个程序运行所需的时间
- print('总用时:', time.time()-start)
时间原因,还有些限制协程数就不多说了,有问题可以互相交流学习。
最后看一下效果:
好,大功告成!!!!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。