更多>>关于我们

西安鲲之鹏网络信息技术有限公司从2010年开始专注于Web(网站)数据抓取领域。致力于为广大中国客户提供准确、快捷的数据采集相关服务。我们采用分布式系统架构,日采集网页数千万。我们拥有海量稳定高匿HTTP代理IP地址池,可以有效获取互联网任何公开可见信息。

您只需告诉我们您想抓取的网站是什么,您感兴趣的字段有哪些,你需要的数据是哪种格式,我们将为您做所有的工作,最后把数据(或程序)交付给你。

数据的格式可以是CSV、JSON、XML、ACCESS、SQLITE、MSSQL、MYSQL等等。

更多>>官方微博

西安鲲之鹏
陕西 西安

加关注

  • 【经验分享】未解锁BL的手机进9008模式(Mi6X为例)

    对于未解锁BL的手机,需要拆机,通过短接特定触点的方式进入9008模式。
    以小米Mi6X为例:
    第一步,拧掉充电口旁边的两颗螺丝。
    第二步,扣开后盖,可能不太好扣,可以借助美工刀在边缘撬一下。拧掉保护条上的3个螺丝。
    第三步,拔掉电池排线。看图,记着两个短接触点的位置。
    第四步,用镊子短接两个触点,同时插入TypeC线,2秒左右设备管理器"端口COM"里会出现9008接口,此时松开镊子。
    发布时间:2024-11-27 10:13:20
  • 【经验分享】已解锁BL的手机进9008模式

    高通9008模式全称"Qualcomm HS-USB QDLoader 9008",它相对于recovery、fastboot和Android系统是独立的。即深刷模式,也叫EDL,号称"救砖神奇"。

    对于已解锁BL的手机,进入9008相对比较简单,以小米Mi6X为例:
    1. 先确定手机是否解锁BL了。已解锁BL的手机,刚开机的时候会有"Unlocked"字样,如附图1所示。
    2. 长按“音量减键 + 开机键”进入fastboot。
    3. 执行fastboot oem edl,即可进入9008模式,进入成功后设备管理器COM端口里可以看到"Qualcomm HS-USB QDLoader 9008"。如附图2、3所示。
    发布时间:2024-11-26 12:53:03
  • 【经验分享】com.android.org.conscrypt.TrustManagerImpl证书固定检测绕过示例

    某APP使用通用的sslunpinning脚本后仍然抓不到包:
    (1)分析logcat日志,发现com.android.org.conscrypt.TrustManagerImpl类相关代码抛出java.security.cert.CertificateException异常,如图1所示。
    (2)hook 类com.android.org.conscrypt.TrustManagerImpl的checkTrusted和checkServerTrusted方法,返回空列表,成功抓到包。

    日志线索寻找关键词:CertificateException、CertificateExpiredExceptio、SSLHandshakeException
    发布时间:2024-10-24 15:36:45
  • 【经验分享】如何获取安卓手机上已安装APP的安装包(.apk)文件?

    1. 先查看已安装APP列表,确定对应APP的包名。
    adb shell pm list packages
    2. 假设包名为org.gushiwen.gushiwen。再根据包名查看APP的详细信息:
    adb shell dumpsys package org.gushiwen.gushiwen
    返回信息中的path属性,以base.apk结尾的,即就是这个APP的安装文件,如附图1所示。另外返回的信息中还有当前APP的版本(versionName属性),如附图2所示。
    3. pull下来这个文件,就可以在其它设备上安装了。
    发布时间:2024-10-22 11:27:51
  • 【经验分享】Dell R720意外断电重启之后丢失硬盘(硬盘状态变为Foreign)问题解决?

    本来有10块盘,启动的时候显示只有9块Virtual Disk。“Ctrl + R”进入RAID设置,在“VD Mgmt”标签页下也只看到了9块Virtual Disk。在“PD Mgmt”标签页下看到是有10块物理盘,不过第5块状态变成“Foreign”了(如附图1所示)。

    解决方法:在“VD Mgmt”标签页下,焦点切换到"PERC H710 Mini"上按F2,然后"Foreign Config",再然后"Import",操作完成(要等待几秒)之后就能看到全部盘了,如图2所示。

    PS:用Ctrl + N快捷键切换菜单标签。
    发布时间:2024-10-18 16:35:44
  • 【经验分享】一个游戏闯关模式学习CSS Selector的网站"CSS Diner":https://flukeout.github.io/
    Python使用BeautifulSoup实现CSS Selector解析HTML文档的示例:

    import requests
    from bs4 import BeautifulSoup

    r = requests.get('http://www.site-digger.com/html/articles/')
    r.encoding = 'UTF-8'
    html = r.text
    soup = BeautifulSoup(html)
    for a in soup.select('ul[class="arclist"] li a'):
    print(a['href'], a.text)
    发布时间:2024-09-02 19:43:03
  • 【经验分享】qemu-system-x86运行tiny11
    (1) 安装qemu-system-x86,安装完成后无需重启。
    sudo apt-get update
    sudo apt-get install qemu qemu-utils qemu-system-x86
    (2) 创建硬盘。
    qemu-img create -f qcow2 tiny11.img 50G
    (3) 创建虚拟机。
    sudo qemu-system-x86_64 --enable-kvm -m 2G -smp 4 -boot order=dc -hda /home/qi/kvm/tiny11-1/tiny11.img -cdrom /home/qi/kvm/tiny11_23H2_x64.iso -vnc :1
    (4) vnc连接 "服务器ip:5901",完成系统安装过程。设置vnc密码的方法:https://qemu-project.gitlab.io/qemu/system/vnc-security.html#with-passwords
    (5) 映射主机端口给虚拟机,使用-redir参数。如下示例,将主机的TCP/UDP4001端口映射到虚拟机的4000端口。
    -redir tcp:4001::4000 -redir udp:4001::4000
    发布时间:2024-08-10 12:13:46
  • 【经验分享】Playwright过geo.captcha-delivery.com检测

    page.add_init_script('''Object.defineProperties(navigator, {webdriver:{get:()=>undefined}}); delete navigator.__proto__.webdriver;''') ​​​
    发布时间:2024-07-31 10:41:18
  • 【经验分享】scrcpy在网络质量欠佳环境下可以通过降低码率来提高流畅度
    e.g.
    scrcpy --bit-rate 1M --max-fps 5
    注意:在新版本中--bit-rate参数更名为--video-bit-rate ​​​
    发布时间:2024-07-03 10:11:54
  • 【经验分享】scrcpy在小米手机上鼠标不起作用问题的解决

    在“开发者选项”中需要打开"USB调试(安全设置) - 允许通过USB调试修改权限或模拟点击"。要打开这个选项,手机需要先登录小米账号,另外手机必须要插有SIM卡。 ​​​
    发布时间:2024-07-03 10:09:29
