爬虫之JS混淆和加密案例

需求:

中国空气质量在线监测分析平台是一个收录全国各大城市天气数据的网站,包括温度、湿度、PM 2.5、AQI 等数据,链接为:https://www.aqistudy.cn/html/city_detail.html,网站显示为:

一连串的分析

该网站所有的空气质量数据都是基于图表进行显示的,并且都是触发鼠标滑动或者点动后才会显示某点的数据,所以如果基于selenium进行数据爬取很吃力,因此考虑采用requests模块进行数据爬取.

首先要找到空气质量数据所在的数据包:

使用抓包工具抓取,经过尝试发现,只有设置的图中设置项发生了变化,然后点击查询按钮,在抓包工具中才会捕获到两个ajax请求的数据包,判断需要爬取的数据存在于该数据包中.

通过这两个 aqistudyapi.php 数据包发现,ajax请求的url为 https://www.aqistudy.cn/apinew/aqistudyapi.php ,请求方式为POST,两个ajax请求均发送了一个参数(d),但是其对应的值让我看不懂,并且两个的值不同,考虑是两次提交的参数不相同,并且进行了加密.

接着去看ajax请求的响应数据,发现也是一串加密后的字符串

那么问题来了,ajax请求提交的数据和返回的响应数据都是经过加密的

  1. 如何生成加密的请求数据?
  2. 在获取响应数据后如何解密?

解决:

现在已经直到,两个aqistudyapi.php的ajax请求都是在修改设置并且点击查询按钮后触发的,也就是说查询按钮上一定绑定了某个点击事件,由这个点击事件发送了对应的ajax请求,首先通过浏览器查看该查询按钮绑定的事件栈

点击到事件对应的js代码,发现事件执行了getData()函数

接下来,搜索getData,找到该函数的js代码

接下来,搜索getAQIData函数,发现getWeatherData函数就在其下方,真是贴心

经过分析后发现,两个函数都调用了getServerData函数,并且传入了method,type,函数,0.5四个参数,此时思考一下,既然没有其余的代码,那么ajax请求是如何发送的呢,显而易见是由getServerData函数发送的,并且传入的函数看起来像是对obj.data进行了一些列的分析展示

[分析-两个函数的内部]

那么,让我们去找一下getServerData函数,首先局部搜索发现没有这个函数,不碍事,让我们来全局搜索一下看看

JavaScript 混淆: 我们会惊讶的发现getServerData后面跟的是什么鬼啊?不符合js函数定义的写法呀!看不懂呀!其实这里是经过 JavaScript 混淆加密了,混淆加密之后,代码将变为人不可读的形式,但是功能是完全一致的,这是一种常见的 JavaScript 加密手段.我们想要查看到该方法的原始实现则必须对其进行反混淆.

JavaScript 反混淆: JavaScript 混淆之后,其实是有反混淆方法的,最简单的方法便是搜索在线反混淆网站,例如 http://www.bm8.com.cn/jsConfusion/ ,将getServerData存在的这行数据粘贴到反混淆网站中

来分析一下getServerData函数,发现它判断当前页面数据后,发起了一个ajax请求(终于找到了!),调用getParam函数,传入了两个参数(method和object),将getParam函数的返回值赋值给param,并且作为ajax请求的参数(d)对应的值发送到了../apinew/aqistudyapi.php,我们看到的乱码一样的参数就是这个函数产生的.

接下来,使用decodeData函数对响应数据进行了解密,然后使用json反序列化,将反序列化的结果赋值给obj,然后调用之前传入的callback进行分析

回想一下getServerData有四个参数分别是:

  • method: GETDETAIL或 GETCITYWEATHER
  • object: param对象,是个字典,有四个属性
    • city: 明显是城市
    • type: 通过标签定位发现是主页右上角的radio框的值,分别为HOUR,DAY,MOUTH
    • startTime: 起始时间
    • endTime: 终止时间
  • callback: 回调函数,用于分析解密后的ajax请求响应
  • period: 0.5

接下来分别找一下getParam和decodeData函数

function decodeData(data) {
        data = AES.decrypt(data, aes_server_key, aes_server_iv);
        data = DES.decrypt(data, des_key, des_iv);
        data = BASE64.decrypt(data);
        return data
    }

