更多>>关于我们

西安鲲之鹏网络信息技术有限公司从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代码的思路来实现。

  1. function h(a) {  
  2.     function b(a, b) {  
  3.         return a << b | a >>> 32 - b  
  4.     }  
  5.     function c(a, b) {  
  6.         var c, d, e, f, g;  
  7.         return e = 2147483648 & a,  
  8.         f = 2147483648 & b,  
  9.         c = 1073741824 & a,  
  10.         d = 1073741824 & b,  
  11.         g = (1073741823 & a) + (1073741823 & b),  
  12.         c & d ? 2147483648 ^ g ^ e ^ f: c | d ? 1073741824 & g ? 3221225472 ^ g ^ e ^ f: 1073741824 ^ g ^ e ^ f: g ^ e ^ f  
  13.     }  
  14.     function d(a, b, c) {  
  15.         return a & b | ~a & c  
  16.     }  
  17.     function e(a, b, c) {  
  18.         return a & c | b & ~c  
  19.     }  
  20.     function f(a, b, c) {  
  21.         return a ^ b ^ c  
  22.     }  
  23.     function g(a, b, c) {  
  24.         return b ^ (a | ~c)  
  25.     }  
  26.     function h(a, e, f, g, h, i, j) {  
  27.         return a = c(a, c(c(d(e, f, g), h), j)),  
  28.         c(b(a, i), e)  
  29.     }  
  30.     function i(a, d, f, g, h, i, j) {  
  31.         return a = c(a, c(c(e(d, f, g), h), j)),  
  32.         c(b(a, i), d)  
  33.     }  
  34.     function j(a, d, e, g, h, i, j) {  
  35.         return a = c(a, c(c(f(d, e, g), h), j)),  
  36.         c(b(a, i), d)  
  37.     }  
  38.     function k(a, d, e, f, h, i, j) {  
  39.         return a = c(a, c(c(g(d, e, f), h), j)),  
  40.         c(b(a, i), d)  
  41.     }  
  42.     function l(a) {  
  43.         for (var b, c = a.length,  
  44.         d = c + 8,  
  45.         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,  
  46.         h = i % 4 * 8,  
  47.         g[b] = g[b] | a.charCodeAt(i) << h,  
  48.         i++;  
  49.         return b = (i - i % 4) / 4,  
  50.     ......(太多了,后面的省略)  

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这个库实现的。

  1. # coding: utf-8  
  2. # alibaba_h5_sign.py  
  3. # 阿里系ajax接口sign签名机制分析及实现  
  4.   
  5. import sys  
  6. # pip install PyExecJS  
  7. import execjs  
  8. from webscraping import common, download  
  9.   
  10. class AlibabaH5Sign:  
  11.     """阿里系ajax接口sign签名实现 
  12.     """  
  13.     def __init__(self, proxy=None):  
  14.         self.proxy = None  
  15.         self.D = download.Download(read_cache=False, write_cache=False, delay=0.3, use_requests=True,  
  16.                                    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')  
  17.         self.token = None  
  18.         self._m_h5_tk = self._m_h5_tk_enc = None  
  19.         self.__sign_js_code = ''  
  20.         with open('sign.js''rb') as f:  
  21.             self.__sign_js_code = f.read()  
  22.         self.jsctx = None  
  23.           
  24.     def get_token(self):  
  25.         """获取有效的_m_h5_tk和_m_h5_tk_enc 
  26.         提交一个无效的token,服务端会返回一个有效的token 
  27.         """  
  28.         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'  
  29.           
  30.         self._m_h5_tk = self._m_h5_tk_enc = None   
  31.         #html = self.D.get(url, headers={'Cookie': '_m_h5_tk=b331d4ff8708d80d8ac280bc05c82ef2_1634294247978; _m_h5_tk_enc=3d5e5c6ee5f337ad5ff34f4da0611acc;'}, proxy=self.proxy)  
  32.         html = self.D.get(url, proxy=self.proxy)          
  33.         #print html  
  34.         #print self.D.response_headers  
  35.         if self.D.response_headers and 'Set-Cookie' in self.D.response_headers:  
  36.             new_cookies = self.D.response_headers['Set-Cookie']  
  37.             self._m_h5_tk = common.regex_get(new_cookies, r'_m_h5_tk=([a-z\d_]+)', normalized=False)  
  38.             self._m_h5_tk_enc = common.regex_get(new_cookies, r'_m_h5_tk_enc=([a-z\d_]+)', normalized=False)  
  39.         if self._m_h5_tk and self._m_h5_tk_enc:  
  40.             common.logger.info('Successed to get _m_h5_tk({}) and _m_h5_tk_enc({}).'.format(self._m_h5_tk, self._m_h5_tk_enc))  
  41.             self.token = self._m_h5_tk.partition('_')[0]  
  42.         else:  
  43.             common.logger.error('Failed to get token: {}'.format(html))  
  44.               
  45.     def sign(self, t, data):  
  46.         """计算签名 
  47.         """  
  48.         st = str(t)  
  49.         appKey = '12574478'  
  50.         if not self.jsctx:  
  51.             self.jsctx = execjs.compile(self.__sign_js_code)  
  52.         if not self.token:  
  53.             self.get_token()  
  54.         if self.token:  
  55.             sign_param = '&'.join([self.token, st, appKey, data])  
  56.             return self.jsctx.call('h', sign_param)  
  57.         else:  
  58.             common.logger.error(u'未获取到token,计算签名失败.')  
  59.               
  60.     def get_cookie(self):  
  61.         """返回http请求用的cookie"""  
  62.         return '_m_h5_tk={}; _m_h5_tk_enc={};'.format(self._m_h5_tk, self._m_h5_tk_enc)  
  63.   
  64. def test():  
  65.     alisign = AlibabaH5Sign()  
  66.     # 获取token  
  67.     alisign.get_token()  
  68.     # 计算签名  
  69.     sign_result = alisign.sign(t='1634528182860', data='{"cid":"TpFacCoreInfosService:TpFacCoreInfosService","methodName":"execute","params":"{\\"facMemId\\":\\"zjduowei\\"}"}')  
  70.     print sign_result  
  71.     # 发送http请求  
  72.     # 如果token过期了,返回的内容含有"令牌过期"字样  
  73.     # 只有token不过期就一直可以使用  
  74.       
  75.       
  76. if __name__ == '__main__':  
  77.     if '--test' in sys.argv:  
  78.         test()  
特别说明:本文旨在技术交流,请勿将涉及的技术用于非法用途,否则一切后果自负。如果您觉得我们侵犯了您的合法权益,请联系我们予以处理。
☹ Disqus被Qiang了,之前所有的评论内容都看不到了。如果您有爬虫相关技术方面的问题,欢迎发到我们的问答平台:http://spider.site-digger.com/