当前位置: 首页 > 技术文章 >
如何计算阿里系Ajax请求中的sign签名
发布时间:2021-10-18

       有过阿里系采集经验的开发者都应该知道,某宝(某猫)H5版、1688、某宝司法拍卖H5版等阿里系网站,在Ajax请求中都会有一个sign签名参数(如下图1、2、3所示),要是值不正确将无法获取到有效的数据(例如返回“非法请求”提示)。如果我们无法构造出有效的sign,就只能通过“模拟浏览器操作”的方式来绕过签名验证,再结合"mitmproxy动态抓包脚本"来提取返回数据,这种方案效率太低,而且很不灵活。本文将介绍如何计算这个sign值以及给出对应的Python实现,这样就能实现通过直接HTTP交互抓取数据。

天猫H5版Ajax请求中的sign参数

图1 天猫H5版Ajax请求中的sign参数

1688网站Ajax请求中的sign参数

图2 1688网站Ajax请求中的sign参数

淘宝司法拍卖H5版Ajax请求中的sign参数

图3 某宝司法拍卖H5版Ajax请求中的sign参数

要想在自己的程序中计算出有效的sign值,就得分析一下这个sign是怎么产生的,分析过程如下:

1. 通过关键词“sign”在mtop.js文件中定位到sign的出生点,如下图所示。