// 发现decodeData的内部分别使用了AES,DES,BASE64对响应数据进行解密
var getParam = (function () {
        function ObjectSort(obj) {
            var newObject = {};
            Object.keys(obj).sort().map(function (key) {
                newObject[key] = obj[key]
            });
            return newObject
        }
        return function (method, obj) {
            var appId = '1a45f75b824b2dc628d5955356b5ef18';
            var clienttype = 'WEB';
            var timestamp = new Date().getTime();
            var param = {
                appId: appId,
                method: method,
                timestamp: timestamp,
                clienttype: clienttype,
                object: obj,
                secret: hex_md5(appId + method + timestamp + clienttype + JSON.stringify(ObjectSort(obj)))
            };
            param = BASE64.encrypt(JSON.stringify(param));
            return AES.encrypt(param, aes_client_key, aes_client_iv)
        }
    })();
// getParam的内部时使用了BASE64和AES对请求数据进行了加密

总结:

点击查询按钮后,触发的事件最终执行了 getServerData 函数,发起了ajax请求,请求的参数是通过getParam(method,object)进行加密的,响应的数据是通过decodeData(data)进行解密的.

PyExecJS 模块

接下来,需要借助于 PyExecJS模块来实现模拟JavaScript代码执行,获取动态加密的请求参数,然后再将加密的响应数据传入decodeData进行解密

PyExecJS介绍: PyExecJS 是一个可以使用 Python 来模拟运行 JavaScript 的库.

环境安装:

  • pip install PyExecJS
  • 安装nodejs的开发环境

接下来,然我们一步一步的来实现

获取ajax请求的动态变化且加密的请求参数

  • 将反混淆网站中的代码粘贴到code.js文件中
  • 在该js文件中添加一个自定义函数getPostParamCode,该函数是为了获取且返回post请求的动态加密参数
function getPostParamCode(method, city, type, startTime, endTime){
    // 封装getParam函数所需的参数
    var param = {};
    param.city = city;
    param.type = type;
    param.startTime = startTime;
    param.endTime = endTime;
    return getParam(method, param);
}
import execjs
# 实例化一个对象
node = execjs.get()

# 创建参数
method = 'GETCITYWEATHER'
city = '北京'
c_type = 'HOUR'
start_time = '2019-10-09 00:00:00'
end_time = '2019-10-11 23:00:00'

# 编译需要的js代码
file_path = './code.js'
js_obj = node.compile(open(file_path, encoding='utf-8').read())
# 获取加密的参数
js_code = 'getPostParamCode("{0}", "{1}", "{2}", "{3}", "{4}")'.format(method, city, c_type, start_time, end_time)
params = js_obj.eval(js_code)
print(params)

携带加密后的数据发送请求

import execjs
import requests
# 实例化一个对象
node = execjs.get()

# 创建参数
method = 'GETCITYWEATHER'
city = '北京'
c_type = 'HOUR'
start_time = '2019-10-09 00:00:00'
end_time = '2019-10-11 23:00:00'

# 编译需要的js代码
file_path = './code.js'
js_obj = node.compile(open(file_path, encoding='utf-8').read())
# 获取加密的参数
js_code = 'getPostParamCode("{0}", "{1}", "{2}", "{3}", "{4}")'.format(method, city, c_type, start_time, end_time)
params = js_obj.eval(js_code)

# 发送请求
url = 'https://www.aqistudy.cn/apinew/aqistudyapi.php'
response_text = requests.post(url, data={'d': params}).text
print(response_text)

对响应数据进行解密

import execjs
import requests
# 实例化一个对象
node = execjs.get()

# 创建参数
method = 'GETCITYWEATHER'
city = '北京'
c_type = 'HOUR'
start_time = '2019-10-09 00:00:00'
end_time = '2019-10-11 23:00:00'

# 编译需要的js代码
file_path = './code.js'
js_obj = node.compile(open(file_path, encoding='utf-8').read())
# 获取加密的参数
js_code = 'getPostParamCode("{0}", "{1}", "{2}", "{3}", "{4}")'.format(method, city, c_type, start_time, end_time)
params = js_obj.eval(js_code)

# 发送请求,获取响应数据
url = 'https://www.aqistudy.cn/apinew/aqistudyapi.php'
response_text = requests.post(url, data={'d': params}).text

# 对响应数据进行解密
js_decode_data = 'decodeData("{}")'.format(response_text)
decode_data = js_obj.eval(js_decode_data)
print(decode_data)

至此,完成了对空气质量数据的爬取.

原文地址:https://www.cnblogs.com/douzi-m/p/12359376.html

