HLS技術(shù)介紹
現(xiàn)在大部分視頻客戶端都采用HTTP Live Streaming,而不是直接播放MP4等視頻文件(HLS,Apple為了提高流播效率開發(fā)的技術(shù))。HLS技術(shù)的特點(diǎn)是將流媒體切分為若干【TS片段】(比如幾秒一段),然后通過一個【M3U8列表文件】將這些TS片段批量下載供客戶端播放器實(shí)現(xiàn)實(shí)時流式播放。因此,在爬取HLS的流媒體文件的思路一般是先【下載M3U8文件】并分析其中內(nèi)容,然后在批量下載文件中定義的【TS片段】,最后將其【組合】成mp4文件或者直接保存TS片段。
說說簡單,其實(shí)在實(shí)際操作中,會遇到很多復(fù)雜的問題,例如m3u8文件下載不下來,ts片段文件被加密了,甚至加密ts片段的密鑰也被加密了。
HLS作用
HLS技術(shù)目前在主流的應(yīng)用產(chǎn)品中被應(yīng)用的比較廣。主要的原因在于,HLS技術(shù)比傳統(tǒng)的流媒體技術(shù)的好處在于,視頻一旦切分完成,之后的分發(fā)過程完全不需要額外使用任何專門軟件,僅僅普通的Web服務(wù)器即可,這樣就降低了對服務(wù)器的技術(shù)要求。
另外,用TS做流媒體封裝還有一個好處,就是不需要加載完整視頻后播放,大大減少了首次載人的延遲,提升了用戶體驗。
此外,HTTP Live Streaming的最大優(yōu)勢為自適應(yīng)碼率流播。客戶端會根據(jù)網(wǎng)絡(luò)狀況自動選擇不同碼率的視頻流,條件允許的情況下使用高碼率,網(wǎng)絡(luò)繁忙時使用低碼率,并且自動在二者間隨意切換。這對移動設(shè)備網(wǎng)絡(luò)狀況不穩(wěn)定的情況下保障流暢播放非常有幫助。
M3U8文件詳解
如果想要爬取HLS技術(shù)下的資源數(shù)據(jù),首先要對M3U8的數(shù)據(jù)結(jié)構(gòu)和字段定義非常了解。M3U8是一個擴(kuò)展文件格式,由M3U擴(kuò)展而來。那么什么事M3U呢?
M3U文件
M3U這種文件格式,本質(zhì)上說不是音頻視頻文件,它是音頻視頻文件的列表文件,是純文本文件。
M3U這種文件被獲取后,播放軟件并不是播放它,而是根據(jù)它的記錄找到媒體的網(wǎng)絡(luò)地址進(jìn)行在線播放。也就是說,M3U格式的文件只是存儲多媒體播放列表,并提供了一個指向其他位置的音頻視頻文件的索引,播放的是那些被指向的文件。
為了能夠更好的理解M3U的概念,我們先簡單做一個M3U文件(myTest.m3u)。在電腦中隨便找?guī)讉€MP3,MP4文件依次輸入這些文件的路徑,myTest.m3u文件內(nèi)容如下
E:\Users\m3u8\劉德華 - 無間道.mp4
E:\Users\m3u8\那英 - 默.mp3
E:\Users\m3u8\周杰倫 - 不能說的秘密.mp4
E:\Users\m3u8\花粥 - 二十歲的某一天.mp3
E:\Users\m3u8\周深 - 大魚.mp4
M3U8文件
M3U8也是一種M3U的擴(kuò)展格式(高級的M3U,所以也屬于M3U)。下面我們將了解一下M3U8中定義的幾個非常重要的關(guān)鍵字:
#EXTM3U:每個M3U文件第一行必須是這個tag標(biāo)識。(簡單了解)
#EXT-X-VERSION:版本,此屬性可用可不用。(簡單了解)
#EXT-X-TARGETDURATION:目標(biāo)持續(xù)時間,是用來定義每個TS的【最大】duration(持續(xù)時間)。(簡單了解)
#EXT-X-ALLOW-CACHE是否允許允許高速緩存。(簡單了解)
#EXT-X-MEDIA-SEQUENCE定義當(dāng)前M3U8文件中第一個文件的序列號,每個ts文件在M3U8文件中都有固定唯一的序列號。(簡單了解)
#EXT-X-DISCONTINUITY:播放器重新初始化(簡單了解)
#EXT-X-KEY定義加密方式,用來加密的密鑰文件key的URL,加密方法(例如AES-128),以及IV加密向量。(記?。?
#EXTINF:指定每個媒體段(ts文件)的持續(xù)時間,這個僅對其后面的TS鏈接有效,每兩個媒體段(ts文件)間被這個tag分隔開。(簡單了解)
#EXT-X-ENDLIST表明M3U8文件的結(jié)束。(簡單了解)
圖片(爬取m3u8文件)
**M3U8示例:**大家會看到在該文件中有大量的ts文件的鏈接地址,這個就是我們之前描述的真正的視頻文件。其中任何一個ts文件都是一小段視頻,可以單獨(dú)播放。我們做視頻爬蟲的目標(biāo)就是把這些ts文件都爬取下來。
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:19
#EXT-X-ALLOW-CACHE:YES
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-KEY:METHOD=AES-128,URI="https://edu.aliyun.com/hls/1109/clef/YnBGq7zAJf1Is7xIB5v8vI7AIORwwG9W",IV=0x0fe82567a6be41afda68d82d3724976a
#EXTINF:8.583,
https://xuecdn2.aliyunedu.net/headLeader-0/20170519032524-ggauw1x00qo0okgk-conv/e_20170519032524-ggauw1x00qo0okgk-conv_hd_seg_0.ts
#EXT-X-DISCONTINUITY
#EXT-X-KEY:METHOD=AES-128,URI="https://edu.aliyun.com/hls/2452/clef/0VqtrHq9IkTfOsLqy0iC1FP9342VZm1s",IV=0xdebe4353e61b56e4ecfe0240ca3f89f5
#EXTINF:10.080,
https://xuecdn2.aliyunedu.net/courselesson-50224/20170630095028-3xsfwyxw20cgwws8-conv/e_20170630095028-3xsfwyxw20cgwws8-conv_hd_seg_0.ts
#EXT-X-KEY:METHOD=AES-128,URI="https://edu.aliyun.com/hls/2452/clef/0VqtrHq9IkTfOsLqy0iC1FP9342VZm1s",IV=0x8a3ce90cf18587963953b948487c1729
#EXT-X-KEY:METHOD=AES-128,URI="https://edu.aliyun.com/hls/2452/clef/0VqtrHq9IkTfOsLqy0iC1FP9342VZm1s",IV=0x3f1c20b9dd4459d0adf972eaba85e0a2
#EXTINF:10.000,
https://xuecdn2.aliyunedu.net/courselesson-50224/20170630095028-3xsfwyxw20cgwws8-conv/e_20170630095028-3xsfwyxw20cgwws8-conv_hd_seg_104.ts
#EXT-X-ENDLIST
EXT-X-KEY中的密鑰文件
對于大多數(shù)的M3U8視頻,一般是不加密的。對于一些重要的視頻服務(wù)商,他們會對其視頻做加密處理。M3U8視頻目前的標(biāo)準(zhǔn)加密方式是使用AES-128進(jìn)行加密處理。如果視頻是加密的,就會在M3U8文件中出現(xiàn)以下信息:
#EXT-X-KEY:METHOD=AES-128,URI="https://edu.aliyun.com/hls/2452/clef/0VqtrHq9IkTfOsLqy0iC1FP9342VZm1s",IV=0x3f1c20b9dd4459d0adf972eaba85e0a2
其中METHOD為加密方法,標(biāo)準(zhǔn)是AES-128。
Key是密鑰文件的下載地址(密鑰為16字節(jié)大小的文件,需要下載)。
IV是加密向量(16個字節(jié)大小的16進(jìn)制數(shù)),如果沒有IV值則使用b"0000000000000000"填充即可。
注意:Key和IV是AES加密解密的必要信息,這里我們就不用深入講解。大家只需要知道Key和IV的值會作為解密函數(shù)的參數(shù)直接調(diào)用就可以了。如果文件中沒有包含#EXT-X-KEY,則媒體文件將不會被加密。
需求
爬取美劇網(wǎng)視頻:https://www.meijuw.com/vodplay/3985-1-1/
設(shè)置
鎖定
下載m3u8文件
代碼
# import requests
# from urllib.parse import urljoin
# import re
# import os
# # pip install pycryptodome
# from Crypto.Cipher import AES
# dirName = 'tsLib'
# if not os.path.exists(dirName):
# os.mkdir(dirName)
#
# headers = {
# 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36'
# }
#
# # 一級m3u8地址
# # m1_url = 'https://new.iskcd.com/20220311/hzfUuf6B/index.m3u8'
# m1_url = 'https://v4.cdtlas.com/20220311/xEaAxRVd/index.m3u8'
# m1_page_text = requests.get(url=m1_url,headers=headers).text
# # print(m1_page_text)
#
# # 從一級m3u8文件中解析出二級m3u8地址
# m1_page_text = m1_page_text.strip() #取出收尾的回車
# # 二級m3u8地址
# m2_url = ''
# for line in m1_page_text.split('\n'):
# if not line.startswith('#'):
# m2_url = line
# # 將m1_url 和m2_url不同之處補(bǔ)充到m2_url中
# m2_url = urljoin(m1_url,m2_url)
# # 至此就獲取到了完整的二級文件地址
# # 請求鏈接地址
# # print(m2_url)
# # 請求二級文件地址內(nèi)容
# m2_page_text = requests.get(url=m2_url,headers=headers).text
# m2_page_text = m2_page_text.strip()
# # print(m2_page_text)
#
# # 解析出解密秘鑰key的地址
# key_url = re.findall('URI="(.*?)"',m2_page_text,re.S)[0]
# key_url =urljoin(m1_url,key_url)
# # print(key_url)
# # 請求key的地址,獲取知秘鑰
# # 注意: key和iv需要為bytes類型
# key = requests.get(url=key_url,headers=headers).content
# iv = b'0000000000000000'
# # print(key)
# # 解析出每一個ts切片的地址
# ts_url_list = []
# for line in m2_page_text.split('\n'):
# if not line.startswith("#"):
# ts_url = line
# ts_url = urljoin(m1_url,ts_url)
# ts_url_list.append(ts_url)
#
# # print(ts_url_list)
# # 請求到每一個ts切片的數(shù)據(jù)
# for url in ts_url_list:
# # 獲取ts片段的數(shù)據(jù)
# ts_data = requests.get(url=url,headers=headers).content
# # 需要對ts片段數(shù)據(jù)進(jìn)行解密(需要用到key和iv)
# aes = AES.new(key=key,mode=AES.MODE_CBC,iv=iv)
# desc_data = aes.decrypt(ts_data) # 獲取了解密后的數(shù)據(jù)
# ts_name = url.split('/')[-1]
# ts_path = dirName+'/'+ts_name
# with open(ts_path,'wb') as fp:
# # 需要將解密后的數(shù)據(jù)寫入文件進(jìn)行保存
# fp.write(desc_data)
# print(ts_name,'下載保存成功!')
代碼 -> 使用協(xié)程下載.ts文件
import requests
from urllib.parse import urljoin
import re
import os
import asyncio
import aiohttp
# pip install pycryptodome
from Crypto.Cipher import AES
dirName = 'tsLib'
if not os.path.exists(dirName):
os.mkdir(dirName)
headers = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36'
}
# 一級m3u8地址
# m1_url = 'https://new.iskcd.com/20220311/hzfUuf6B/index.m3u8'
m1_url = 'https://v4.cdtlas.com/20220311/xEaAxRVd/index.m3u8'
# m1_url = 'https://iqiyi.sd-play.com/20211104/6AoEDjLD/index.m3u8'
# m1_url = 'https://new.iskcd.com/20220228/hc80JRB9/1100kb/hls/playlist_up.m3u8'
# m1_url = 'https://iqiyi.sd-play.com/20220112/tvnvoPbM/index.m3u8'
m1_page_text = requests.get(url=m1_url,headers=headers).text
# print(m1_page_text)
# 從一級m3u8文件中解析出二級m3u8地址
m1_page_text = m1_page_text.strip() #取出收尾的回車
# 二級m3u8地址
m2_url = ''
for line in m1_page_text.split('\n'):
if not line.startswith('#'):
m2_url = line
# 將m1_url 和m2_url不同之處補(bǔ)充到m2_url中
m2_url = urljoin(m1_url,m2_url)
# 至此就獲取到了完整的二級文件地址
# 請求鏈接地址
# print(m2_url)
# 請求二級文件地址內(nèi)容
m2_page_text = requests.get(url=m2_url,headers=headers).text
m2_page_text = m2_page_text.strip()
# print(m2_page_text) # 打印輸出整個.ts文件
# 解析出解密秘鑰key的地址
key_url = re.findall('URI="(.*?)"',m2_page_text,re.S)[0]
key_url =urljoin(m1_url,key_url)
# print(key_url) # 打印請求鏈接地址 https://iqiyi.shanshanku.com/20211104/6AoEDjLD/1200kb/hls/key.key
# 請求key的地址,獲取知秘鑰
# 注意: key和iv需要為bytes類型
key = requests.get(url=key_url,headers=headers).content
iv = b'0000000000000000'
# print(key) # 得到解密密鑰
# 解析出每一個ts切片的地址
ts_url_list = []
for line in m2_page_text.split('\n'):
if not line.startswith("#"):
ts_url = line
ts_url = urljoin(m1_url,ts_url)
ts_url_list.append(ts_url)
# print(ts_url_list) # 列表組成的逗號分隔的.ts文件
# 異步請求到每一個ts切片的數(shù)據(jù)
async def get_ts(url):
async with aiohttp.ClientSession() as sess:
async with await sess.get(url=url,headers=headers) as response:
ts_data = await response.read() # 獲取byte形式的響應(yīng)數(shù)據(jù)
# 需要對ts片段數(shù)據(jù)進(jìn)行解密(需要用到key和iv)
aes = AES.new(key=key, mode=AES.MODE_CBC, iv=iv)
desc_data = aes.decrypt(ts_data) # 獲取了解密后的數(shù)據(jù)
return [desc_data,url]
def download(t):
r_list = t.result()
data =r_list[0]
url = r_list[1] # ts文件的地址
ts_name = url.split('/')[-1]
ts_path = dirName + '/' + ts_name
with open(ts_path, 'wb') as fp:
# 需要將解密后的數(shù)據(jù)寫入文件進(jìn)行保存
fp.write(data)
print(ts_name, '下載保存成功!')
tasks = []
for url in ts_url_list:
c = get_ts(url)
task = asyncio.ensure_future(c)
task.add_done_callback(download)
tasks.append(task)
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
執(zhí)行過程
文章來源:http://www.zghlxwxcb.cn/news/detail-406386.html
保存文件結(jié)果
文章來源地址http://www.zghlxwxcb.cn/news/detail-406386.html
到了這里,關(guān)于【M3U8】python(流視頻數(shù)據(jù))的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!