先把代码放上来
import base64
import hashlib
import json
import re
import time
import requests
import urllib3
urllib3.disable_warnings()
def get_data(url, utid):
"""
生成data参数
:param url: 视频地址
:return:
"""
# 从连接中获取vid
vid = url.split('id_')[-1].split('.')[0]
# 获取部分url用于生成加密参数
url_ = url.split('//')[-1].split('?')[0]
# base64 加密
emb = base64.b64encode(("809715843" + url_).encode('utf-8')).decode('utf-8')
data = {"biz_params": {"vid": vid},
"ad_params": {
"atm": "",
"aw": "w",
"bt": "pc",
"d": "0",
"dq": "auto",
"emb": emb,
"fu": "0",
"isvert": "0",
"needbf": "2",
"os": "win",
"osv": "10",
"p": "1",
"partnerid": "null",
"pver": "0.6.16",
"rst": "mp4",
"site": "1",
"sver": "1.1",
"vip": "0",
"vs": "1",
"wintype": "interior"
},
"steal_params": {
"ccode": "0502",
"client_ip": "192.168.1.1",
"client_ts": str(round(time.time() * 1000)),
"utid": utid,
"version": "0.6.16",
"ckey": "DIl58SLFxFNndSV1GFNnMQVYkx1PP5tKe1siZu/86PR1u/Wh1Ptd+WOZsHHWxysSfAOhNJpdVWsdVJNsfJ8Sxd8WKVvNfAS8aS8fAOzYARzPyPc3JvtnPHjTdKfESTdnuTW6ZPvk2pNDh4uFzotgdMEFkzQ5wZVXl2Pf1/Y6hLK0OnCNxBj3+nb0v72gZ6b0td+WOZsHHWxysSo/0y9D2K42SaB8Y/+aD2K42SaB8Y/+ahU+WOZsHcrxysooUeND"
}
}
data_ = dict()
# 将参数json化
for key, value in data.items():
data_[key] = json.dumps(value)
return json.dumps(data_)
def get_all_parameter(data_, token=''):
"""
获得所有参数
:param data_: data参数
:param token: token参数
:return:
"""
t = str(round(time.time() * 1000))
data = {
'jsv': '2.5.0',
'appKey': '24679788',
't': t,
'api': 'mtop.youku.play.ups.appinfo.get',
'v': '1.1',
'sign': get_sign(t, data_, token),
'timeout': '20000',
'YKPid': '20160317PLF000211',
'YKLoginRequest': 'true',
'AntiFlood': 'true',
'AntiCreep': 'true',
'type': 'jsonp',
'dataType': 'jsonp',
'callback': 'mtopjsonp1',
'data': data_
}
return data
def get_sign(t, data, token):
"""
:param t: 13位时间戳
:param data: 请求时的参数data
:param token: 从cookie中获得
:return: 加密后的sign
"""
appKey = '24679788'
sign = token + '&' + t + '&' + appKey + '&' + data
md5 = hashlib.md5()
md5.update(sign.encode('UTF-8'))
sign = md5.hexdigest()
return sign
def get_interface(url, utid, session, headers, proxies=None):
# 支持代理和无代理两种请求
"""
获取视频借口内容
:param url: 视频连接
:return:
"""
data_ = get_data(url, utid)
data = get_all_parameter(data_)
url = 'https://acs.youku.com/h5/mtop.youku.play.ups.appinfo.get/1.1/'
if not proxies:
response = session.get(url, params=data, verify=False, headers=headers)
else:
response = session.get(url, params=data, verify=False, headers=headers, proxies=proxies)
# print(response.text)
if '令牌为空' in response.text:
# 从cookie中获取token
token = response.cookies['_m_h5_tk'].split('_')[0]
print("获取token", token)
data = get_all_parameter(data_, token=token)
data['callback'] = 'mtopjsonp1'
# 请求接口
if not proxies:
response = session.get(url, params=data, verify=False, headers=headers)
else:
response = session.get(url, params=data, verify=False, headers=headers, proxies=proxies)
return response.text
def parse(url, utid):
result = ''
times = 1
headers = {
'Host': "acs.youku.com",
'Connection': "keep-alive",
'Pragma': "no-cache",
'Cache-Control': "no-cache",
'User-Agent': "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/72.0.3626.119 Safari/537.36",
'Accept': "*/*",
'Referer': url,
'Accept-Encoding': "gzip, deflate, br",
'Accept-Language': "zh-CN,zh;q=0.9",
'cache-control': "no-cache",
}
while times:
session = requests.Session()
proxies = {}
time.sleep(5)
try:
result = get_interface(url, utid, session, headers)
except:
result = ''
if len(result) > 10000:
break
else:
times -= 1
print(result)
def get_utid():
t = int(time.time() * 1000)
url = 'https://log.mmstat.com/eg.js?t={}'.format(t)
headers = {
'authority': 'log.mmstat.com',
'method': 'GET',
'path': '/eg.js',
'scheme': 'https',
'accept': '*/*',
'accept-encoding': 'gzip, deflate, br',
'accept-language': 'zh-CN,zh;q=0.9',
'referer': 'https://v.youku.com/',
'sec-fetch-dest': 'script',
'sec-fetch-mode': 'no-cors',
'sec-fetch-site': 'cross-site',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36'
}
resp = requests.get(url, headers=headers)
print(resp.text)
utid = re.search('Etag="(.*?)"', resp.text, re.S).group(1).strip()
return utid
if __name__ == '__main__':
utid = get_utid()
print('访问链接获取', utid)
parse('https://v.youku.com/v_show/id_XNDA4NTM4NTQwMA==.html', utid)
print('使用selenium获取')
utid = 'dAPfGLzvZ2ICAbcOhzje/chE'
parse('https://v.youku.com/v_show/id_XNDA4NTM4NTQwMA==.html', utid)
一些参数的加解密就不多做介绍了,在一些博主的文章里有很详细的逆向思路和解析。
这里主要说一下,如果有大量的类似访问,导致接口返回异常的分析。
在程序入口,parse方法传入两个参数,一个是url,没啥好说的,另一个就是要分析的主角之一:utid。
utid相当于该网站给每个用户生成的唯一id,那它是如何生成的呢?
我们先打开一个无缓存的浏览器。
要先打开下方的抓包界面
输入URL回车,看看发起了哪些请求:
如上图,有个https://log.mmstat.com/eg.js,右侧被标记的,就是代码里的utid。
为什么要把这个utid单独拿出来说呢。
优酷对于该接口的访问有两个判断
1,某个ip高频次的访问,该接口会报异常,返回无用的数据
2,某个utid高频次的访问,该接口会报异常,返回用户账号异常
那么针对第一种情况,我们可以使用代理绕过。
第二种情况,就需要更换utid了。
上面介绍说,使用https://log.mmstat.com/eg.js去获取utid,可行吗?
经过试验,是不可行的,对https://log.mmstat.com/eg.js进行请求,无论如何构造请求头,或者使用代理获取utid,都能正常获取到utid。
但是,获取到的utid确是不可用的,使用上述方法获取到的utid去访问接口,依旧无法获取到数据。
那,如果真的对该目标接口需要大量的访问,而没有utid,该怎么办呢。
简单,上面也说了,使用无缓存的浏览器打开页面就会产生一个utid,这个utid是可用的。那我们只需要一直打开无缓存的浏览器,获取utid,把utid保存下来,当我们有足够的utid,理论上就可以达到我们的目的。
可是,当某个ip高频次的打开浏览器访问链接,那么浏览器页面也会弹出警告,提示登录。
于是,我们想到了代理。
思路就有了,使用隧道代理+selenium+chromedriver,执行打开模拟器访问该网站页面,utid就在cookie里,获取cookie里的utis存库,在使用的时候随机取,就可以达到我们的目的。
seleniu+隧道代理+chromedriver的使用方法:https://blog.csdn.net/s_kangkang_A/article/details/115323462 在代码中我写了一个对比,获取到的数据如下:
很明显验证了上面的所述。
还有一个主角,session
在代码中,请求用到了session,这是必不可少的,因为保持状态的访问才能获取到重要参数,token
重要更新
从 https://log.mmstat.com/eg.js 获取的utid真的不可用吗?为什么?怎么使之变得可用?
请看分析:https://blog.csdn.net/s_kangkang_A/article/details/116754192