篇幅有限
完整內(nèi)容及源碼關(guān)注公眾號:ReverseCode,發(fā)送 沖
需求
最近需要抓取一些淘寶商品的首圖視頻,比如https://item.taobao.com/item.htm?spm=a230r.1.14.31.7ebfcec2qmczgd&id=641116554739&ns=1&abbucket=2#detail,該頁面首圖視頻頁面元素是blob協(xié)議加密,該協(xié)議返回大多是m3u8格式的視頻,并被切分為多個ts格式的小段視頻集合。

分析
通過chrome抓包果然找到了m3u8視頻請求,第一個請求返回ts文件列表,緊接著發(fā)起視頻請求返回ts視頻數(shù)據(jù)。

# EXTM3U:.m3u8文件的格式定義
# EXT-X-KEY: 密鑰的信息
# METHOD: 加密的方法,這里采用的是AES-128的加密方式
# URI: 密鑰的地址,需要獲取訪問得到密鑰的信息
# IV: 偏移量,AES加密的方法,通過這個密鑰就可以解密,獲取正確的視頻信息
數(shù)據(jù)來源找到了,那么緊跟著就是找到這些鏈接的組成字段,如首個鏈接https://tbm-auth.alicdn.com/e99361edd833010b/1Ptetzs7wLumqr8DVXj/IZAAx7ivPbWWLLDYpm0_275076925941___hd.m3u8?auth_key=1619353994-0-0-7eac2e2d00d26717d7aad9746575f99f中大部分url參數(shù)都是加密串,通過搜索其中的1Ptetzs7wLumqr8DVXj,找到了多條符合條件的請求。

第一條請求中返回的數(shù)據(jù)中有兩個參數(shù)的video_url,分別是hlsResources和mp4Resources,返回了m3u8和mp4格式,好家伙,這樣省去了合并m3u8個流程,直接拿mp4格式的視頻即可。
逆向
拿到返回video_url的請求的參數(shù),通過逐條過濾參數(shù)發(fā)現(xiàn),最終生效的參數(shù)只有四個,分別是appKey,t,sign,data,每次請求都有失效時間。

其中appKey固定為12574478,t為精確到毫秒的時間戳,sign是今天的逆向主角參數(shù),data動態(tài)內(nèi)容為'{"videoId": "%s","from":"detail"}' % "301079547561",其中301079547561作為videoId在頁面請求時直接返回在頁面js中。
sign
無痕瀏覽器清空頁面緩存,搜索sign,從眾多頁面中找到可能出現(xiàn)的位置,sign就是j,而j = h(d.token + "&" + i + "&" + g + "&" + c.data),其中d.token是加密字符串,i為時間戳,g為固定值12574478,c.data為{"videoId":"275076925941","from":"detail"}

在控制臺中調(diào)用h函數(shù)返回32位字符串,猜測是md5加密,就不扣h函數(shù)的js了。

接下來就是分析這些參數(shù)中唯一變的參數(shù)d.token的來源。
d.token
第一次斷點(diǎn)時d.token為undefined。
放開斷點(diǎn)后搜索d.token的值0027f0b395e6356158d06d22da238855,第一次出現(xiàn)在了返回video_url的請求返回時set-cookie中,作為Response Cookie返回了兩個cookie,一個是_m_h5_tk,一個是_m_h5_tk_enc,_分割的前面一段就是d.token的值。


第二次進(jìn)入斷點(diǎn)時d.token=0027f0b395e6356158d06d22da238855,放開斷點(diǎn)后搜索0027f0b395e6356158d06d22da238855出現(xiàn)在了同一個請求的Request Cookie中。

邏輯梳理
大概思路清晰了,對同一個請求多次訪問,第一次返回cookie作為第二次請求的cookie,cookie中的_m_h5_tk_enc通過_分割的前半段字符串作為d.token,根據(jù)d.token + "&" + i + "&" + g + "&" + c.data進(jìn)行md5得到sign,請求時加上兩個cookie,完成video_url的請求,從而實(shí)現(xiàn)淘寶商品首圖的視頻抓取。
爬蟲
APPKEY = '12574478'
DATA = '{"videoId": "%s","from":"detail"}' % "301079547561"
URL = 'https://h5api.m.taobao.com/h5/mtop.taobao.cloudvideo.video.queryforh5/1.0/'
params = {'jsv': '2.4.11', 'appKey': APPKEY, 't': int(time.time() * 1000),
'sign': 'FAKE_SIGN_WITH_ANYTHING', 'api': 'mtop.wdetail.getItemDescx', 'callback': 'mtopjsonp1','v': '4.9',
'type': 'jsonp', 'dataType': 'jsonp',
'data': DATA}
headers = {
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 9_3_4 like Mac OS X) AppleWebKit/601.1.46 ' + \
'(KHTML, like Gecko) Version/9.0 Mobile/13G35 Safari/601.1',
}
images = []
# get token in first request
r1 = requests.get(URL, params=params, headers=headers)
token_with_time = r1.cookies.get('_m_h5_tk')
token = token_with_time.split('_')[0]
enc_token = r1.cookies.get('_m_h5_tk_enc')
# get results in second request
t2 = str(int(time.time() * 1000))
c = '&'.join([token, t2, APPKEY, DATA])
m = hashlib.md5()
m.update(c.encode('utf-8'))
params.update({'t': t2, 'sign': m.hexdigest()})
cookies = {'_m_h5_tk': token_with_time, '_m_h5_tk_enc': enc_token}
r2 = requests.get(URL, params=params, headers=headers, cookies=cookies)
results=json.loads(re.match(r' mtopjsonp1\((.*?)\)', r2.text).group(1))
video_url = jsonpath(results, '$..video_url')[1]
print(video_url)
完整源碼請關(guān)注微信公眾號:ReverseCode,回復(fù):JS逆向
本文由博客群發(fā)一文多發(fā)等運(yùn)營工具平臺 OpenWrite 發(fā)布