更多>>关于我们

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

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

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

更多>>官方微博

西安鲲之鹏
陕西 西安

加关注

  • 【经验分享】playwright/selenium绕过反自动化/爬虫检测最新方案
    原理:是通过在页面加载之前注入js脚本,抹去navigator.webdriver等浏览器自动化特征。
    使用的这个js脚本(stealth.min.js)来源于Github上的puppeteer-extra-plugin-stealth项目(A plugin for puppeteer-extra and playwright-extra to prevent detection.)。

    playwright的示例:
    context.add_init_script(path='stealth.min.js')
    page = context.new_page()
    page.goto("https://bot.sannysoft.com/")

    selenium的示例:
    driver = webdriver.Chrome()
    with open('stealth.min.js', 'r') as f:
    js = f.read()
    driver.execute_cdp_cmd('Page.addScriptToEvaluateOnNewDocument', {'source': js})

    效果检验:
    1. 第一个检验标准,是看是否还会爆出navigator.webdriver等特征,可以直接在控制台查看,也可以通过访问https://bot.sannysoft.com/来查看。例如附图1是未做绕过处理时的截图。
    2. 第二个检验标准,是尝试登录淘宝,看能否成功。如果未做绕过处理,会出现滑块验证码,而且是无法正常处理的那种,无法登录成功。如附图2所示。

    经过测试,上述两项检验标准均通过:即没有爆出navigator.webdriver等特征,也可以成功登录淘宝,不出现滑块验证码。

    另附stealth.min.js的下载地址:
    http://www.site-digger.com/uploads/stealth.min.js
    发布时间:2022-08-10 11:05:15
  • 【经验分享】miller以XTAB格式查看字段内容较多或者字段较多的CSV文件
    示例:mlr --icsv --oxtab --from mouser_products_202208.csv head -n 3
    官方文档:https://miller.readthedocs.io/en/latest/file-formats/#xtab-vertical-tabular

    效果如附图所示。 ​​​
    发布时间:2022-08-10 10:26:30
  • 【分享】"CZ88纯真IP库20220420版" - MySQL版(52万条)免费下载

    数据说明:
    基于”QQ纯真IP库20220420版”转换的MySQL版本,共计529,985条。添加的数字格式的IP段起始和终止字段,可以通过该数据快速查询出某IP的归属国家、位置信息。

    字段说明:
    `ip_start_num` – 起始IP,数字类型(根据ip_start转换)
    `ip_end_num` – 终止IP,数字类型(根据ip_end转换)
    `ip_start` – 起始IP,字符串类型
    `ip_end` – 终止IP,字符串类型
    `country` – 所属国家
    `location` – 所在位置

    用法举例:
    (1)先将待查询IP转为数字类型。例如:
    ip_num = struct.unpack(‘!I’, socket.inet_aton(ip))[0]
    (2)查询”ip_start_num <= ip_num and ip_end_num >= ip_num”的记录,例如SQL语句:
    sql = ‘SELECT country FROM ips WHERE ip_start_num <= %s AND ip_end_num >= %s;’.format(ip_num, ip_num)

    示例数据:
    http://db.site-digger.com/csv/637a38385f6970735f32303232303432305f73616d706c65/

    下载链接:
    http://www.data-shop.net/2014/08/cz88-free-ips-database-20220420-mysql-version-52w/

    应用举例:
    如附图2所示,是使用该数据实现的IP地址归属地查询API。
    发布时间:2022-08-02 12:34:26
  • 【经验分享】一例Squid http认证异常问题的排查
    背景:
    (1)Squid配置的HTTP代理。使用external_acl_type自定义acl脚本进行ip和用户名密码认证,acl通过的放行,否则deny。
    (2)Playwright设置带用户名密码认证的http代理(上述Squid)代理。

    问题:
    curl命令或者requests库测试均工作正常,但是Playwright始终报 NS_ERROR_PROXY_FORBIDDEN错误。

    排查:
    用curl -v命令测试用户名密码不对或者为空情况下squid的返回,如下图所示,返回的是403状态码。意识到问题的所在了,对于浏览器来说,只有当服务端返回407状态码时,才会启用HTTP Proxy Basic Auth认证。此时返回403直接中断了后续认证流程。

    解决:
    自定义的external_acl_type之后,在http_access deny all之前,加入acl auth_user,让其在external_acl_type的ACL匹配失败的情况下能够返回407状态码。

    延伸:
    如何让“浏览器一开始就带上Proxy-Authorization头,这样就能直接通过认证,减少一次407的返回,提高效率。就像request库一样”? 经过测试发现使用一些代理插件,例如SwitchyOmega,可以实现。
    发布时间:2022-06-23 10:58:25
  • 【经验分享】利用HAProxy Runtime API的"set server"命令可以动态修改Haproxy的Backend server。
    例如 将Backend "backendout"的 Server "server1"的地址修改为115.209.110.181,端口修改为32603,执行如下命令即可。
    echo "set server backendout/server1 addr 115.209.110.181 port 32603" | socat stdio tcp4-connect:127.0.0.1:9999

    有两个前提:
    (1)HAproxy 2.0以后的版本才支持 Runtime API。
    (2)在配置文件里要开始 Runtime API的端口。如下配置示例,在9999端口开启Runtime API。
    global
    stats socket ipv4@127.0.0.1:9999 level admin

    详细用法可以参见官方博客里的示例,链接为https://www.haproxy.com/blog/dynamic-configuration-haproxy-runtime-api/
    发布时间:2022-05-22 16:22:21
  • 【经验分享】linux下如何检测某个网口下有ADSL拨号设备信号?
    用途:检测拨号设备是否工作正常,或者网口与拨号设备的连接是否正常。
    方法:可以使用"pppoe-discovery -I eth1"命令,eth1修改为具体要检测的网口。如果对应网口和拨号设备连接正常,将会返回BAS的名称和IP。
    文档连接:https://www.systutorials.com/docs/linux/man/8-pppoe-discovery/

    如附图所示,为分别在连云港联通、南京电信、南昌电信拨号环境下做的测试。
    发布时间:2022-05-10 19:10:47
  • 【经验分享】如何查看客户通过web隧道访问了什么网站?
    Web隧道内传输的数据通常是经过ssl加密的,如何通过抓包查看客户访问了什么网站呢?我们知道Web隧道的建立离不开HTTP的CONNECT方法,因为只要我们过滤CONNECT关键词就能拿到客户连接的目标网址。
    例如,我们通过ngrep进行包过滤,锁定Web隧道服务的端口(本例为65002)
    sudo ngrep -d ens3 -W byline -N -t -q CONNECT port 65002
    PS:ens3 是网口。结果如附图所示。
    发布时间:2022-05-10 16:58:59
  • 【经验分享】如何计算阿里系Ajax请求中的sign签名?
    有过阿里系采集经验的开发者都应该知道,某宝(天猫)H5版、1688、某宝司法拍卖H5版等阿里系网站,在Ajax请求中都会有一个sign签名参数(如下图1、2、3所示),要是值不正确将无法获取到有效的数据(例如返回“非法请求”提示)。如果我们无法构造出有效的sign,就只能通过“模拟浏览器操作”的方式来绕过签名验证,再结合"mitmproxy动态抓包脚本"来提取返回数据,这种方案效率太低,而且很不灵活。本文将介绍如何计算这个sign值以及给出对应的Python实现,这样就能实现通过直接HTTP交互抓取数据。点击链接查看详情>>> http://www.site-digger.com/html/articles/20211018/851.html

    •  
    发布时间:2021-10-18 14:59:23
  • 【经验分享】接上一篇:Android APP内置WebView如何获取当前加载的URL(方法二)?
    这次是直接HOOK android.webkit.WebView类的loadUrl()方法,可以直接得到加载的URL。同时也可以再loadUrl()内调用setWebContentsDebuggingEnabled(true)开启远程调试。效果如附图所示,这是某宝APP内嵌的H5页面,成功获取到了URL并且在inspect中能看到对应的WebView窗口(开启该WebView的远程调试成功)。

    // 查看WebView加载的URLs并启动远程调试
    Java.use("android.webkit.WebView").loadUrl.overload("java.lang.String").implementation = function (s) {
    console.log('WebView.loadUrl():' + s);
    console.log('Set webview DebuggingEnabled!');
    this.setWebContentsDebuggingEnabled(true);
    this.loadUrl.overload("java.lang.String").call(this, s);
    };
    发布时间:2022-05-06 13:31:47
  • 【经验分享】Android APP内置WebView如何获取当前加载的URL?
    (1)使用Firida强制其开启WebView的远程调试,以某司法拍卖APP为例,执行如下代码后。
    Java.perform(function() {
    var WebView = Java.use("com.uc.webview.export.WebView");
    WebView.setWebContentsDebuggingEnabled(true);
    }
    );
    (2)通过adb forward命令把WebView的Unix域套接字映射为本地端口,详细步骤可参见我们的这篇文章http://www.site-digger.com/html/articles/20220414/860.html
    (3)通过WebView的远程调试接口即可看到当前Tab的JSON数据,如附图所示,其中就有URL信息。
    发布时间:2022-05-06 13:04:25
当前位置: 首页 > 技术文章 >
如何计算阿里系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/