淘宝sign签名在mtop.js中的位置

2. 可以看出来,h()函数实现了sign的计算,所需的变量有token, i, g和data。

其中i为当前时间戳,g为固定值"12574478",data为请求的核心数据。难点在于token是如何产生的?

3. 通过js插桩发现:

(1)token(上述代码中的d.token)的值和请求头Cookie中的_m_h5_tk的第一部分是一致的。那么只要分析出来_m_h5_tk是如何产生的就能获取到token了。

(2)h()函数的算法如下。过程比较复杂,直接用Python还原难度太大,后面我们将使用Python执行JS代码的思路来实现。

function h(a) {
    function b(a, b) {
        return a << b | a >>> 32 - b
    }
    function c(a, b) {
        var c, d, e, f, g;
        return e = 2147483648 & a,
        f = 2147483648 & b,
        c = 1073741824 & a,
        d = 1073741824 & b,
        g = (1073741823 & a) + (1073741823 & b),
        c & d ? 2147483648 ^ g ^ e ^ f: c | d ? 1073741824 & g ? 3221225472 ^ g ^ e ^ f: 1073741824 ^ g ^ e ^ f: g ^ e ^ f
    }
    function d(a, b, c) {
        return a & b | ~a & c
    }
    function e(a, b, c) {
        return a & c | b & ~c
    }
    function f(a, b, c) {
        return a ^ b ^ c
    }
    function g(a, b, c) {
        return b ^ (a | ~c)
    }
    function h(a, e, f, g, h, i, j) {
        return a = c(a, c(c(d(e, f, g), h), j)),
        c(b(a, i), e)
    }
    function i(a, d, f, g, h, i, j) {
        return a = c(a, c(c(e(d, f, g), h), j)),
        c(b(a, i), d)
    }
    function j(a, d, e, g, h, i, j) {
        return a = c(a, c(c(f(d, e, g), h), j)),
        c(b(a, i), d)
    }
    function k(a, d, e, f, h, i, j) {
        return a = c(a, c(c(g(d, e, f), h), j)),
        c(b(a, i), d)
    }
    function l(a) {
        for (var b, c = a.length,
        d = c + 8,
        e = (d - d % 64) / 64, f = 16 * (e + 1), g = new Array(f - 1), h = 0, i = 0; c > i;) b = (i - i % 4) / 4,
        h = i % 4 * 8,
        g[b] = g[b] | a.charCodeAt(i) << h,
        i++;
        return b = (i - i % 4) / 4,
    ......(太多了,后面的省略)

4. 通过测试发现Cookie中的_m_h5_tk和_m_h5_tk_enc是必须参数,如果值无效将返回"非法令牌"。_m_h5_tk_enc应该是_m_h5_tk的的签名或者叫做校验码。

5. 通过分析Cookie的产生过程发现,当我们指定一个无效的_m_h5_tk和_m_h5_tk_enc,服务端会返回一个有效的_m_h5_tk和_m_h5_tk_enc,如下图所示。

淘宝_m_h5_tk和_m_h5_tk_enc产生过程

token的有效获取方法掌握了,问题就都解决了。整理一下思路:

1. 通过提交一个含有无效的_m_h5_tk的请求(或者不带_m_h5_tk),获取服务端返回的有效的_m_h5_tk和_m_h5_tk_enc,进而得到token。

2. 通过h(token + '&' + i + '&' + '12574478' + '&' + data)计算出有效的签名sign。

3. 发送http请求,提取数据。token是可以复用的,不需要每个请求都重新获取一次token。但需要注意的是token是有有效期的,如果返回"令牌过期",就需要重新获取。

下面给出Python的实现过程,其中执行js代码是通过PyExecJS这个库实现的。

# coding: utf-8
# alibaba_h5_sign.py
# 阿里系ajax接口sign签名机制分析及实现

import sys
# pip install PyExecJS
import execjs
from webscraping import common, download

class AlibabaH5Sign:
    """阿里系ajax接口sign签名实现
    """
    def __init__(self, proxy=None):
        self.proxy = None
        self.D = download.Download(read_cache=False, write_cache=False, delay=0.3, use_requests=True,
                                   user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36')
        self.token = None
        self._m_h5_tk = self._m_h5_tk_enc = None
        self.__sign_js_code = ''
        with open('sign.js', 'rb') as f:
            self.__sign_js_code = f.read()
        self.jsctx = None
        
    def get_token(self):
        """获取有效的_m_h5_tk和_m_h5_tk_enc
        提交一个无效的token,服务端会返回一个有效的token
        """
        url = 'https://h5api.m.1688.com/h5/mtop.taobao.widgetservice.getjsoncomponent/1.0/?jsv=2.5.8&appKey=12574478&t=1634284176335&sign=1ae085904b015fe4e1f7c7be21d1e588&api=mtop.taobao.widgetService.getJsonComponent&v=1.0&ecode=1&type=jsonp&isSec=0&timeout=20000&dataType=jsonp&callback=mtopjsonp5&data=%7B%22cid%22%3A%22TpFacCoreInfosService%3ATpFacCoreInfosService%22%2C%22methodName%22%3A%22execute%22%2C%22params%22%3A%22%7B%5C%22facMemId%5C%22%3A%5C%22zjduowei%5C%22%7D%22%7D'
        
        self._m_h5_tk = self._m_h5_tk_enc = None 
        #html = self.D.get(url, headers={'Cookie': '_m_h5_tk=b331d4ff8708d80d8ac280bc05c82ef2_1634294247978; _m_h5_tk_enc=3d5e5c6ee5f337ad5ff34f4da0611acc;'}, proxy=self.proxy)
        html = self.D.get(url, proxy=self.proxy)        
        #print html
        #print self.D.response_headers
        if self.D.response_headers and 'Set-Cookie' in self.D.response_headers:
            new_cookies = self.D.response_headers['Set-Cookie']
            self._m_h5_tk = common.regex_get(new_cookies, r'_m_h5_tk=([a-z\d_]+)', normalized=False)
            self._m_h5_tk_enc = common.regex_get(new_cookies, r'_m_h5_tk_enc=([a-z\d_]+)', normalized=False)
        if self._m_h5_tk and self._m_h5_tk_enc:
            common.logger.info('Successed to get _m_h5_tk({}) and _m_h5_tk_enc({}).'.format(self._m_h5_tk, self._m_h5_tk_enc))
            self.token = self._m_h5_tk.partition('_')[0]
        else:
            common.logger.error('Failed to get token: {}'.format(html))
            
    def sign(self, t, data):
        """计算签名
        """
        st = str(t)
        appKey = '12574478'
        if not self.jsctx:
            self.jsctx = execjs.compile(self.__sign_js_code)
        if not self.token:
            self.get_token()
        if self.token:
            sign_param = '&'.join([self.token, st, appKey, data])
            return self.jsctx.call('h', sign_param)
        else:
            common.logger.error(u'未获取到token,计算签名失败.')
            
    def get_cookie(self):
        """返回http请求用的cookie"""
        return '_m_h5_tk={}; _m_h5_tk_enc={};'.format(self._m_h5_tk, self._m_h5_tk_enc)

def test():
    alisign = AlibabaH5Sign()
    # 获取token
    alisign.get_token()
    # 计算签名
    sign_result = alisign.sign(t='1634528182860', data='{"cid":"TpFacCoreInfosService:TpFacCoreInfosService","methodName":"execute","params":"{\\"facMemId\\":\\"zjduowei\\"}"}')
    print sign_result
    # 发送http请求
    # 如果token过期了,返回的内容含有"令牌过期"字样
    # 只有token不过期就一直可以使用
    
    
if __name__ == '__main__':
    if '--test' in sys.argv:
        test()
特别说明:本文旨在技术交流,请勿将涉及的技术用于非法用途,否则一切后果自负。如果您觉得我们侵犯了您的合法权益,请联系我们予以处理。
☹ Disqus被Qiang了,之前所有的评论内容都看不到了。如果您有爬虫相关技术方面的问题,欢迎发到我们的问答平台:http://spider.site-digger.com/