幹活幹活,區區懶癌已經阻擋不了澎湃的洪荒之力了...... python
運行環境:Windows基於python3.6windows
-------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ------------------瀏覽器
抓取視頻時遇到M3U8的確挺煩人的,去年年末實習,因爲項目需求因此和一個同事主攻蟒蛇爬蟲,抓取含有清晰人臉的圖片和視頻,在爬取一些視頻網站和直播網站時就被「它」糊了一臉,做爲一隻,呃,不對,是兩隻剛入爬蟲坑的菜鳥,在視頻加載播放時找不到啥關於的.mp4,.MKV的連接,反而出現很多.ts的連接,而後爬取找度娘,重點以下:服務器
M3U8是蘋果公司推出一種視頻播放標準,是一種文件檢索格式,將視頻切割成一小段一小段的TS格式的視頻文件,而後存在服務器中(如今爲了減小I / O訪問次數,通常存在服務器的內存中),經過M3U8解析出來路徑,而後去請求,是如今比較流行的一種加載方式,諸如騰訊視頻之類大多都是切割成TS流進行加載。ide
但當時仍是暈,理論和實際處理是兩回事,那時候關於如何爬取M3U8的博客不是不少並且不多說到重點(給俺下載代碼,理論改天再補)樣麼是其它語言寫的更懵,近來卻是愈來愈多了(惋惜當時沒遇上),最後找到篇關於下載的卻發現是python2的代碼,由於那時候剛開始使用蟒以前學的是Java中,因此調了好一會仍是運行失敗,可是也收穫了很多,以後和同事研究了兩天試驗了幾個方案最終肯定採用邊下邊合的方式進行處理,上個月底從公司離職了,因爲公司管理較爲嚴格禁止向外網(公司局域網之外)發送消息或拷貝文件,(嗯,拍照也不行,就似〜這麼嚴)因此這幾天晚上下班回來抽空寫寫改改又另起了爐竈,代碼以下,隨意寫的因此異常處理和模擬瀏覽器啥的就沒加了(懶)!工具
注:僅限窗口下使用,若是要在Linux的上使用須要修改合併命令,嗯,或者等幾天我再來篇兼容的網站
# !/user/bin/env python # -*- coding: utf-8 -*- # au: caopeiya # 20180808 import os, shutil import urllib.request, urllib.error, requests # 打開並讀取網頁內容 def getUrlData(url): try: urlData = urllib.request.urlopen(url, timeout=20) # .read().decode('utf-8', 'ignore') return urlData except Exception as err: print(f'err getUrlData({url})\n', err) return -1 # 下載文件-urllib.request def getDown_urllib(url, file_path): try: urllib.request.urlretrieve(url, filename=file_path) return True except urllib.error.URLError as e: # hasttr(e, 'code'),判斷e 是否有.code屬性,由於不肯定是否是HTTPError錯誤,URLError包含HTTPError,可是HTTPError之外的錯誤是不返回錯誤碼(狀態碼)的 if hasattr(e, 'code'): print(e.code) # 打印服務器返回的錯誤碼(狀態碼),如403,404,501之類的 elif hasattr(e, 'reason'): print(e.reason) # 打印錯誤緣由 def getVideo_urllib(url_m3u8, path, videoName): print('begin run ~~\n') # urlData = getUrlData(url_m3u8).readlines() urlData = getUrlData(url_m3u8) num = 0 tempName_video = os.path.join(path, f'{videoName}.ts') # f'{}' 至關於'{}'.format() 或 '%s'%videoName # print(urlData) for line in urlData: # 解碼,因爲是直接使用了所抓取的連接內容,因此須要按行解碼,若是提早解碼則不能使用直接進行for循環,會報錯 # 改用上面的readlines()或readline()也能夠,但更繁瑣些,一樣須要按行解碼,效率更低 url_ts = line.decode('utf-8') tempName_ts = os.path.join(path, f'{num}.ts') # f'{}' 至關於'{}'.format() if not '.ts' in url_ts: continue else: if not url_ts.startswith('http'): # 判斷字符串是否以'http'開頭,若是不是則說明url連接不完整,須要拼接 # 拼接ts流視頻的url url_ts = url_m3u8.replace(url_m3u8.split('/')[-1], url_ts) print(url_ts) getDown_urllib(url_ts, tempName_ts) # 下載視頻流 if num == 0: # 重命名,已存在則自動覆蓋 shutil.move(tempName_ts, tempName_video) num += 1 continue cmd = f'copy /b {tempName_video}+{tempName_ts} {tempName_video}' res = os.system(cmd) if res == 0: os.system(f'del {tempName_ts}') if num == 20: # 限制下載的ts流個數,這個視頻挺長有四百多個.ts文件,因此限制一下 break num += 1 continue print(f'Wrong, copy {num}.ts-->{videoName}.ts failure') return False os.system(f'del {path}/*.ts') # 調用windows命令行(即cmd)工具,運行命令 filename = os.path.join(path, f'{videoName}.mp4') shutil.move(tempName_video, filename) print(f'{videoName}.mp4 finish down!') if __name__ == '__main__': url_m3u8 = 'http://wscdn.alhls.xiaoka.tv/201886/2f5/75a/HoHdTc1LjUaBjZbJ/index.m3u8' path = r'D:\videos' videoName = url_m3u8.split('/')[-2] getVideo_urllib(url_m3u8, path, videoName)
注:修改文件名時,特地選擇shutil模塊(能夠看做操做系統的高級版)的移動方法,雖然移動主要是用來移動文件的,重命名算是附帶的,不過強制覆蓋的特色在這裏頗有用,避免中斷後從新下載時重命名產生異常。url
PS:說來有趣,7月31號,也就是離職的那天上午,靈感突顯,利用請求下載文件的寫入特色,完全解決了調用命令行致使的不兼容的窗口之外環境的問題,哈哈,因此下一篇就它了。spa