前言
一到周末就想搞點有意思的事,比如之前分享的arduino
開發,比如上周分享的博客爬蟲,今天我又想搞點有意思的事,所以就有了今天的內容——python
爬取m3u8
視頻資源。不過需要在這里需要着重說明的是,技術無罪,切勿用技術搞違法犯罪的事,不然日子真的就越來越有判頭了。
知識擴展
在開始抓取m3u8
視頻之前,我們先了解下m3u8
的相關知識,了解的更多,可以讓我們少走彎路。
m3u8是什么?
在此之前,我僅僅知道m3u8
是一種網絡串流,在平時娛樂時候會找一些m3u8
的資源,看看直播啥的,直到今天要分享m3u8
的相關內容,才真正開始搜集m3u8
的相關知識點。關於m3u8
連百度百科都沒有說明,搜到知乎一篇內容(【全網最全】m3u8到底是什么格式?一篇文章搞定m3u8下載),下面的原理圖也是參照的這篇內容:
從上面的原理圖中我們可以得到以下知識點:
- 首先
m3u8
並非是視頻格式,而是視頻文件的索引。 ts
文件才是我們真正播放的視頻資源。ts
是日本高清攝像機拍攝下進行的封裝格式,全稱為MPEG2-TS
。ts
即"Transport Stream
"的縮寫。MPEG2-TS
格式的特點就是要求從視頻流的任一片段開始都是可以獨立解碼的。
m3u8
通常分兩種格式,一種是單碼率(固定分辨率),一種是多碼率(包含多種分別率)。下面就是一個單碼率的m3u8
文件的內容:
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:10
#EXT-X-MEDIA-SEQUENCE:1619459525
#EXT-X-PROGRAM-DATE-TIME:2021-10-23T05:39:42Z
#EXTINF:10.0,
1634967582-1-1619459525.hls.ts
#EXT-X-PROGRAM-DATE-TIME:2021-10-23T05:39:52Z
#EXTINF:10.0,
1634967592-1-1619459526.hls.ts
#EXT-X-PROGRAM-DATE-TIME:2021-10-23T05:40:02Z
#EXTINF:10.0,
1634967602-1-1619459527.hls.ts
這個就是一個多碼率的m3u8
文件,從多碼率的文件格式可以看出來,多碼率中包括了多個單碼率是m3u8
文鏈接:
#EXTM3U
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=500000,RESOLUTION=480x270
500kb/hls/index.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=300000,RESOLUTION=360x202
300kb/hls/index.m3u8
m3u8文件指令
另外我在另外一篇博客發現m3u8
其實是m3u
文件的擴展(參考文檔2
),這可能就是為什么沒有找到m3u8
相關詞條的原因吧,同時在m3u
的詞條中發現了M#U
文件的指令描述(每個字段的含義):
#EXTM3U //必需,表示一個擴展的m3u文件
#EXT-X-VERSION:3 //hls的協議版本號,暗示媒體流的兼容性
#EXT-X-MEDIA-SEQUENCE:xx //首個分段的sequence number
#EXT-X-ALLOW-CACHE:NO //是否緩存
#EXT-X-TARGETDURATION:5 //每個視頻分段最大的時長(單位秒)
#EXT-X-DISCONTINUITY //表示換編碼
#EXTINF: //每個切片的時長
另外關於m3u
的文件指令是有國際標准的,感興趣的小伙伴可以去看下:
http://tools.ietf.org/html/draft-pantos-http-live-streaming-06
好了,關於m3u8
的相關內容,我們就說這么多,感興趣的小伙伴可以自己繼續探索,下面我們看下如何用python
抓取、解析和下載m3u8
文件索引中的視頻文。
抓取、解析、下載
首先我們先要拿到目標視頻資源的索引文件,也就是m3u8
文件。一般稍微懂點web
開發的小伙伴,應該都知道瀏覽器抓包吧,F12
打開瀏覽器控制台,然后選擇Network
,刷新下頁面,在左側資源區找到index.m3u8
文件。
通常會有兩個m3u8
,第一個是獲取視頻碼率列表的,也就是多碼率m3u8
,這個文件我們是沒辦法直接解析的,我們要找的是包含ts
視頻資源的m3u8
文件。這里我隨便在網上搜了一個葫蘆娃的視頻,然后通過控制台拿到m3u8
文件地址:
https://vod1.bdzybf1.com/20200819/wMgIH6RN/1000kb/hls/index.m3u8
下面我們就用python
解析下載這個視頻文件。
解析m3u8文件
解析m3u8
文件最核心的地方是分析m3u8
的文件結構,然后根據其文件內容寫出對應的解析邏輯。這里我推薦直接用requests
庫模擬調用,然后分析響應結果,因為用文本工具之類的查看m3u8
文件的話,換行符\n
、制表符\t
這些看起來不夠直觀,但是requests
就不會有這個問題,因為我們解析的就是requests
的響應結果。
這里的以我們上面m3u8
文件為例響應結果如下:
可以清晰地看出,這個m3u8
文件是通過換行符\n
分割的,有部分m3u8
文件中會出現制表符和換行符組合的情況,所以具體情況具體分析。
另外,我從響應內容中發現,這個視頻資源是進行了AES-128
加密的,所以后面在下載視頻資源的時候要解密。
解析ts視頻地址
因為python
代碼都很簡單,代碼量也不多,所以就不展開講了,看代碼注釋應該可以看懂。這個方法主要是為了獲取ts
視頻文件的地址。
import requests
def getTsFileUrlList(m3u8Url):
# 組裝請求頭
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36'
}
# 請求 m3u8文件,並拿到文件內容
ts_rs = requests.get(url = m3u8Url, headers=headers).text
print(ts_rs)
# 換行符分割文件內容
list_content = ts_rs.split('\n');
print('list_content:{}'.format(list_content))
player_list = []
# 循環分割結果
for line in list_content:
# 以下拼接方式可能會根據自己的需求進行改動
if '#EXTINF' in line:
continue;
# 僅記錄 ts文件地址
elif line.endswith('.ts'):
player_list.append(line)
print('數據列表組裝完成-size: {}'.format(len(player_list)));
return player_list;
運行上面這個方法的話,最終會獲取到m3u8
索引文件中所有的ts
視頻地址,但是由於我們剛才已經從m3u8
文件中發現了,視頻資源是加密的,所以在下載的時候我們需要解密。
下載文件
這里的方法是下載並解密視頻資源,這里我加了一個解密操作,因為注釋夠清晰,所以我也不過多贅述了
def fileDownloadWithdecrypt(fileSavePath, player_list):
# 創建文件夾
if not os.path.exists(fileSavePath):
os.mkdir(fileSavePath);
# 循環 ts 視頻資源列表
for index, url in enumerate(player_list):
# 發送下載請求
ts_video = requests.get(url = url, headers=headers)
# 保存文件
with open('{}/{}.ts'.format(fileSavePath, str(index + 1)), 'wb') as file:
# 視頻資源解密
context = decrypt(ts_video.content);
# 文件寫入
file.write(context)
print('正在寫入第{}個文件'.format(index + 1))
print('下載完成');
下面是解密代碼
from Crypto.Cipher import AES # 用於AES解碼
def decrypt(context):
# 加密的key
key = b'2cd1da2aedacaec8';
# 解密
cryptor = AES.new(key, AES.MODE_CBC, key);
decrypt_content = cryptor.decrypt(context);
return decrypt_content;
這里用的是pycryptodome
庫,安裝方式如下:
pip3 install pycryptodome
關於密文,在m3u8
文件里面已經有了,直接下載就可以拿到,這里我就不通過代碼拿了:
這里下載視頻會比較費時間,為了提高下效率可以用多線程,但是由於時間的關系就不演示了。
合並視頻
合並視頻就更簡單了,就是講前面我們保存的視頻合並成一個完整的視頻,合並完之后的格式是mp4
。
def fileMerge(filePath):
# 查詢出文件中的ts文件
c = os.listdir(filePath)
# 打開視頻保存文件
with open('%s.mp4' % filePath, 'wb+') as f:
# 循環
for i in range(len(c)):
# 打開 ts 視頻文件
x = open('{}/{}.ts'.format(filePath, str(i + 1)), 'rb').read()
f.write(x)
print('合並完成')
我看了下,短短的一集葫蘆娃,總共被分割成272
個ts
文件(好像比這個多,我沒下載完就把網斷了):
272
個ts
文件合並成一個視頻文件:
好了,到這里我們python
爬取m3u8
視頻資源的實例就結束了,今天的示例還算比較完美,目標也比較完美的達成了。
完整代碼如下:
import requests
import os
from Crypto.Cipher import AES # 用於AES解碼
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36'
}
def getTsFileUrlList(m3u8Url):
ts_rs = requests.get(url = m3u8Url, headers=headers).text
print(ts_rs)
list_content = ts_rs.split('\n');
print('list_content:{}'.format(list_content))
player_list = []
index = 1;
for line in list_content:
# 以下拼接方式可能會根據自己的需求進行改動
if '#EXTINF' in line:
continue;
elif line.endswith('.ts'):
player_list.append(line)
print('數據列表組裝完成-size: {}'.format(len(player_list)));
return player_list;
def fileDownloadWithdecrypt(fileSavePath, player_list):
if not os.path.exists(fileSavePath):
os.mkdir(fileSavePath);
for index, url in enumerate(player_list):
ts_video = requests.get(url = url, headers=headers)
with open('{}/{}.ts'.format(fileSavePath, str(index + 1)), 'wb') as file:
context = decrypt(ts_video.content);
file.write(context)
print('正在寫入第{}個文件'.format(index + 1))
print('下載完成');
def fileMerge(filePath):
c = os.listdir(filePath)
with open('%s.mp4' % filePath, 'wb+') as f:
for i in range(len(c)):
x = open('{}/{}.ts'.format(filePath, str(i + 1)), 'rb').read()
f.write(x)
print('合並完成')
def decrypt(context):
key = b'2cd1da2aedacaec8';
cryptor = AES.new(key, AES.MODE_CBC, key);
decrypt_content = cryptor.decrypt(context);
return decrypt_content;
if __name__ == '__main__':
m3u8Url = 'https://vod1.bdzybf1.com/20200819/wMgIH6RN/1000kb/hls/index.m3u8';
videoList = getTsFileUrlList(m3u8Url);
print(videoList)
savePath = "./test"
fileDownloadWithdecrypt(savePath, videoList);
fileMerge(savePath);
運行上面的代碼,你就可以得到一集完整的葫蘆娃
當然,如果你能找到資源,用上面這段代碼爬取小電影也是可以的
總結
今天的視頻爬蟲很簡單,可以說非常簡單,核心技術點也不多,主要涉及如下幾點:
request
請求- 字符串解析(響應結果解析)
- 文件操作
ASE
解密
只需要稍微有一點python
基礎,就可以做出來,所以這里我也沒什么好總結的了。
最后,免費為python
做一個無償廣告。我一直覺得python
是一門不錯的語言,特別是作為腳本使用的時候,真的是太方便了,今天它也依然沒有讓我失望。
其實嚴格來說,我學python
,但是之前一直詬病於它的縮進語法,所以也就一直沒入門,直到做了一段時間java web
開發之后,回頭再看下python
,覺得好簡單,於是就又愉快地使用它了,不過用它寫腳本真的太爽了,短短幾行代碼,搞定java
一個繁瑣的項目,而且用起來也很輕便。
最近一年多的時間,我用它處理過數據、用它跑數據庫統計過數據,然后日程工作中我可以用它生成文件目錄、爬取資料,也感覺我對它越來越有好感,所以在這里強烈安利各位小伙伴都來學下python
,特別是那些非開發崗位的小伙伴,python
簡直是統計數據的利器,雖然不像廣告吹的那么秀,但是技多不壓身呀,而且它真的是可以極大提供我們的效率。
最后的最后,再強調下,技術無罪,切勿用技術搞違法犯罪的事,不然日子真的就越來越有判頭了!
技術無罪,切勿用技術搞違法犯罪的事,不然日子真的就越來越有判頭了!
技術無罪,切勿用技術搞違法犯罪的事,不然日子真的就越來越有判頭了!
重要的事情說三遍!!!
另外,今天忙着搞爬蟲了,設計模式的類圖還沒來得及補,明天要加下油了。好了,鐵子們,晚安吧