中国空气质量在线检测平台数据爬取
中国空气质量在线检测平台数据爬取
平台网站:https://www.aqistudy.cn/html/city_detail.html
1.获取综合版块对应的数据:
将当前页面的搜索条件进行修改后,点击搜索按钮,
才可以通过抓包工具捕获到ajax请求的数据包,
数据包中提取出的ajax请求的url是:https://www.aqistudy.cn/apinew/aqistudyapi.php
2.ajax请求的数据包中响应数据是经过加密的密文数据data,
并且请求参数d所对应的请求参数d的值是动态变化.
3.找到搜索按钮点击所对应的点击事件,
通过火狐浏览器进行操作,操作结束后,发现搜索按钮有一个click事件,
该事件触发后执行了一个getData()的js函数,定位该函数的源码位置
function getData()
{
state = 0;
city=$('#city').val();
$.cookie('dcity', city, {expires : 30});
//type = $('#type').combobox('getValue');
type = $('input:radio[name=type]:checked').val();
getTimeSel();
if(type=="HOUR")
{
var timediff = converTimeFormat($('#dtbEndTime').datetimebox('getValue')).getTime()-converTimeFormat($('#dtbStartTime').datetimebox('getValue')).getTime();
if(timediff >30*24*3600*1000)
{
showMessage(false,"按小时查询仅支持查询一个月数据,查看长时间变化趋势请选择按日查询!");
return ;
}
}
getAQIData();
getWeatherData();
}
在该函数内部有一个type=="HOUR",
还有两个重要的函数调用,分别是 getAQIData();getWeatherData();
function getAQIData()
{
var method = 'GETDETAIL';
var param = {};
param.city = city;
param.type = type;
param.startTime = startTime;
param.endTime = endTime;
getServerData(method, param, function(obj) {
function getWeatherData()
{
var method = 'GETCITYWEATHER';
var param = {};
param.city = city;
param.type = type;
param.startTime = startTime;
param.endTime = endTime;
getServerData(method, param, function(obj) {
getAQIData();getWeatherData()的实现内部取查找相关的蛛丝马迹:
定义了method='GETDETAIL'和param这两个变量,并且param是一个字典,字典中有四个键值(city,type,startTime,endTime)
还发现了另一个函数的调用getServerData(method,param,回调函数,0.5)
分析getServerData这个函数的定义,想要从中找到相关的蛛丝马迹:
在谷歌抓包工具中做全局搜索,最终定位到了该函数的实现在jQuery-min.js文件中,
来到了该文件中进行了getServerData的定义查找,发现找到的是经过加密的js函数的实现。
js代码块加密成为js混淆:我们必须将混淆的数据进行js反混淆。
js反混淆:通过该网站进行反混淆的操作http://www.bm8.com.cn/jsConfusion/
经过反混淆后,我们终于看到了getServerData函数js实现的源码,对实现的源码进行分析:
-
在该函数内部找到了ajax请求相关的操作
-
找到了动态请求参数d值的来源:
getParam(method, object); 返回的就是我们苦苦寻找的动态变化的post请求参数。
-
找到了对ajax请求到的密文数据data进行解密的js函数:
decodeData(data),该函数参数data就是加密的响应数据,该函数的返回值就是经过解密的原文数据。
分析总结:点击查询按钮后,最终是触发了getServerData函数发起了ajax请求,请求参数是通过getParam进行的加密,响应回来的密文数据是通过decodeData函数进行解密处理的。
问题:找到的相关重要的信息对应的函数都是js写的,爬虫程序必须基于python实现
4.PyExecJS介绍
PyExecJS 是一个可以使用 Python 来模拟运行 JavaScript 的库。
我们需要pip install PyExecJS对其进行环境安装。
注意点:如果想在python中使用PyExecJS,必须事先安装好nodejs的环境
-
将反混淆的js代码保存下来
-
在该文件中添加自定义函数,getPostParamCode,该函数是为了获取且返回post请求的动态加密参数
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); }
在py文件中先获取node对象,设定参数值,再编译js文件
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 = 'js.js'
# 注意要加上编码,否则可能会出现编码问题报错
ctx = node.compile(open(file,encoding='utf-8').read())
py文件中执行js语句获取动态加密参数
# Get params
js = 'getPostParamCode("{0}", "{1}", "{2}", "{3}", "{4}")'.format(method, city, type, start_time, end_time)
params = ctx.eval(js)
访问url获取加密数据
url = 'https://www.aqistudy.cn/apinew/aqistudyapi.php'
pm = {
'd':params
}
#text就是请求到的加密的响应数据
text = requests.post(url,data=pm).text
执行js语句解密data数据
js = 'decodeData("{0}")'.format(text)
data = ctx.eval(js)
print(data)
成功代码:
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 = 'js.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)
url = 'https://www.aqistudy.cn/apinew/aqistudyapi.php'
pm = {
'd': params
}
# text就是请求到的加密的响应数据
text = requests.post(url, data=pm).text
js = 'decodeData("{0}")'.format(text)
data = ctx.eval(js)
print(data)