生活随笔
收集整理的這篇文章主要介紹了
练习:《斗鱼视频》m3u8流视频采集下载+思路+Python
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
首先感謝以下大佬提供幫助
斗魚視頻下載-> https://www.jianshu.com/p/feccccb097be批量合并處理B站視頻->https://www.wandouip.com/t5i227224/You-Get->https://you-get.org/
思路
- 首先打開了PC端斗魚視頻一個鏈接:https://v.douyu.com/show/0Q8mMY0xXDL749Ad
- 發現一個參數在鏈接中 0Q8mMY0xXDL749Ad
- 通過抓包發現了一個很重要的文件:playlist.m3u8
-
- 里面包含了數個 .ts 的網絡地址;
-
- .ts 文件是可以播放的視頻片段;
-
- 發現 可以通過合并 .ts 片段可以得到完整視頻;
- 出現一個問題:playlist.m3u8 怎么獲取?
-
- 發現:https://v.douyu.com/api/stream/getStreamUrl 可以獲取 playlist.m3u8 文件地址;
-
- 需要POST傳入一些參數才行,發現: sign 參數是一種簽名,一般通過JS生成,找了半天沒有方法生成 sign 參數;
-
- 通過查閱大佬文獻發現:手機端的斗魚視頻有接口可以直接獲取 playlist.m3u8 文件地址,成功越過 sign 簽名防線;
- 手機端斗魚視頻鏈接:https://vmobile.douyu.com/show/0Q8mMY0xXDL749Ad
-
- 通過抓包發現:https://vmobile.douyu.com/video/getInfo?vid=0Q8mMY0xXDL749Ad;
-
- 這就解決了playlist.m3u8 文件獲取問題:json[‘data’][‘video_url’] 第一個難題解決!!;
綜上所述,整理一下具體采集流程:
獲取vid = 0Q8mMY0xXDL749Ad (就是鏈接中的參數);通過 https://vmobile.douyu.com/video/getInfo?vid=0Q8mMY0xXDL749Ad 獲取 playlist.m3u8 文件地址;解析 playlist.m3u8 文件提取所有 .ts文件;下載所有 .ts 文件;合并 .ts 成視頻文件輸出;
Python實現
不要開啟線程池,因為會有一些問題
app.py
config 中可以配置
import requests
import re
import json
import time
import pymongo
import psutil
from hashlib import md5
from moviepy.editor import *
from multiprocessing import Pool#基本配置
config = {'UID':'gKpdxKRWXwaW',#用戶ID'CID':104,#欄目ID'TYPE':1, #1=>按用戶id采集列表,2=>按欄目ID采集列表'TIME_START':1,#起始時間'TIME_ENT':500,#結束時間'PAGE_START':1,#起始頁'PAGE_END':10,#結束頁'TIME_GE':0,#每個下載間隔時間'POOL':False,#是否開啟線程池'CHECKID':True, #True 過濾已經下載過的視頻 False 不過濾'FILE_PATH':'F:/ceshi/',#下載目錄,【會自動創建文件夾】'TS_PATH':'F:/ceshi/download/',#緩存文件目錄,【會自動創建文件夾】'DB_URL':'localhost',#數據庫地址'DB_NAME':'douyu',#數據庫名稱''DB_TABLE':'douyu'#數據庫表
}#MongoDB初始化
client = pymongo.MongoClient(config['DB_URL'])
mango_db = client[config['DB_NAME']]#MongoDB存儲
def save_to_mango(result):if mango_db[config['DB_TABLE']].insert_one({'vid':result}):print('成功存儲到MangoDB')return Truereturn False
#MongoDB驗證重復
def check_to_mongo(vid):count = mango_db[config['DB_TABLE']].find({'vid':vid}).count()if count==0:return Falsereturn True#刪除文件
def del_file(page):if os.path.exists(page):# 刪除文件,可使用以下兩種方法。os.remove(page)# os.unlink(my_file)else:print('no such file:%s' % page)#循環列表刪除文件
def loop_del_file(arr):for item in arr:del_file(item)#請求器
def get_content_requests(url):headers = {}headers['user-agent']='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36'headers['cookie'] = 'dy_did=07f83a57d1d2e22942e0883200001501; acf_did=07f83a57d1d2e22942e0883200001501; Hm_lvt_e99aee90ec1b2106afe7ec3b199020a7=1556514266,1557050422,1557208315; acf_auth=; acf_auth_wl=; acf_uid=; acf_nickname=; acf_username=; acf_own_room=; acf_groupid=; acf_notification=; acf_phonestatus=; _dys_lastPageCode=page_video,page_video; Hm_lpvt_e99aee90ec1b2106afe7ec3b199020a7=1557209469; _dys_refer_action_code=click_author_video_cate2'try:req_content = requests.get(url,headers = headers)if req_content.status_code == 200:return req_contentprint('請求失敗:',url)return Noneexcept:print('請求失敗:', url)return None#把時間換算成秒
def str_to_int(time):try:time_array = time.split(':')time_int = (int(time_array[0])*60)+int(time_array[1])return time_intexcept:print('~~~~~計算視頻時間失敗~~~~~')return None#提取需要采集的數據
def get_list(html,type = 1):data = []try:list_json = json.loads(str(html))for om in list_json['data']['list']:gtime = str_to_int(om['video_str_duration'])if gtime > config['TIME_START'] and gtime < config['TIME_ENT']:if type == 2:data.append({'title': om['title'], 'vid': om['url'].split('show/')[1]})else:data.append({'title': om['title'], 'vid': om['hash_id']})return dataexcept:print('~~~~~數據提取失敗~~~~~')return None#解析playlist.m3u8
def get_ts_list(m3u8):data = []try:html_m3u8_json = json.loads(m3u8)m3u8_text = get_content_requests(html_m3u8_json['data']['video_url'])m3u8_vurl =html_m3u8_json['data']['video_url'].split('playlist.m3u8?')[0]if m3u8_text:get_text = re.findall(',\n(.*?).ts(.*?)\n#',m3u8_text.text,re.S)for item in get_text:data.append(m3u8_vurl+item[0]+'.ts'+item[1])return datareturn Noneexcept:print('~~~~~解析playlist.m3u8失敗~~~~~')return None# 殺死moviepy產生的特定進程
def killProcess():# 處理python程序在運行中出現的異常和錯誤try:# pids方法查看系統全部進程pids = psutil.pids()for pid in pids:# Process方法查看單個進程p = psutil.Process(pid)# print('pid-%s,pname-%s' % (pid, p.name()))# 進程名if p.name() == 'ffmpeg-win64-v4.1.exe':# 關閉任務 /f是強制執行,/im對應程序名cmd = 'taskkill /f /im ffmpeg-win64-v4.1.exe 2>nul 1>null'# python調用Shell腳本執行cmd命令os.system(cmd)except:pass#下載.ts文件
def download_ts(m3u8_list,name):try:if not os.path.exists(config['FILE_PATH']):os.makedirs(config['FILE_PATH'])if not os.path.exists(config['TS_PATH']):os.makedirs(config['TS_PATH'])if os.path.exists(config['FILE_PATH']+name+'.mp4'):name = name+'_'+str(int(time.time()))print('開始下載:',name)L = []R = []for p in m3u8_list:ts_find = get_content_requests(p)file_ts = '{0}{1}.ts'.format(config['TS_PATH'],md5(ts_find.content).hexdigest())with open(file_ts,'wb') as f:f.write(ts_find.content)R.append(file_ts)hebing = VideoFileClip(file_ts)L.append(hebing)killProcess()print('下載完成:',file_ts)mp4file = '{0}{1}.mp4'.format(config['FILE_PATH'],name)final_clip = concatenate_videoclips(L)final_clip.to_videofile(mp4file, fps=24, remove_temp=True)killProcess()loop_del_file(R)print('\n下載完成:',name)print('')return Trueexcept:print('~~~~~合成.ts文件失敗~~~~~')return None#下載視頻列表
def list_get_kong(list_json):for item in list_json:y = Trueif config['CHECKID']:if check_to_mongo(item['vid']):print('~~~~~檢測到重復項~~~~~')y = Falseif y:get_show_html = get_content_requests('https://vmobile.douyu.com/video/getInfo?vid=' + item['vid'])if get_show_html:m3u8_list = get_ts_list(get_show_html.text)if m3u8_list:download = download_ts(m3u8_list, item['title'])if download: save_to_mango(item['vid'])time.sleep(config['TIME_GE'])#控制器
def main(page):if config['TYPE']==1:print('~~~~~按用戶ID采集~~~~~')listurl = 'https://v.douyu.com/video/author/getAuthorVideoListByNew?up_id={0}&cate2_id=0&limit=30&page={1}'.format(config['UID'],page)get_list_html = get_content_requests(listurl)if get_list_html:list_json = get_list(get_list_html.text,1)if list_json:list_get_kong(list_json)else:print('~~~~~按列表ID采集~~~~~')listurl = 'https://v.douyu.com/video/video/listData?page={1}&cate2Id={0}&action=new'.format(config['CID'],page)get_list_html = get_content_requests(listurl)if get_list_html:list_json = get_list(get_list_html.text,2)if list_json:list_get_kong(list_json)#初始化
if __name__=='__main__':if config['POOL']:groups = [x for x in range(config['PAGE_START'],config['PAGE_END']+1)]pool = Pool()pool.map(main, groups)else:for item in range(config['PAGE_START'],config['PAGE_END']+1):main(item)print('~~~~~已經完成【所有操作】~~~~~')
總結:眾所周知,BiliBili是一個學習的網站!
總結
以上是生活随笔為你收集整理的练习:《斗鱼视频》m3u8流视频采集下载+思路+Python的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。