时间: 2024-10-09 22:58:03

爬虫之JS混淆和加密案例的相关文章

爬虫之Js混淆&加密案例

需求: 中国空气质量在线监测分析平台是一个收录全国各大城市天气数据的网站,包括温度.湿度.PM 2.5.AQI 等数据,链接为:https://www.aqistudy.cn/html/city_detail.html,网站显示为: 一连串的分析 该网站所有的空气质量数据都是基于图表进行显示的,并且都是触发鼠标滑动或者点动后才会显示某点的数据,所以如果基于selenium进行数据爬取很吃力,因此考虑采用requests模块进行数据爬取. 首先要找到空气质量数据所在的数据包: 使用抓包工具抓取,经

简谈-Python爬虫破解JS加密的Cookie

通过Fiddler抓包比较,基本可以确定是JavaScript生成加密Cookie导致原来的请求返回521. 发现问题: 打开Fiddler软件,用浏览器打开目标站点(http://www.kuaidaili.com/proxylist/2/) .可以发现浏览器对这个页面加载了两次,第一次返回521,第二次才正常返回数据.很多没有写过网站或是爬虫经验不足的童鞋,可能就会觉得奇怪为什么会这样?为什么浏览器可能正常返回数据而代码却不行? 仔细观察两次返回的结果可以发现: 1.第二次请求比第一次请求的

js压缩、混淆和加密

最近看到有些论坛在讨论js压缩.混淆和加密的问题,特意找了些资料看了下,现在总结一下: 1.关于三者的定义与区别 压缩:删除 Javascript 代码中所有注释.跳格符号.换行符号及无用的空格,从而压缩 JS 文件大小,优化页面加载速度. 混淆:经过编码将变量和函数原命名改为毫无意义的命名(如function(a,b,c,e,g)等),以防止他人窥视和窃取 Javascript 源代码,也有一定压缩效果. 加密:一般用eval方法加密,效果与混淆相似,也做到了压缩的效果. 从定义中可以看出,压

echarts   js 模板制作 地图 案例分享

今天分享一个 echarts   js 模板制作 地图 案例,有类似需求的筒子可以参考 O(∩_∩)O 需求:因为最近管理的全国各地代理服务器越来越多,有时上级需要看下我们的代理分布,比如带宽,比如供应商,如果用纯excel不是很直观,就套用了前端的Js模板,修改了些代码,填充部分值进去,就出来基本效果了. 上效果图: 基本上把全国各地的资源情况很直观的显示出来了,这里只填充了部分值,后期会将值存入mysql,并且用上ajax 来塞值,这里就列个基本的效果图. 5分钟上手echarts,官网教程

用JS添加文本框案例代码

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-

MD5加密案例

MD5加密案例 最近在做权限有关的案例,需要用到MD5加密技术,所以在此拿出来分享一下: 下面这块代码不仅包括MD5加密,还有SHA1加密技术: public class MD5Utils { public static String getMD5(String comeString) { String s = null; char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', '

JS实现base64加密解密

JS实现base64加密解密 转载自http://blog.csdn.net/fengzheng0306/archive/2006/04/25/676055.aspx 方法一: <HTML><HEAD><TITLE>Base64</TITLE><script language=javascript>var base64EncodeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrs

C#解惑46: 令人混淆的构造器案例

谜题46: 令人混淆的构造器案例 本谜题呈现了两个容易令人混淆的构造器.Main方法调用了一个构造器,但是它调用的究竟是哪一个呢?该程序的输出取决于这个问题的答案.那么它会打印什么呢?甚至它是否合法? class Confusing { Confusing(object o) { System.Console.WriteLine("object"); } Confusing(double[] dArray) { System.Console.WriteLine("double

小试.NET代码保护软件(代码混淆、加密)

有着微软人性化的开发工具VISUAL STUDIO和MSDN详尽的帮助,.NET 的开发效率的确高. 但是由于.NET同JAVA一样都采用中间语言.虚拟机/SDK等诸多特质,而且高等语言的类库编码规范,MSIL微软中间语言可读性很高,所以代码很容易就能被反编译,或许这是一种“另类的开源”. 到目前为止.NET开发的桌面软件或者说共享软件真的很少,大多都是一些管理系统.ERP.等一些数据库软件,这些软件一般结构复杂,采用了架构或者平台等一些机制,而且一个功能都可以写N多个类,即使被反编译了别人也要