芝麻HTTP:JavaScript加密逻辑分析与Python模拟执行实现数据爬取
本节来说明一下 JavaScript 加密逻辑分析并利用 Python 模拟执行 JavaScript 实现数据爬取的过程。在这里以中国空气质量在线监测分析平台为例来进行分析,主要分析其加密逻辑及破解方法,并利用 PyExecJS 来实现 JavaScript 模拟执行来实现该网站的数据爬取。
反混淆
JavaScript 混淆之后,其实是有反混淆方法的,最简单的方法便是搜索在线反混淆网站,这里提供一个:http://www.bm8.com.cn/jsConfusion/,我们将 jquery-1.8.0.min.js 中第二行 eval 开头的混淆后的 JavaScript 代码复制一下,然后粘贴到这个网站中进行反混淆,就可以看到正常的 JavaScript 代码了,搜索一下就可以找到 getServerData() 方法了,可以看到这个方法确实发出了一个 Ajax 请求,请求了刚才我们分析到的接口:
那么到这里我们又可以发现一个很关键的方法,那就是 getParam(),它接受了 method 和 object 参数,然后返回得到的 param 结果就作为 POST Data 参数请求接口了,所以 param 就是加密后的 POST Data,一些加密逻辑都在 getParam() 方法里面,其方法实现如下:
- 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)
- }
- })();
可以看到这里使用了 Base64 和 AES 加密。加密之后的字符串便作为 POST Data 传送给服务器了,然后服务器再进行解密处理,然后进行逻辑处理,然后再对处理后的数据进行加密,返回了加密后的数据,那么 JavaScript 再接收到之后再进行一次解密,再渲染才能得到正常的结果。
所以这里还需要分析服务器传回的数据是怎样解密的。顺腾摸瓜,很容易就找到一个 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
- }
嗯,这里又经过了三层解密,才把正常的明文数据解析出来。
所以一切都清晰了,我们需要实现两个过程才能正常使用这个接口,即实现 POST Data 的加密过程和 Response Data 的解密过程。其中 POST Data 的加密过程是 Base64 + AES 加密,Response Data 的解密是 AES + DES + Base64 解密。加密解密的 Key 也都在 JavaScript 文件里能找到,我们用 Python 实现这些加密解密过程就可以了。
所以接下来怎么办?接着刚啊!
接着刚才怪!
何必去费那些事去用 Python 重写一遍 JavaScript,万一二者里面有数据格式不统一或者二者由于语言不兼容问题导致计算结果偏差,上哪里去 Debug?
那怎么办?这里我们借助于 PyExecJS 库来实现 JavaScript 模拟就好了。
PyExecJS
PyExecJS 是一个可以使用 Python 来模拟运行 JavaScript 的库。大家可能听说过 PyV8,它也是用来模拟执行 JavaScript 的库,可是由于这个项目已经不维护了,而且对 Python3 的支持不好,而且安装出现各种问题,所以这里选用了 PyExecJS 库来代替它。
首先我们来安装一下这个库:
- pip install PyExecJS
使用 pip 安装即可。
在使用这个库之前请确保你的机器上安装了以下其中一个JS运行环境:
- JScript
- JavaScriptCore
- Nashorn
- Node
- PhantomJS
- PyV8
- SlimerJS
- SpiderMonkey
PyExecJS 库会按照优先级调用这些引擎来实现 JavaScript 执行,这里推荐安装 Node.js 或 PhantomJS。
接着我们运行代码检查一下运行环境:
- import execjs
- print(execjs.get().name)
运行之后,由于我安装了 Node.js,所以这里会使用 Node.js 作为渲染引擎,结果如下:
- Node.js (V8)
接下来我们将刚才反混淆的 JavaScript 保存成一个文件,叫做 encryption.js,然后用 PyExecJS 模拟运行相关的方法即可。
首先我们来实现加密过程,这里 getServerData() 方法其实已经帮我们实现好了,并实现了 Ajax 请求,但这个方法里面有获取 Storage 的方法,Node.js 不适用,所以这里我们直接改写下,实现一个 getEncryptedData() 方法实现加密,在 encryption.js 里面实现如下方法:
- function getEncryptedData(method, city, type, startTime, endTime) {
- var param = {};
- param.city = city;
- param.type = type;
- param.startTime = startTime;
- param.endTime = endTime;
- return getParam(method, param);
- }
接着我们模拟执行这些方法即可:
- import execjs
- # Init environment
- node = execjs.get()
- # Params
- method = 'GETCITYWEATHER'
- city = '北京'
- type = 'HOUR'
- start_time = '2018-01-25 00:00:00'
- end_time = '2018-01-25 23:00:00'
- # Compile javascript
- file = 'encryption.js'
- ctx = node.compile(open(file).read())
- # Get params
- js = 'getEncryptedData("{0}", "{1}", "{2}", "{3}", "{4}")'.format(method, city, type, start_time, end_time)
- params = ctx.eval(js)
这里我们首先定义一些参数,如 method、city、start_time 等,这些都可以通过分析 JavaScript 很容易得出其规则。
然后这里首先通过 execjs(即 PyExecJS)的 get() 方法声明一个运行环境,然后调用 compile() 方法来执行刚才保存下来的加密库 encryption.js,因为这里面包含了一些加密方法和自定义方法,所以只有执行一遍才能调用。
接着我们再构造一个 js 字符串,传递这些参数,然后通过 eval() 方法来模拟执行,得到的结果赋值为 params,这个就是 POST Data 的加密数据。
接着我们直接用 requests 库来模拟 POST 请求就好了,也没必要用 jQuery 自带的 Ajax 了,当然后者也是可行的,只不过需要加载一下 jQuery 库。
接着我们用 requests 库来模拟 POST 请求:
- # Get encrypted response text
- api = 'https://www.aqistudy.cn/apinew/aqistudyapi.php'
- response = requests.post(api, data={'d': params})
这样 response 的内容就是服务器返回的加密的内容了。
接下来我们再调用一下 JavaScript 中的 decodeData() 方法即可实现解密:
- # Decode data
- js = 'decodeData("{0}")'.format(response.text)
- decrypted_data = ctx.eval(js)
这样 decrypted_data 就是解密后的字符串了,解密之后,实际上是一个 JSON 字符串:
- {', 'wd': '南风', 'tq': '晴间多云'}]}}}
大功告成!
这样我们就可以成功获取温度、湿度、风力、天气等信息了。
另外这部分数据其实不全,还有 PM 2.5、AQI 等数据需要用另外一个 method 参数 GETDETAIL,修改一下即可获取这部分数据了。
再往后的数据就是解析和存储了,这里不再赘述。
芝麻HTTP:JavaScript加密逻辑分析与Python模拟执行实现数据爬取的更多相关文章
- python实现人人网用户数据爬取及简单分析
这是之前做的一个小项目.这几天刚好整理了一些相关资料,顺便就在这里做一个梳理啦~ 简单来说这个项目实现了,登录人人网并爬取用户数据.并对用户数据进行分析挖掘,终于效果例如以下:1.存储人人网用户数据( ...
- python爬虫26 | 把数据爬取下来之后就存储到你的MySQL数据库。
小帅b说过 在这几篇中会着重说说将爬取下来的数据进行存储 上次我们说了一种 csv 的存储方式 这次主要来说说怎么将爬取下来的数据保存到 MySQL 数据库 接下来就是 学习python的正确姿势 真 ...
- 爬虫05 /js加密/js逆向、常用抓包工具、移动端数据爬取
爬虫05 /js加密/js逆向.常用抓包工具.移动端数据爬取 目录 爬虫05 /js加密/js逆向.常用抓包工具.移动端数据爬取 1. js加密.js逆向:案例1 2. js加密.js逆向:案例2 3 ...
- 人人贷网的数据爬取(利用python包selenium)
记得之前应同学之情,帮忙爬取人人贷网的借贷人信息,综合网上各种相关资料,改善一下别人代码,并能实现数据代码爬取,具体请看我之前的博客:http://www.cnblogs.com/Yiutto/p/5 ...
- 【学习笔记】Python 3.6模拟输入并爬取百度前10页密切相关链接
[学习笔记]Python 3.6模拟输入并爬取百度前10页密切相关链接 问题描述 通过模拟网页,实现百度搜索关键词,然后获得网页中链接的文本,与准备的文本进行比较,如果有相似之处则代表相关链接. me ...
- Python爬虫 股票数据爬取
前一篇提到了与股票数据相关的可能几种数据情况,本篇接着上篇,介绍一下多个网页的数据爬取.目标抓取平安银行(000001)从1989年~2017年的全部财务数据. 数据源分析 地址分析 http://m ...
- Python爬虫入门教程 42-100 爬取儿歌多多APP数据-手机APP爬虫部分
1. 儿歌多多APP简单分析 今天是手机APP数据爬取的第一篇案例博客,我找到了一个儿歌多多APP,没有加固,没有加壳,没有加密参数,对新手来说,比较友好,咱就拿它练练手,熟悉一下Fiddler和夜神 ...
- Python网络爬虫与如何爬取段子的项目实例
一.网络爬虫 Python爬虫开发工程师,从网站某一个页面(通常是首页)开始,读取网页的内容,找到在网页中的其它链接地址,然后通过这些链接地址寻找下一个网页,这样一直循环下去,直到把这个网站所有的网页 ...
- Python爬虫入门教程 15-100 石家庄政民互动数据爬取
石家庄政民互动数据爬取-写在前面 今天,咱抓取一个网站,这个网站呢,涉及的内容就是 网友留言和回复,特别简单,但是网站是gov的.网址为 http://www.sjz.gov.cn/col/14900 ...
随机推荐
- 51NOD 1220 约数之和 [杜教筛]
1220 约数之和 题意:求\(\sum_{i=1}^n \sum_{j=1}^n \sigma_1(ij)\) \[ \sigma_0(ij) = \sum_{x\mid i}\sum_{y\mi ...
- Leetcode刷题C#版之Toeplitz Matrix
题目: Toeplitz Matrix A matrix is Toeplitz if every diagonal from top-left to bottom-right has the sam ...
- java单例模式学习笔记
最近一直在学习多线程,在学习过程中遇到了关于单例模式的多线程安全问题,内容如下: 一:首先什么是单例模式 单例模式具有的三要点: 一个类只能有一个实例: 必须是由它自己创建的这个实例: 它必须自行向外 ...
- 平面上给定n条线段,找出一个点,使这个点到这n条线段的距离和最小。
题目:平面上给定n条线段,找出一个点,使这个点到这n条线段的距离和最小. 源码如下: #include <iostream> #include <string.h> #incl ...
- [Uva10601]Cubes
[Uva10601]Cubes 标签: 置换 burnside引理 题意 给你12跟长度相同的小木棍,每个小木棍有一个颜色.统计他们能拼成多少种不同的立方体.旋转后相同的立方体认为是相同的. 题解 这 ...
- 9、flask之SQLAlchemy
本篇导航: 介绍 使用 SQLAlchemy-Utils 一. 介绍 SQLAlchemy是一个基于Python实现的ORM框架.该框架建立在 DB API之上,使用关系对象映射进行数据库操作,简言之 ...
- Zabbix 3.2.4至3.2.7的升级方案
1.关闭Zabbix Server 防止有新的数据提交到数据库中,也可以关闭数据库.如果更新过程中,评估告警信息可以忽略,可以先执行备份操作. 1.1.检查当前版本 /usr/local/zabbix ...
- git 域名配置
在Godaddy购买的域名: 查找DNSpod解析域名,没什么难度,就是添加一条记录,保存而已,记得在添加域名到DNSpod之后,复制两个NS地址到godaddy的域名服务器下: Git项目根目录下创 ...
- Ubantu16.04 redis安装
通过FTP方式将redis的安装包从windows上传到linux上 解压命令:$sudo tar -zxf ~/Downloads/redis-3.2.7.tar.gz -C /usr/local ...
- Redis限制在规定时间范围内登陆错误次数限制
在博客里之前有过一篇文章是 <PHP结合Redis来限制用户或者IP某个时间段内访问的次数>,这篇文章的思路也是一样的.看下代码吧 //登录错误次数校验 $key = "logi ...