1. 程式人生 > >python爬取網站m3u8視訊,將ts解密成mp4,合併成整體視訊

python爬取網站m3u8視訊,將ts解密成mp4,合併成整體視訊

一些網站會提供m3u8視訊地址,以供下載觀看。或者一些網站經過分析後發現是使用m3u8格式進行播放的,這時使用m3u8的地址連結就可以下載到相應的視訊。

一、關於m3u8:(https://blog.csdn.net/baidu_34418350/article/details/64922512)

m3u8是蘋果公司推出一種視訊播放標準,是m3u的一種,不過 編碼方式是utf-8,是一種檔案檢索格式,將視訊切割成一小段一小段的ts格式的視訊檔案,然後存在伺服器中(現在為了減少I/o訪問次數,一般存在伺服器的記憶體中),通過m3u8解析出來路徑,然後去請求。

第一層

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=2650800,RESOLUTION=1920x1080
1.m3u8

觀察資料嗎,沒有用http://開頭時,不是真正路徑,需要拼接字串再次請求:http://cdn.can.cibntv.net/12/201702161000/rexuechangan01/1.m3u8 得到資料

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:14
#EXTINF:11.480, 
20170215T224129-1-0.ts
#EXTINF:11.480, 
20170215T224129-1-1.ts
#EXTINF:10.480, 
20170215T224129-1-2.ts
#EXTINF:11.400, 
20170215T224129-1-3.ts
#EXTINF:11.120, 

20170215T224129-1-4.ts

。。。

#EXTINF:1.000000,
3RCP49g82011159.ts
#EXTINF:0.600000,
3RCP49g82011160.ts

#EXT-X-ENDLIST

看到ts結尾的檔案,這才是視訊真正的存放路徑:

這時候用瀏覽器下載就可以播放。不過這個播放不用我們去解析 android 4.0以後的videoView 就支援自動解析,並拼接播放。

安卓程式碼:

Uri uri = Uri.parse("http://cdn.can.cibntv.net/12/201702161000/rexuechangan01/rexuechangan01.m3u8");
video_view.setMediaController(new MediaController(this
)); video_view.setVideoURI(uri); video_view.requestFocus(); ideo_view.start();
這樣就可以簡單的播放M3u8格式的視訊了。

下載到本地,可直接用視訊軟體開啟:


二、視訊下載

可以用python指令碼自動下載這些ts檔案,但實際上有些網站的ts檔案是用AES-128加密過的,所以需要解密才能播放。

加密過的視訊在第二層m3u8中會有一個key檔案連結:

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:2
#EXT-X-MEDIA-SEQUENCE:0

#EXT-X-KEY:METHOD=AES-128,URI="key.key"  #key金鑰檔案

需要去讀取這個key檔案,才能拿到解密金鑰。

# -*- coding:utf-8 -*-  
import os
import sys
import requests
import datetime
from Crypto.Cipher import AES
from binascii import b2a_hex, a2b_hex

reload(sys)
sys.setdefaultencoding('utf-8')

def download(url):
    download_path = os.getcwd() + "\download"
    if not os.path.exists(download_path):
        os.mkdir(download_path)
        
    #新建日期資料夾
    download_path = os.path.join(download_path, datetime.datetime.now().strftime('%Y%m%d_%H%M%S'))
    #print download_path
    os.mkdir(download_path)
        
    all_content = requests.get(url).text  # 獲取第一層M3U8檔案內容
    if "#EXTM3U" not in all_content:
        raise BaseException("非M3U8的連結")

    if "EXT-X-STREAM-INF" in all_content:  # 第一層
        file_line = all_content.split("\n")
        for line in file_line:
            if '.m3u8' in line:
                url = url.rsplit("/", 1)[0] + "/" + line # 拼出第二層m3u8的URL
                all_content = requests.get(url).text

    file_line = all_content.split("\n")

    unknow = True
    key = ""
    for index, line in enumerate(file_line): # 第二層
        if "#EXT-X-KEY" in line:  # 找解密Key
            method_pos = line.find("METHOD")
            comma_pos = line.find(",")
            method = line[method_pos:comma_pos].split('=')[1]
            print "Decode Method:", method
            
            uri_pos = line.find("URI")
            quotation_mark_pos = line.rfind('"')
            key_path = line[uri_pos:quotation_mark_pos].split('"')[1]
            
            key_url = url.rsplit("/", 1)[0] + "/" + key_path # 拼出key解密金鑰URL
            res = requests.get(key_url)
            key = res.content
            print "key:" , key
            
        if "EXTINF" in line: # 找ts地址並下載
            unknow = False
            pd_url = url.rsplit("/", 1)[0] + "/" + file_line[index + 1] # 拼出ts片段的URL
            #print pd_url
            
            res = requests.get(pd_url)
            c_fule_name = file_line[index + 1].rsplit("/", 1)[-1]
            
            if len(key): # AES 解密
                cryptor = AES.new(key, AES.MODE_CBC, key)  
                with open(os.path.join(download_path, c_fule_name + ".mp4"), 'ab') as f:
                    f.write(cryptor.decrypt(res.content))
            else:
                with open(os.path.join(download_path, c_fule_name), 'ab') as f:
                    f.write(res.content)
                    f.flush()
    if unknow:
        raise BaseException("未找到對應的下載連結")
    else:
        print "下載完成"
    merge_file(download_path)

def merge_file(path):
    os.chdir(path)
    cmd = "copy /b * new.tmp"
    os.system(cmd)
    os.system('del /Q *.ts')
    os.system('del /Q *.mp4')
    os.rename("new.tmp", "new.mp4")
    
if __name__ == '__main__': 
    url = "http://cdn.can.cibntv.net/12/201702161000/rexuechangan01/rexuechangan01.m3u8" 
    download(_url)

三、關於解密報錯:No module named Crypto.Cipher

在python 中使用AES演算法時,會報告上述錯誤,原因是Crypto並非標準模組,需要自己單獨安裝。

from Crypto.Cipher import AES

第一種辦法:pip install pycropt 如果報錯,就選擇第二種辦法。

第二種辦法:使用編譯好的安裝包。http://www.voidspace.org.uk/python/modules.shtml#pycrypto

裝上不好使就換一個試試,安裝上面2個都不好使,就安裝低版本的(我本機安裝上面2個都報錯,安裝下面的版本就可用了)

如果還是報錯就到下面目錄修改大小寫:(我沒遇到這個問題)

C:\Python27\Lib\site-packages\crypto 改成

 C:\Python27\Lib\site-packages\Crypto

四、合併檔案

可以通過cmd命令的方式將所有的ts合併成一個檔案:

copy /b d:\xxx\download_ts\*   d:\xxx\download_ts\new.mp4

直接呼叫merge_file即可,會刪除臨時檔案