企业网站群建设模式,个人wordpress是否需要备案,贵阳市小程序网站开发公司,电商网站的分辨率文章目录 一、完整代码二、视频分析1. 认识m3u8文件2. 获取密钥#xff0c;构建解密器3. 下载ts文件4. 合并ts文件为mp4 三、总结 一、完整代码
完整代码如下#xff1a;
import requests
from multiprocessing import Pool
import re
import os
from tqdm import tqdm
fro… 文章目录 一、完整代码二、视频分析1. 认识m3u8文件2. 获取密钥构建解密器3. 下载ts文件4. 合并ts文件为mp4 三、总结 一、完整代码
完整代码如下
import requests
from multiprocessing import Pool
import re
import os
from tqdm import tqdm
from Crypto.Cipher import AES# 创建临时文件夹
dirs ts_list_need_to_merge/
os.makedirs(dirs, exist_okTrue)headers {Accept: */*,Accept-Language: zh-CN,zh;q0.9,en;q0.8,en-GB;q0.7,en-US;q0.6,Connection: keep-alive,Origin: http://www.kpd510.me,Referer: http://www.kpd510.me/,Sec-Fetch-Dest: empty,Sec-Fetch-Mode: cors,Sec-Fetch-Site: cross-site,User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36 Edg/116.0.1938.69,sec-ch-ua: Chromium;v116, Not)A;Brand;v24, Microsoft Edge;v116,sec-ch-ua-mobile: ?0,sec-ch-ua-platform: Windows,}def parse_m3u8_text(m3u8_text):m3u8_text m3u8_text.split()encode_info [line for line in m3u8_text if line.startswith(#EXT-X-KEY:)][0]pattern r#EXT-X-KEY:METHOD(.*),URI\(.*)\ ## 获得加密method 和 key.key的urlmatch re.search(pattern, encode_info)if match:method match.group(1)key_url match.group(2)else:raise 解析失败## 获得ts文件urlts_list [line for line in m3u8_text if line.endswith(ts)]return method, key_url, ts_listdef decrypt_content_and_save_file(filename, content, decrypter):with open(filename, modewb) as f:f.write(decrypter.decrypt(content))def merge_ts_to_mp4(filename, ts_file_list):with open(filename, modeab) as f1:for ts_file in ts_file_list:with open(ts_file, moderb) as f2:f1.write(f2.read())print(filename, 完成)def process_one_url(ts_url, key):decrypter AES.new(key, AES.MODE_CBC)filename dirs os.path.split(ts_url)[-1]content requests.get(ts_url, headersheaders).contentdecrypt_content_and_save_file(filename, content, decrypter)return filenamedef download_method_1(ts_list, key):# 普通次序一个一个下载耗时11分钟ts_file_list []for ts_url in tqdm(ts_list):filename process_one_url(ts_urlts_url, keykey)ts_file_list.append(filename)return ts_file_listdef download_method_2(ts_list, key, processes_nums2):# 多进程下载 耗时1分钟class CallBack:def __init__(self, nums) - None:self.pbar tqdm(totalnums)self.filenames []def callback(self, filename):self.pbar.update(1)self.filenames.append(filename)callback CallBack(len(ts_list))pool Pool(processesprocesses_nums)for ts_url in ts_list:pool.apply_async(process_one_url, (ts_url, key), error_callbackprint, callbackcallback.callback)pool.close()pool.join()callback.pbar.close()return [dirs os.path.split(ts_url)[-1] for ts_url in ts_list]if __name__ __main__:m3u8_url https://play.bo262626.com/20231108/xV1bY9Cn/700kb/hls/index.m3u8response requests.get(m3u8_url, headersheaders)m3u8 response.textmethod, key_url, ts_list parse_m3u8_text(m3u8)key_url https://play.bo262626.com key_urlts_list [https://play.bo262626.com item for item in ts_list]key requests.get(key_url, headersheaders).contentts_file_list download_method_2(ts_list, keykey, processes_nums10)merge_ts_to_mp4(test.mp4, ts_file_list)二、视频分析
1. 认识m3u8文件
m3u8的结构详细分析可以看这个链接m3u8 文件格式详解 - 简书 (jianshu.com)这里我们只简要介绍一下
相信无论多小白都应该知道如何打开开发者模型解析得到下面的结果 要注意的是只有预览里面包含了ts信息的才算是我们需要的m3u8文件大家可以看到左侧有两个index.m3u8其中一个是没有ts信息的所以我们直接忽略掉现在我们先下载来再来具体分析一下m3u8文件以及里面的内容分别表达什么意思
下载代码如下
import requests
import reheaders {Accept: */*,Accept-Language: zh-CN,zh;q0.9,en;q0.8,en-GB;q0.7,en-US;q0.6,Connection: keep-alive,Origin: http://www.kpd510.me,Referer: http://www.kpd510.me/,Sec-Fetch-Dest: empty,Sec-Fetch-Mode: cors,Sec-Fetch-Site: cross-site,User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36 Edg/116.0.1938.69,sec-ch-ua: Chromium;v116, Not)A;Brand;v24, Microsoft Edge;v116,sec-ch-ua-mobile: ?0,sec-ch-ua-platform: Windows,
}response requests.get(https://play.bo262626.com/20231108/xV1bY9Cn/700kb/hls/index.m3u8, headersheaders)m3u8 response.textm3u8文件的实质是一个视频的url列表其中ts是计算器可以直接播放的视频格式文件但是直接下载是可能被加了密的文件我们需要m3u8文件内容信息进行解密
我们可以这样理解m3u8把一个完整的mp4视频切割成了很多的小块每一个小块在m3u8都是ts文件格式并在m3u8中采取了加密的措施至于为什么要加密这里就不多介绍
在一般的视频爬取中我们只需要考虑两个部分一个是EXT-X-KEY一个是ts 其中EXT-X-KEY包含了ts的加密方式ts包含了ts文件的下载地址
在红色部分也就是EXT-X-KEY部分我们可以从METHOD中获取到采取的加密方式是AES-128同时看到URI的地址/20231126/10VkaJks/700kb/hls/key.key这也就是AES加密密匙的地址key.key接下来我们写一个文件来对m3u8文件解析目的是提取出红色部分和蓝色部分
代码如下
def parse_m3u8_text(m3u8_text):m3u8_text m3u8_text.split()encode_info [line for line in m3u8_text if line.startswith(#EXT-X-KEY:)][0]pattern r#EXT-X-KEY:METHOD(.*),URI\(.*)\ ## 获得加密method 和 key.key的urlmatch re.search(pattern, encode_info)if match:method match.group(1)key_url match.group(2)else:raise 解析失败## 获得ts文件urlts_list [line for line in m3u8_text if line.endswith(ts)]return method, key_url, ts_list## 在这里我们直接把m3u8文本丢进去就可以获得
## method, key_url, ts_list
method, key_url, ts_list parse_m3u8_text(m3u8)
## method AES-128
## key_url /20231108/xV1bY9Cn/700kb/hls/key.key
## ts_list [...ts, ...ts, ...]2. 获取密钥构建解密器
因为构建解密器我们需要密钥而密钥存储在key.key中首先我们需要解析key_url获取密钥
在这里可以明显的看到key_url /20231108/xV1bY9Cn/700kb/hls/key.key这不是一个完整的url我们在这里加上获取m3u8请求的主域名便好
代码如下
headers {Accept: */*,Accept-Language: zh-CN,zh;q0.9,en;q0.8,en-GB;q0.7,en-US;q0.6,Connection: keep-alive,Origin: http://www.kpd510.me,Referer: http://www.kpd510.me/,Sec-Fetch-Dest: empty,Sec-Fetch-Mode: cors,Sec-Fetch-Site: cross-site,User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36 Edg/116.0.1938.69,sec-ch-ua: Chromium;v116, Not)A;Brand;v24, Microsoft Edge;v116,sec-ch-ua-mobile: ?0,sec-ch-ua-platform: Windows,
}key_url https://play.bo262626.com key_url
key requests.get(key_url, headersheaders).content
# 这里我们得到key b388d590fabfeabcf 是一个二进制结果得到了密钥再结合加密方式AES-128我们就可以构建一个解密器构建解密器代码如下
from Crypto.Cipher import AES
## 这里网络爬取视频一般是MODE_CBC模式
decrypter AES.new(key, AES.MODE_CBC)这里要提起的是网络上的m3u8文件采取的加密一般是AES.MODE_CBC模式在后续操作中如果这里出问题就换MODE一个一个试就好
3. 下载ts文件
由于有许多的ts文件我们有三种方法第一是简单的requests请求一个一个下这也是最费时的一种第二个是多进程或者多线程的方式下载第三个是采用协程的方式接下来我们一个个实现
在开始之间ts_list存在同样的问题就是需要重构url这里代码如下
ts_list [https://play.bo262626.com item for item in ts_list]# 这里得到
# [https://play.bo262626.com/20231108/xV1bY9Cn/700kb/hls/o3jSJ9mc.ts,
# https://play.bo262626.com/20231108/xV1bY9Cn/700kb/hls/GNHDlClJ.ts,
# https://play.bo262626.com/20231108/xV1bY9Cn/700kb/hls/zKym5c6V.ts,
# https://play.bo262626.com/20231108/xV1bY9Cn/700kb/hls/4ll4NQH3.ts,
# https://play.bo262626.com/20231108/xV1bY9Cn/700kb/hls/RwUOniSQ.ts ...]再测试一下解密器是否可以
import os
from tqdm import tqdmdirs ts_list_need_to_merge/
os.makedirs(dirs, exist_okTrue)def decrypt_content_and_save_file(filename, content):with open(filename, modewb) as f:f.write(decrypter.decrypt(content))def process_one_url(ts_url, key):decrypter AES.new(key, AES.MODE_CBC)filename dirs os.path.split(ts_url)[-1]content requests.get(ts_url, headersheaders).contentdecrypt_content_and_save_file(filename, content, decrypter)return filenametest_content requests.get(ts_list[0], headersheaders).content
decrypt_content_and_save_file(test.ts, test_content)## 打开视频看是否能打开
## 如果能打开说明解密没问题直接requests: 代码如下
def download_method_1(ts_list, key):# 这里弄一个filename_list 方便后续合并ts到mp4ts_file_list []for ts_url in tqdm(ts_list):filename process_one_url(ts_urlts_url, keykey)ts_file_list.append(filename)return ts_file_list# 下载测试
ts_file_list download_method_1(ts_list, key)实现挺慢的不合理 多进程: 代码如下
def download_method_2(ts_list, key, processes_nums2):class CallBack:def __init__(self, nums) - None:self.pbar tqdm(totalnums)self.filenames []def callback(self, filename):self.pbar.update(1)self.filenames.append(filename)callback CallBack(len(ts_list))pool Pool(processesprocesses_nums)for ts_url in ts_list:pool.apply_async(process_one_url, (ts_url, key), error_callbackprint, callbackcallback.callback)pool.close()pool.join()callback.pbar.close()return [dirs os.path.split(ts_url)[-1] for ts_url in ts_list]ts_file_list download_method_2(ts_list, keykey, processes_nums10)爬取巨快1分钟下载20多分钟的视频 4. 合并ts文件为mp4
在完成前面的步骤后直接ab的方式把所有的文件按顺序加入就好
def merge_ts_to_mp4(filename, ts_file_list):with open(filename, modeab) as f1:for ts_file in ts_file_list:with open(ts_file, moderb) as f2:f1.write(f2.read())merge_ts_to_mp4(test.mp4, ts_file_list)后续如果需要删除ts_list_need_to_merge/这个临时文件夹里面的所有内容直接运行下面代码
import send2trashsend2trash.send2trash(ts_list_need_to_merge/) # send2trash.send2trash(dirs)三、总结
别在图书馆测试这段代码