文章目錄
- 一、完整代碼
- 二、視頻分析
- 1. 認識m3u8文件
- 2. 獲取密鑰,構建解密器
- 3. 下載ts文件
- 4. 合并ts文件為mp4
- 三、總結
一、完整代碼
完整代碼如下:
import requests
import re
import os
from tqdm import tqdm
from Crypto.Cipher import AES# 創建臨時文件夾
dirs = 'ts_list_need_to_merge/'
os.makedirs(dirs, exist_ok=True)headers = {'Accept': '*/*','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','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";v="116", "Not)A;Brand";v="24", "Microsoft Edge";v="116"','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 download_method_1(ts_list):# 這里弄一個filename_list 方便后續合并ts到mp4ts_file_list = []for ts_url in tqdm(ts_list):filename = dirs + os.path.split(ts_url)[-1]content = requests.get(ts_url, headers=headers).contentdecrypt_content_and_save_file(filename, content)ts_file_list.append(filename)return ts_file_listdef decrypt_content_and_save_file(filename, content):with open(filename, mode='wb') as f:f.write(decrypter.decrypt(content))def merge_ts_to_mp4(filename, ts_file_list):with open(filename, mode='ab') as f1:for ts_file in ts_file_list:with open(ts_file, mode='rb') as f2:f1.write(f2.read())if __name__ == '__main__':m3u8_url = 'https://play.bo262626.com/20231108/xV1bY9Cn/700kb/hls/index.m3u8'response = requests.get(m3u8_url, headers=headers)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, headers=headers).contentdecrypter = AES.new(key, AES.MODE_CBC)ts_file_list = download_method_1(ts_list[:3])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;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.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";v="116", "Not)A;Brand";v="24", "Microsoft Edge";v="116"','sec-ch-ua-mobile': '?0','sec-ch-ua-platform': '"Windows"',
}response = requests.get('https://play.bo262626.com/20231108/xV1bY9Cn/700kb/hls/index.m3u8', headers=headers)m3u8 = response.text
m3u8
文件的實質是一個視頻的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;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.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";v="116", "Not)A;Brand";v="24", "Microsoft Edge";v="116"','sec-ch-ua-mobile': '?0','sec-ch-ua-platform': '"Windows"',
}key_url = 'https://play.bo262626.com' + key_url
key = requests.get(key_url, headers=headers).content
# 這里我們得到key = b'388d590fabfeabcf' 是一個二進制結果
得到了密鑰,再結合加密方式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' ...]
再測試一下解密器是否可以:
def decrypt_content_and_save_file(filename, content):with open(filename, mode='wb') as f:f.write(decrypter.decrypt(content))test_content = requests.get(ts_list[0], headers=headers).content
decrypt_content_and_save_file('test.ts', test_content)## 打開視頻看是否能打開
## 如果能打開說明解密沒問題
直接requests: 代碼如下
import os
from tqdm import tqdmdirs = 'ts_list_need_to_merge/'
os.makedirs(dirs, exist_ok=True)def download_method_1(ts_list):# 這里弄一個filename_list 方便后續合并ts到mp4ts_file_list = []for ts_url in tqdm(ts_list):filename = dirs + os.path.split(ts_url)[-1]content = requests.get(ts_url, headers=headers).contentdecrypt_content_and_save_file(filename, content)ts_file_list.append(filename)return ts_file_list# 下載測試
ts_file_list = download_method_1(ts_list[:4])
實現挺慢的,不合理;

多進程: 代碼如下
# 忘記了,有時間再回來實現
協程: 代碼如下
# 忘記了,有時間再回來實現
4. 合并ts文件為mp4
在完成前面的步驟后,直接ab
的方式把所有的文件按順序加入就好;
def merge_ts_to_mp4(filename, ts_file_list):with open(filename, mode='ab') as f1:for ts_file in ts_file_list:with open(ts_file, mode='rb') 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)
三、總結
別在圖書館測試這段代碼!