zoukankan      html  css  js  c++  java
  • 12、Python 高级反爬机制-破解js加密

    1、案例需求:爬取空气质量数据

    URL:https://www.aqistudy.cn/html/city_detail.html

    2、分析思路:

    1.页面中是有相关的查询条件,指定查询条件后点击查询按钮,就会加载出相关的数据。

    • 查询的条件:
      • 城市名称
      • 查询的时间范围
    • 当点击了查询按钮后,整张页面没有刷新,而是局部页面发生了刷新
      • 说明:点击了查询按钮后,发起了一个ajax请求,该请求可以帮我们进行页面的局部刷新,且请求到符合查询条件的相关指标数据。

    2.目的:想要获取点击查询按钮后加载出来的数据。将ajax请求到的数据获取即可。

    • 可以通过抓包工具捕获到ajax请求的数据包,

      从数据包中想要提取出:

      • 请求url:https://www.aqistudy.cn/apinew/aqistudyapi.php
      • 请求方式:post
      • 请求参数:d,参数值是一组看不懂的字符串
        • 是动态变化的请求参数(每次请求对应的请求参数的值都是不一样的)

    • 响应数据:一组看不懂的字符串
      • 响应一定是需要加载到前台页面进行显示,但是捕获到响应数据并不是前台页面加载出来的原文数据。说明请求到的响应数据一定是一组密文数据。

    3.处理动态变化的请求参数

    • 该请求是一个ajax请求,查看ajax请求对应的js代码或者Jquery代码中有没有请求参数的设置呢?
    • 当点击了页面中的查询按钮后,就会发起一个ajax请求。说明该查询按钮一定被绑定了一个点击事件,当点击事件发生后就会执行一组ajax请求的代码。
    • 通过查看ajax请求代码,就可以发现请求参数的参数值。

    示例代码:

    $.ajax({
           	 url:"发送请求(提交或读取数据)的地址", 
             dataType:"预期服务器返回数据的类型",  
             type:"请求方式", 
             async:"true/false",
             data:{id:func},
             success:function(data){请求成功时执行},      
             error:function(){请求失败时执行}
    });
    

    4.寻找ajax请求的代码。

    • 找到查询按钮绑定的点击事件。(火狐浏览器的开发者工具)

    5.发现getData函数就是点击了查询按钮后触发的函数,该函数实现内容去查询ajax请求的代码

    6.查看getData函数的实现:

    跳转到指定的位置

    局部搜索getData

    • type=="HOUR":查询条件是以小时为单位

    • 没有发现ajax请求代码,但有另外的两个函数的调用,

    • 说明ajax请求代码一定是存在于这两个函数实现中。

      getAQIData();

      getWeatherData();

    7.查看getAQIData的实现:

    • 未发现ajax请求代码,发现了另一个函数调用getServerData,
      • getServerData参数:
        • method = GETDETAIL
        • param = {city,type,starttime,endtime}

    8.getServerData(method,param)的实现:

    • 可以基于谷歌浏览器的抓包工具进行全局搜索。

    • js混淆:服务器端会将一些比较重要的函数实现进行加密。

    • 解决方式:通过工具js反混淆:进行线上平台暴力破解。平台的url:http://www.bm8.com.cn/jsConfusion/

    • 破解后找到了通过getServerData函数的实现ajax请求代码,

      且发现

      • 动态变化的请求参数:getParam(method, object)函数返回
        • 函数参数
          • method:GETDETAI
          • object:{city,type,starttime,endtime}
      • 加密的响应数据解密的函数:decodeData(data)
        • 参数data为加密的响应数据,返回值为解密后的原文数据
      function getServerData(method, object, callback, period) {
              const key = hex_md5(method + JSON.stringify(object));
              const data = getDataFromLocalStorage(key, period);
              if (!data) {
                  var param = getParam(method, object);
                  $.ajax({
                      url: '../apinew/aqistudyapi.php',
                      data: {
                          d: param
                      },
                      type: "post",
                      success: function (data) {
                          data = decodeData(data);
                          obj = JSON.parse(data);
                          if (obj.success) {
                              if (period > 0) {
                                  obj.result.time = new Date().getTime();
                                  localStorageUtil.save(key, obj.result)
                              }
                              callback(obj.result)
                          } else {
                              console.log(obj.errcode, obj.errmsg)
                          }
                      }
                  })
              } else {
                  callback(data)
              }
          }
      

    3、js逆向获取数据:

    • js逆向

      • 可以将js代码改写成python代码进行相关的调用。
      • 方式:
        • 1.手动将js函数改写成python函数
        • 2.可以使用工具自动进行python函数的改写
          • pyexclJS
    • PyExecJS介绍:PyExecJS 是一个可以使用 Python 来模拟运行 JavaScript 的库。

    • 需要环境安装

      pip install PyExecJS
      
    • 注意:如果想使用PyExecJS的话,在你的本机中必须安装好nodejs的环境。

    将解密成功的js方法存放到本地jsCode.js并对其方法进行封装

    function getPostParamCode(method, city, type, startTime, endTime){
        var param = {};
        param.city = city;
        param.type = type;
        param.startTime = startTime;
        param.endTime = endTime;
        return getParam(method, param);
    }
    
    #动态实时获取ajax请求的参数
    import execjs
    node = execjs.get()
     
    # Params
    method = 'GETDETAIL'
    city = '北京'
    type = 'HOUR'
    start_time = '2018-01-25 00:00:00'
    end_time = '2018-01-25 23:00:00'
     
    # Compile javascript
    file = 'jsCode.js'
    ctx = node.compile(open(file,encoding='utf-8').read())
     
    # Get params
    js = 'getPostParamCode("{0}", "{1}", "{2}", "{3}", "{4}")'.format(method, city, type, start_time, end_time)
    params = ctx.eval(js)			#调用执行封装好的方法
    print(params)
    
    >>>
    tdgHOYxwKdDSgYXe+RLPzYCgLvrddahasI5XXklB4gVLYqab+XRPpMD/oSqnJ/aEmFwzVEUhLnPzRy03+X1BIzLvxQKwu4A3YsqR3OemYgNnHqPdBwvJlbxia99YeK+xNLwdqFad2OO8kQ/eMmdXDnGMvVAdhy3hOdXSgMgwVdUjXSyKzDV31TAxmYlJqwB6U3oElEpwW7AG1sOS1EpGER7Q1a1xkekm9tvDAeHRXrPB1jXX4hsdnZoYBSE23ei+sBC/30MZXDD1ons7hnF4fNS7j0aSqyscRk5ueQAvN1FRHCg9aM9tClVrDd4dC9q5Tk8vlH8aiTmGBZjYRkdIina1REOBdr3z73I+8GTRintq9RjSTycygKb3IpNejPAtU+P4FwPOmhiTCf1pDl0GXOw23BHL/8yR0yWCSHwOH+EDUmV+oQKOwh7T84w7LGjaHB0hGrvW94R6bI5iC+Qsaw==
    
    #携带上动态变化的请求参数发起ajax请求获取加密的响应数据
    import execjs
    node = execjs.get()
     
    # Params
    method = 'GETDETAIL'
    city = '北京'
    type = 'HOUR'
    start_time = '2018-01-25 00:00:00'
    end_time = '2018-01-25 23:00:00'
     
    # Compile javascript
    file = 'jsCode.js'
    ctx = node.compile(open(file,encoding='utf-8').read())
     
    # Get params
    js = 'getPostParamCode("{0}", "{1}", "{2}", "{3}", "{4}")'.format(method, city, type, start_time, end_time)
    params = ctx.eval(js)			#调用执行封装好的方法
    
    #发起post请求
    url = 'https://www.aqistudy.cn/apinew/aqistudyapi.php'
    response_text = requests.post(url, data={'d': params}).text
    print(response_text)
    >>>
    qZMXM1uw6YvIv1UWRplJEP8adQ/jrupTMOgOGHddu9sLczXIVdbUQNC6FKKO1n/E+u+ROZbS20IkqL9BxAlZGzas1Cr/5Xra0/8RJ4dgOSerFidYiI6gQTe2hR83SC2FtPOuHYS/0KmslfuTqyH21g==
    
    import execjs
    import requests
    
    node = execjs.get()
    
    # Params
    method = 'GETDETAIL'
    city = '北京'
    type = 'HOUR'
    start_time = '2018-01-25 00:00:00'
    end_time = '2018-01-25 23:00:00'
    
    # Compile javascript
    file = 'jsCode.js'
    ctx = node.compile(open(file,encoding='utf-8').read())
    
    # Get params
    js = 'getPostParamCode("{0}", "{1}", "{2}", "{3}", "{4}")'.format(method, city, type, start_time, end_time)
    params = ctx.eval(js)
    
    #发起post请求
    url = 'https://www.aqistudy.cn/apinew/aqistudyapi.php'
    response_text = requests.post(url, data={'d': params}).text
    
    #对加密的响应数据进行解密
    js = 'decodeData("{0}")'.format(response_text)
    decrypted_data = ctx.eval(js)
    print(decrypted_data)
    

    这篇博文涉及到的反爬机制有:

    • js加密
      • js逆向可以破解js加密
    • 动态变化的请求参数
    • js混淆
  • 相关阅读:
    Android中文API(119)——TableRow
    Android开发者指南(12) —— Android Supported Media Formats
    Android中文合集 最终版
    Android开发者指南(10) —— Android API Levels
    Android中文API(126) —— Message
    Android中文API(118)——FilterQueryProvider
    [Android]通过PhoneLookup读取所有电话号码
    Android中文API(124) —— DialerFilter
    Android开发者指南(15) —— Managing Virtual Devices
    Android中文API(127) —— MessageQueue
  • 原文地址:https://www.cnblogs.com/remixnameless/p/13179271.html
Copyright © 2011-2022 走看看