python3爬蟲(4)各種網站視頻下載方法
原創H-KING 最后發布於2019-01-09 11:06:23 閱讀數 13608 收藏
展開
理論上來講只要是網上(瀏覽器)能看到圖片,音頻,視頻,都能夠下載下來,然而實際操作的時候也是有一定難度和技術的,這篇文章主要講述各個網站視頻資源如何下載。
B站視頻
頁面鏈接:
https://www.bilibili.com/bangumi/play/ep118490?from=search&seid=7943855106424547918
首先我們用萬能下載器“you-get”測試一下,下載成功,60多兆的視頻文件,打開可以觀看。我們在瀏覽器輸入該網址,F12打開網絡監測,回車進入該網頁,點擊播放視頻,觀看一分鍾左右,為什么要觀看一分鍾,主要是看視頻是一個鏈接傳輸,還是不停的更換視頻鏈接,還有就是1分鍾會有挺大的視頻緩沖數據,明顯比其他網絡資源大,方便咱們分析。暫停視頻,停止抓包,看到抓包欄信息如下:
我們重點注意,Size,Time,Watefall欄,因為視頻鏈接要返回數據,大小和花費時間明顯比其他的資源大的多,滾動下看看所有信息,找到一個懷疑目標
看到返回數據29.4兆,這應該就是視頻資源,先別急着分析這個鏈接,我們再看看有無其他懷疑目標,滾動一邊發現僅此一個。選中該鏈接,看看詳細信息
我們看到該鏈接是個get請求和一個關鍵字.flv,這個應該就是視頻連接地址。
全連接如下:
https://upos-hz-mirrorkodo.acgvideo.com/upgcxcode/49/50/29645049/29645049-1-32.flv?e=ig8euxZM2rNcNbKHhwdVhoMMnWdVhwdEto8g5X10ugNcXBlqNxHxNEVE5XREto8KqJZHUa6m5J0SqE85tZvEuENvNC8xNEVE9EKE9IMvXBvE2ENvNCImNEVEK9GVqJIwqa80WXIekXRE9IMvXBvEuENvNCImNEVEua6m2jIxux0CkF6s2JZv5x0DQJZY2F8SkXKE9IB5QK==&deadline=1547005191&dynamic=1&gen=playurl&oi=22475807&os=kodo&platform=pc&rate=490000&trid=52c1879aeb584205af339c5624957e09&uipk=5&uipv=5&um_deadline=1547005191&um_sign=6049af7768edf6ebf7819f897bbda605&upsig=0b7a5fa168a1d70655e645783d7184d3
這個鏈接結構式視頻鏈接+參數的形式,’?’號后面都是參數,
視頻鏈接如下:
https://upos-hz-mirrorkodo.acgvideo.com/upgcxcode/49/50/29645049/29645049-1-32.flv
把這兩個地址分別輸入瀏覽器地址欄試試,發現都沒什么反應,再用瀏覽器自帶下載工具試試(當然也可以用其他下載工具試,如迅雷),
發現全連接那個下載失敗,視頻連接那個下載成功,下載到了60多兆的視頻文件,可以播放,這樣這個下載地址就算找到了,我們再試試上次用python寫的簡單7行代碼試試,
代碼鏈接:
python3爬蟲(2)下載有固定鏈接的視頻
發現瞬間結束,調試一下發現下載失敗了,錯誤碼:459。如下圖:
這個不應該啊,估計是http請求頭出了問題,我們抓下瀏覽器是什么頭,發現瀏覽器自帶抓包工具無法抓下載的包頭,只能夠抓瀏覽網頁的頭,用抓包工具Fiddler抓吧。
我們把這個包頭寫到請求里面,發現下載成功了。完整代碼如下:
import requests
hd = {
'Connection':'keep-alive',
'Host':'upos-hz-mirrorkodo.acgvideo.com:443',
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 SE 2.X MetaSr 1.0'
}
print("開始下載")
url = 'https://upos-hz-mirrorkodo.acgvideo.com/upgcxcode/49/50/29645049/29645049-1-32.flv'
r = requests.get(url, headers=hd, stream=True)
with open('test.mp4', "wb") as mp4:
for chunk in r.iter_content(chunk_size=1024 * 1024):
if chunk:
mp4.write(chunk)
print("下載結束")
2.優酷
頁面鏈接:
http://v.youku.com/v_show/id_XMTQ2NzQyMjY1Ng.html
還是老規矩,
首先我們用萬能下載器“you-get”測試一下,下載成功,13多兆的視頻文件,打開可以觀看。我們在瀏覽器輸入該網址,F12打開網絡監測,回車進入該網頁,點擊播放視頻,觀看一分鍾左右。暫停視頻,停止抓包。觀察抓包欄目,這次和上面的例子不一樣,這次找了半天也沒找到1個超過1兆的數據包,推測是分開傳輸的,經過進一步的尋找發現m3u8鏈接,如下圖:
經過前面的學習我們知道這是m3u8+ts傳輸視頻流的,具體技術請看:
python3爬蟲(3)下載流媒體m3u8
我們看下這個具體細節,手工下載一下m3u8文件,可以下載,和網頁端比較一下是一樣的,手工下載ts列表里面的ts文件,也是可以下載的,播放一下看看,都沒啥問題。這些步驟可以用下面的代碼實現,前提是知道m3u8下載地址(包括地址里面的參數)
import os
import requests
"""
下載M3U8文件里的所有片段
"""
def download(url):
download_path = os.getcwd() + "\download"
if not os.path.exists(download_path):
os.mkdir(download_path)
all_content = requests.get(url).text # 獲取M3U8的文件內容
file_line = all_content.split("\r\n") # 讀取文件里的每一行
# 通過判斷文件頭來確定是否是M3U8文件
if file_line[0] != "#EXTM3U":
raise BaseException(u"非M3U8的鏈接")
else:
unknow = True # 用來判斷是否找到了下載的地址
for index, line in enumerate(file_line):
if "EXTINF" in line:
unknow = False
# 拼出ts片段的URL
pd_url = file_line[index + 1]
res = requests.get(pd_url)
# c_fule_name = str(index)+ '.ts'
c_fule_name = "%(index)02d" % {'index': index} + '.ts'
with open(download_path + "\\" + c_fule_name, 'ab') as f:
f.write(res.content)
f.flush()
if unknow:
raise BaseException("未找到對應的下載鏈接")
else:
print("下載完成")
#合並的時候名字要有規律,從前往后排
def merge_file(path):
os.chdir(path)
os.system("copy /b * new.mp4")
if __name__ == '__main__':
download("http://pl-ali.youku.com/playlist/m3u8?vid=XMTQ2NzQyMjY1Ng&type=hd2&ups_client_netip=0156f41f&utid=cKsgFHBPZVECAXUjhXp%2Bu8Ip&ccode=0502&psid=244b3690aa7b9cd1c11c2f6c8ae6582b&duration=90&expire=18000&drm_type=1&drm_device=7&ups_ts=1547012701&onOff=0&encr=0&ups_key=9a7e324bb33543281964c43caa15dc80")
merge_file(os.getcwd() + "\download")
我們這個時候就考慮能否僅根據網頁地址全自動下載呢,畢竟you-get可以做到全自動,我們來看看這個m3u8地址:
http://pl-ali.youku.com/playlist/m3u8?vid=XMTQ2NzQyMjY1Ng&type=mp4&ups_client_netip=0156f41f&utid=cKsgFHBPZVECAXUjhXp%2Bu8Ip&ccode=0502&psid=db59c8bd03f9e26f6b21b17bccf8f1c9&duration=90&expire=18000&drm_type=1&drm_device=7&ups_ts=1547003676&onOff=0&encr=0&ups_key=49fa2661f64619e0e57d22611df8e5b7
地址是固定的,參數才是關鍵,參數如下:
vid:XMTQ2NzQyMjY1Ng
type:mp4
ups_client_netip:0156f41f
utid:cKsgFHBPZVECAXUjhXp+u8Ip
ccode:0502
psid:db59c8bd03f9e26f6b21b17bccf8f1c9
duration:90
expire:18000
drm_type:1
drm_device:7
ups_ts:1547003676
onOff:0
encr:0
ups_key:49fa2661f64619e0e57d22611df8e5b7
這些參數我們猜測,應該是獲取這個m3u8之前向服務器獲取的,應該是以json方式返回來的,在抓包欄目里面我們找找,緊挨着這個m3u8向上找,找到了1個,
幾個ups屬性找到了,沒有ups_key,這個估計可能是js算出來的碼,不是從服務器獲取到的。Vid屬性可以從網址里面提取出來,type是清晰度,mp4:標清,hd:高清,hd2:超清,(我們切換清晰度,觀察m3u8地址變化總結出來的),utid是個固定值,我們換個視頻還是這個碼,ccode, drm_type, drm_device, onOff, encr,也一樣固定值;duration進過總結就是視頻時長(秒),按道理來講應該是服務器放回來的,簡單找了一下沒找到。先記錄到這里吧。
================以下為2019/1/10 13:20 更新=======================
接着上次聊。
這個m3u8的參數:type:mp4,ups_key:49fa2661f64619e0e57d22611df8e5b7,都是不容易找到。
上次我們找到ups那個json里面當時也看了看其他節點,收獲還真不小,如下圖:
注意這個stream分支,打開他的分支0,
我們看到了m3u8地址,這就太好了,再往下看看,
有個cdn_url,從這個地址我們可以看出這就是一個視頻鏈接。試着下載一下看看,就是我們在網頁里面看到的視頻。剛不是看到stream有4個子項嗎,分別打開看看,每個子項里面都有m3u8_url和cdn_url,試了一下每個都可以下載視頻,一種height屬性:288,288,378,622。看下界面有,標清,高清,超清,這些應該是互相對應的。亦即只要獲得了這個json其實就獲取視頻源頭。我們看下這個請求的地址:
http://acs.youku.com/h5/mtop.youku.play.ups.appinfo.get/1.1/?jsv=2.5.0&appKey=24679788&t=1547091011531&sign=be34df75c65e2e871e720039252ee056&api=mtop.youku.play.ups.appinfo.get&v=1.1&timeout=20000&YKPid=20160317PLF000211&YKLoginRequest=true&AntiFlood=true&AntiCreep=true&type=jsonp&dataType=jsonp&callback=mtopjsonp1&data=%7B%22steal_params%22%3A%22%7B%5C%22ccode%5C%22%3A%5C%220502%5C%22%2C%5C%22client_ip%5C%22%3A%5C%22192.168.1.1%5C%22%2C%5C%22utid%5C%22%3A%5C%22cKsgFHBPZVECAXUjhXp%2Bu8Ip%5C%22%2C%5C%22client_ts%5C%22%3A1547091011%2C%5C%22version%5C%22%3A%5C%220.6.8%5C%22%2C%5C%22ckey%5C%22%3A%5C%22DIl58SLFxFNndSV1GFNnMQVYkx1PP5tKe1siZu%2F86PR1u%2FWh1Ptd%2BWOZsHHWxysSfAOhNJpdVWsdVJNsfJ8Sxd8WKVvNfAS8aS8fAOzYARzPyPc3JvtnPHjTdKfESTdnuTW6ZPvk2pNDh4uFzotgdMEFkzQ5wZVXl2Pf1%2FY6hLK0OnCNxBj3%2Bnb0v72gZ6b0td%2BWOZsHHWxysSo%2F0y9D2K42SaB8Y%2F%2BaD2K42SaB8Y%2F%2BahU%2BWOZsHcrxysooUeND%5C%22%7D%22%2C%22biz_params%22%3A%22%7B%5C%22vid%5C%22%3A%5C%22XMTQ2NzQyMjY1Ng%3D%3D%5C%22%7D%22%2C%22ad_params%22%3A%22%7B%5C%22vs%5C%22%3A%5C%221.0%5C%22%2C%5C%22pver%5C%22%3A%5C%220.6.8%5C%22%2C%5C%22sver%5C%22%3A%5C%221.0%5C%22%2C%5C%22site%5C%22%3A1%2C%5C%22aw%5C%22%3A%5C%22w%5C%22%2C%5C%22fu%5C%22%3A0%2C%5C%22d%5C%22%3A%5C%220%5C%22%2C%5C%22bt%5C%22%3A%5C%22pc%5C%22%2C%5C%22os%5C%22%3A%5C%22win%5C%22%2C%5C%22osv%5C%22%3A%5C%227%5C%22%2C%5C%22dq%5C%22%3A%5C%22auto%5C%22%2C%5C%22atm%5C%22%3A%5C%22%5C%22%2C%5C%22partnerid%5C%22%3A%5C%22null%5C%22%2C%5C%22wintype%5C%22%3A%5C%22interior%5C%22%2C%5C%22isvert%5C%22%3A0%2C%5C%22vip%5C%22%3A0%2C%5C%22emb%5C%22%3A%5C%22AjM2Njg1NTY2NAJ2LnlvdWt1LmNvbQIvdl9zaG93L2lkX1hNVFEyTnpReU1qWTFOZy5odG1s%5C%22%2C%5C%22p%5C%22%3A1%2C%5C%22rst%5C%22%3A%5C%22mp4%5C%22%2C%5C%22needbf%5C%22%3A2%7D%22%7D
看看他的參數列表:
jsv:2.5.0
appKey:24679788
t:1547091011531
sign:be34df75c65e2e871e720039252ee056
api:mtop.youku.play.ups.appinfo.get
v:1.1
timeout:20000
YKPid:20160317PLF000211
YKLoginRequest:true
AntiFlood:true
AntiCreep:true
type:jsonp
dataType:jsonp
callback:mtopjsonp1
data:{"steal_params":"{\"ccode\":\"0502\",\"client_ip\":\"192.168.1.1\",\"utid\":\"cKsgFHBPZVECAXUjhXp+u8Ip\",\"client_ts\":1547091011,\"version\":\"0.6.8\",\"ckey\":\"DIl58SLFxFNndSV1GFNnMQVYkx1PP5tKe1siZu/86PR1u/Wh1Ptd+WOZsHHWxysSfAOhNJpdVWsdVJNsfJ8Sxd8WKVvNfAS8aS8fAOzYARzPyPc3JvtnPHjTdKfESTdnuTW6ZPvk2pNDh4uFzotgdMEFkzQ5wZVXl2Pf1/Y6hLK0OnCNxBj3+nb0v72gZ6b0td+WOZsHHWxysSo/0y9D2K42SaB8Y/+aD2K42SaB8Y/+ahU+WOZsHcrxysooUeND\"}","biz_params":"{\"vid\":\"XMTQ2NzQyMjY1Ng==\"}","ad_params":"{\"vs\":\"1.0\",\"pver\":\"0.6.8\",\"sver\":\"1.0\",\"site\":1,\"aw\":\"w\",\"fu\":0,\"d\":\"0\",\"bt\":\"pc\",\"os\":\"win\",\"osv\":\"7\",\"dq\":\"auto\",\"atm\":\"\",\"partnerid\":\"null\",\"wintype\":\"interior\",\"isvert\":0,\"vip\":0,\"emb\":\"AjM2Njg1NTY2NAJ2LnlvdWt1LmNvbQIvdl9zaG93L2lkX1hNVFEyTnpReU1qWTFOZy5odG1s\",\"p\":1,\"rst\":\"mp4\",\"needbf\":2}"}
這條路走下去主要有3個難點:
難點一:
sign:cda7f7031b84db2b741d31ac4a8bec89
難點二:
ckey:115#133lN51O1TaT1YgQMCfR1Csou61hIeAacuvuZj .............
難點三:
emb:AjM2Njg1NTY2NAJ2LnlvdWt1LmNvbQIvdl9zaG93L2lkX1hNVFEyTnpReU1qWTFOZy5odG1s
這3個值不知道哪里去弄,當然按道理來講肯定有點方去弄,只是難度有點大。
剛剛看到希望,瞬間蔫了,因為我們看了一下其他鏈接,返回json的不是很多,json里面有這些字段值是沒有,這就有點棘手了。
回頭想想,不是you-get能夠獲取到這個視頻嗎,我們看看他是怎么獲得到的。打開抓包工具Fiddler,這個是名氣最大的,簡單看下,7個請求,其中有6個是python進程的請求,1個是瀏覽器sogouexplore.exe請求。
看看這個6個python請求。第一個是log.mmstat.com,第二個是https請求,后邊4次好像是重復的動作,一模一樣,其實是兩次請求。點開https請求,發現沒有什么有價值的信息,好像是Fiddler解析https需要配置什么,上次配置之后又導致瀏覽器不能正常訪問網頁。換個抓包工具吧,我下載了一個HttpAnalyzer。效果還不錯,先看下抓包結果,
選中那個https請求,看下是能夠完全解析的。返回數據支持json,data,hex,preview四種展示方式
我們看下,這個返回結果其實和上面咱們那個比較長的請求結果是一樣的。You-get獲取到這個json之后解析了里面的cdn_url選擇最清晰的下載了下來。看下他傳輸的時候有哪些參數,看了下這個參數少了很多,鑒於這個請求之前沒有和優酷服務器通訊,我們猜測這些值都是定值,有仔細看了下client_ts字段值很想時間戳,確定了一下就是時間戳,
我們簡短的寫代碼,模擬一下這個請求,果然成功了,返回了想要的json。這個請求這么好,瀏覽器里面是不是也有這個請求而我們沒注意到呢?我們搜索一下ups.youku.com。看看瀏覽器有沒發出類似的請求,
發現0個請求,也就是沒有發出向這個網站的請求,那you-get又是怎么知道的呢,估計是以前版本,請求是向ups.youku.com發出的,現在更新了。既然更新了為什么老的還能用?因為整個優酷太大,前台頁面有很多,更新前台頁面工作量太大,所以現在服務器是新老本兼容的,只不過以后的頁面都會以更新之后的出現。
第一個鏈接http://log.mmstat.com/eg.js是干什么的,一開始沒注意,因為根據現有內容已經可以獲得我們想要的內容,后來測試的路中出現問題:客戶端無權播放,
You-get也會出現類似問題,只不過概率很低,我們分一下原因,發現you-get發送請求中utid的值是變動,這個值是哪里來的?本地有個列表循環着來,不太合適吧,我們看下第一個鏈接http://log.mmstat.com/eg.js 正好發現了這個值。我們改下自己代碼,每次請求之先獲取一下這個utid,發現現在就很流暢了。
注意:
通過這次實戰我們也了解到了,不會存在一種給個頁面就能下載頁面里面的視頻的通殺方案,you-get之所以能下載大部分主流網站是因為他為每個網站都做了適配,亦即每個網站下載視頻原理他都已經研究了。
源碼(想要下載你的視頻,將param里面的vid值改成你的地址欄的值就行了)
import requests
import time
import json
def downfile(filename, url):
r = requests.get( url, stream=True)
with open(filename, "wb") as mp4:
for chunk in r.iter_content(chunk_size=1024 * 1024):
if chunk:
mp4.write(chunk)
# 獲取證書
r = requests.get('http://log.mmstat.com/eg.js')
start = len('window.goldlog=(window.goldlog||{});goldlog.Etag=\"')
end = len('window.goldlog=(window.goldlog||{});goldlog.Etag=\"66C9FJlZrDYCAQFQU4AhkzO0')
sert = r.text[start:end]
param = {
'vid':'XMTQ2NzQyMjY1Ng',
'ccode':'0590',
'client_ip':'192.168.1.1',
'utid':'2py9FNCXjUcCAQFQU4APrwPf',
'client_ts':'1547028409',
'ckey':'DIl58SLFxFNndSV1GFNnMQVYkx1PP5tKe1siZu/86PR1u/Wh1Ptd+WOZsHHWxysSfAOhNJpdVWsdVJNsfJ8Sxd8WKVvNfAS8aS8fAOzYARzPyPc3JvtnPHjTdKfESTdnuTW6ZPvk2pNDh4uFzotgdMEFkzQ5wZVXl2Pf1/Y6hLK0OnCNxBj3+nb0v72gZ6b0td+WOZsHHWxysSo/0y9D2K42SaB8Y/+aD2K42SaB8Y/+ahU+WOZsHcrxysooUeND'
}
headers = {
'Accept-Encoding': 'identity',
'Host': 'ups.youku.com',
'Referer': 'http://v.youku.com',
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36',
'Connection': 'close'
}
now = time.time()
nowint = int(now)
nowstr = str(nowint)
param['client_ts'] = nowstr
param['utid'] = sert
url = 'http://v.youku.com/ups/get.json?'
r = requests.get(url, headers=headers, params=param)
r_j = json.loads(r.text)
streams = r_j.get('data').get('stream')
for stream in streams:
m3u8_url = stream.get('m3u8_url')
cdn_url = stream.get('segs')[0].get('cdn_url')
print('m3u8_url:' + m3u8_url)
print('cdn_url:' + cdn_url)
filename = cdn_url[len('http://ykugc.cp31.ott.cibntv.net/65720C705E33C7182E34B311F/'):len('http://ykugc.cp31.ott.cibntv.net/65720C705E33C7182E34B311F/030020010056B748030687093F3C3CF7C1644A-3E5C-1E70-1F07-0C1AD8DC39BB.mp4')]
downfile(filename,cdn_url)
print('end')
最后,現在CSDN寫文章支持word拷貝帶圖片,贊一個,記得一年前寫文章要單獨上傳圖片,麻煩死,現在好了。。
————————————————
版權聲明:本文為CSDN博主「H-KING」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/liujiayu2/article/details/86137139