视频网站的接口解析

先把代码放上来

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,那它是如何生成的呢?

我们先打开一个无缓存的浏览器。

要先打开下方的抓包界面

视频网站的接口解析_第1张图片

输入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 在代码中我写了一个对比,获取到的数据如下:

视频网站的接口解析_第2张图片

很明显验证了上面的所述。

还有一个主角,session

在代码中,请求用到了session,这是必不可少的,因为保持状态的访问才能获取到重要参数,token

 

 

 

重要更新

从 https://log.mmstat.com/eg.js 获取的utid真的不可用吗?为什么?怎么使之变得可用?

请看分析:https://blog.csdn.net/s_kangkang_A/article/details/116754192

你可能感兴趣的:(爬虫实战,python,爬虫)