一文看懂,python抓取m3u8里ts加密视频及合成多线程写入的问题

Posted m0_68242099

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一文看懂,python抓取m3u8里ts加密视频及合成多线程写入的问题相关的知识,希望对你有一定的参考价值。

花了几天时间搞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'pathna.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)

时间原因,还有些限制协程数就不多说了,有问题可以互相交流学习。

最后看一下效果:

好,大功告成!!!! 

python抓取m3u8文件,并提取.ts文件合成视频

 本节抓取手机app视频,charles抓包部分就不演示了,抓包内容如下:

 

可以直接抓取到.ts视频文件,但全都是视频片段,如果要抓全部的视频,就要找m3u8文件,里边有所有的视频路径,在拼接url前缀,就可以拿到正确的视频url了。

以下是代码部分:

import requests
import os,sys
import re
#读取m3u8文件并提取.ts文件路径
url="http://f1.thishs.com/578a7600fb83e8566227a90f3bd926b4/5E64C7E6/vod2/_definst_/mp4:2020/5/0227/STP12280/STP12280.mp4/chunklist.m3u8"
res=requests.get(url).text
print(res)
url_prifix="http://f1.thishs.com"
ts=re.findall(r"/.*?\\.ts",res,flags=re.S)
print(len(ts),ts)
#分别获取.ts文件并以二进制保存
for i in ts:
    u=url_prifix+i
    r=requests.get(u).content
    print(i,u)
    filename=u[-4:] if u[-5:].startswith("_") else u[-5:]
    with open(filename,mode="wb") as file:
        file.write(r)
#利用cmd命令将.ts文件合成为mp4格式
os.system("copy /b *.ts hello.mp4")
print("转换成功")

运行程序,已经可以看到.ts文件被合成为一个.mp4格式的视频了,可以正常播放,就是视频时长不准确

 

以上是关于一文看懂,python抓取m3u8里ts加密视频及合成多线程写入的问题的主要内容,如果未能解决你的问题,请参考以下文章

网页视频经过m3u8 技术切片且加密的TS文件在播放时有缓存文件,但提取出来就无法播放了,这种情况

python爬虫练习18:爬虫抓取视频思路2

.ts视频采用m3u8方式,已知是 AES-128 为加密方式,求教

关于m3u8格式的视频文件ts转mp4下载和key加密问题

什么是m3u8格式? Python合并ts文件至mp4格式及解密教程

ts文件、m3u8文件 key文件