中国空气质量在线监测分析平台数据爬取

2024-03-24 07:40

本文主要是介绍中国空气质量在线监测分析平台数据爬取,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

网站:添加链接描述
空气质量在线检测分析平台
这个网站的数据爬取我在网上查过别人的教程,比较繁琐,我有一个比较简单的方法获取数据,现在分享给大家
目标数据:
在这里插入图片描述

找数据接口

1.网站禁用F12和右键 —> 右上角三点 —>更多工具 —> 开发者工具

2.定时器debugger —> 一律不在此处暂停
在这里插入图片描述
如果这样弄之后被网页检测到,刷新即可

3.分析数据接口,找xhr请求,
在这里插入图片描述
接口是https://www.aqistudy.cn/historydata/api/historyapi.php,点击载荷和预览可知,发送请求的数据和接受的数据都是加密的,因此我们需要分析发送数据和接收数据的加密过程,模拟发送请求获取天气数据并解密

发送数据加密

  1. 打xhr断点,

在这里插入图片描述

2.然后在原网页上查询别的城市天气数据,再查南京不会有xhr请求(估计是缓存了)
在这里插入图片描述
它断住了,然后我们就在调用堆栈里分析,看能不能找到加密的函数,走到ajax函数
在这里插入图片描述

之前分析数据接口时可知, 拿数据的xhr请求为post请求,字段名即为:hA4Nse2cT, 由此可知,pKmSFk8就是密文,它是怎么生成 的?
看上面,就是poPBVxzNuafY8Yu函数所以我们要进入 poPBVxzNuafY8Yu 函数并分析加密过程,在上面那一句打上断点,重新发送请求
在这里插入图片描述
我们就进来了这个函数,分析这个函数可知,这个函数最后返回一个对象 这个对象有:
appId —> 写死的 “3c9208efcfb2f5b843eec8d96de6d48a”
method—> 写死的 “GETMONTHDATA”
timestamp—> 时间戳
clienttype —>写死的“WEB”
object—> 也是一个对象,里面的参数我们查询的城市
secret—> hex_md5(传入上面的参数和根据键排序后的oNLhNQ对象),我觉得这个hex_md5就是MD5加密,于是打开nodejs尝试一下
在这里插入图片描述
把这些参数都弄进去
在这里插入图片描述
在这里插入图片描述
由此可以看到,hex_md5就是正宗的MD5加密,没有什么改动的地方
接下来就是base64和aes加密,
在这里插入图片描述
当然我们也需要验证base64有没有被修改过,我验证了一下就是正宗的base64
这里要用到AES.encrypt ,我们进入这个函数看看

    ![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/17c2d69e2a5841679e2297372309dacc.png#pic_center)发现这个加密,key和iv都是从外面传入再MD5加密,并截取对应的长度,把这个aes加密解密都扣到我的js代码中于是我们需要寻找key和iv,我发现就在这个页面的顶部![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/8c9a170b772f41dfbfa574b8b284ba3a.png#pic_center)

把网站的aes加解密扣下来,形成我的完整的模拟加密代码

var CryptoJS=require('crypto-js')
const  ask4u6FbhGV8 = "a0QHmC1Ova5958nC";//AESkey,可自定义
const  asi2hhkBUJbo = "bMu71lHRX6bRmPxU";//密钥偏移量IV,可自定义const  acky6QolJSJi = "dLRSzDrm8xkryEyL";//AESkey,可自定义
const  acixHVhiNqmK = "fex6AA4zRfVrSPmr";//密钥偏移量IV,可自定义const  dskQCqpdBOGo = "hEaIOlrX7tlhAOkz";//DESkey,可自定义
const  dsiqYiQHbZQp = "xMBwDXG1HOubUV04";//密钥偏移量IV,可自定义const  dckCheMkUojW = "oi4aKMxMECWSyTaz";//DESkey,可自定义
const  dciEekKS6Cws = "p2uRrSFcN9oKLrKY";//密钥偏移量IV,可自定义var AES = {encrypt: function(text, key, iv) {var secretkey = (CryptoJS.MD5(key).toString()).substr(16, 16);var secretiv = (CryptoJS.MD5(iv).toString()).substr(0, 16);secretkey = CryptoJS.enc.Utf8.parse(secretkey);secretiv = CryptoJS.enc.Utf8.parse(secretiv);var result = CryptoJS.AES.encrypt(text, secretkey, {iv: secretiv,mode: CryptoJS.mode.CBC,padding: CryptoJS.pad.Pkcs7});return result.toString();},decrypt: function(text, key, iv) {var secretkey = (CryptoJS.MD5(key).toString()).substr(16, 16);var secretiv = (CryptoJS.MD5(iv).toString()).substr(0, 16);secretkey = CryptoJS.enc.Utf8.parse(secretkey);secretiv = CryptoJS.enc.Utf8.parse(secretiv);var result = CryptoJS.AES.decrypt(text, secretkey, {iv: secretiv,mode: CryptoJS.mode.CBC,padding: CryptoJS.pad.Pkcs7});return result.toString(CryptoJS.enc.Utf8);}
};
var gethistory = (function(){function osZ34YC04S(obj){var newObject = {};Object.keys(obj).sort().map(function(key){newObject[key] = obj[key];});return newObject;
}
return function(name){m0fhOhhGL='GETMONTHDATA'var oNLhNQ={city:name}var aMFs = '3c9208efcfb2f5b843eec8d96de6d48a';var cVWG2 = 'WEB';var t5GECZQ = new Date().getTime();var pKmSFk8 = {appId: aMFs,method: m0fhOhhGL,timestamp: t5GECZQ,clienttype: cVWG2,object: oNLhNQ,secret: CryptoJS.MD5(aMFs + m0fhOhhGL + t5GECZQ + cVWG2 + JSON.stringify(osZ34YC04S(oNLhNQ))).toString(CryptoJS.enc.Hex)};console.log(pKmSFk8.secret)pKmSFk8 = CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse(JSON.stringify(pKmSFk8)));pKmSFk8 = AES.encrypt(pKmSFk8, acky6QolJSJi, acixHVhiNqmK);return pKmSFk8;
};
})();

​     然后就是写py request,先用execjs调用js代码模拟加密后,发送密文给对应的API,看能不能获取到对应的加密数据,看获取的是不是与网站获取的相同```python
import requests
import execjs
import json
import requestscookies = {'Hm_lvt_6088e7f72f5a363447d4bafe03026db8': '1704421259','Hm_lpvt_6088e7f72f5a363447d4bafe03026db8': '1704422591',
}headers = {'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7','Accept-Language': 'zh-CN,zh;q=0.9','Connection': 'keep-alive',# 'Cookie': 'Hm_lvt_6088e7f72f5a363447d4bafe03026db8=1704421259; Hm_lpvt_6088e7f72f5a363447d4bafe03026db8=1704422591','Referer': 'https://www.aqistudy.cn/historydata/','Sec-Fetch-Dest': 'document','Sec-Fetch-Mode': 'navigate','Sec-Fetch-Site': 'same-origin','Sec-Fetch-User': '?1','Upgrade-Insecure-Requests': '1','User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36','sec-ch-ua': '"Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"','sec-ch-ua-mobile': '?0','sec-ch-ua-platform': '"Windows"',
}city="南京"with open("test.js", 'r',encoding='utf-8') as f:js_code = f.read()
env=execjs.compile(js_code)
history=env.call("gethistory",city)
print(fuck)
data = {'hA4Nse2cT': history
}
response = requests.post('https://www.aqistudy.cn/historydata/api/historyapi.php', cookies=cookies, headers=headers, data=data)
encrypted_data=response.text
print(encrypted_data)

这里我们选天津,看我们py拿到的数据跟网站发送拿到的是不是一样的,

网站的:在这里插入图片描述

我拿到的:
在这里插入图片描述

可以发现是一样的,所以至此完成了发送数据的模拟加密

接收数据的解密:


接下来我们探索怎么解密拿到的数据
在这里插入图片描述
这时dGHdO还是密文,
在这里插入图片描述
这里已是明文,
所以可知,dxvERkeEvHbS是解密函数, 然后我们再发一次请求,进入这个函数单步执行,
在这里插入图片描述
在这一页面上方,我们也是找到了解密函数,可知,把数据经过base64 -> des ->aes ->base64 这四次解密过程即可,
于是我们在nodejs上模拟解密,加上之前的模拟加密代码,
构成了这一次网站js逆向的完整js代码,需要安装crypto-js
没有则运行,npm i crypto-js

完整的js模拟加解密代码

var CryptoJS=require('crypto-js')
const  ask4u6FbhGV8 = "a0QHmC1Ova5958nC";//AESkey,可自定义
const  asi2hhkBUJbo = "bMu71lHRX6bRmPxU";//密钥偏移量IV,可自定义const  acky6QolJSJi = "dLRSzDrm8xkryEyL";//AESkey,可自定义
const  acixHVhiNqmK = "fex6AA4zRfVrSPmr";//密钥偏移量IV,可自定义const  dskQCqpdBOGo = "hEaIOlrX7tlhAOkz";//DESkey,可自定义
const  dsiqYiQHbZQp = "xMBwDXG1HOubUV04";//密钥偏移量IV,可自定义const  dckCheMkUojW = "oi4aKMxMECWSyTaz";//DESkey,可自定义
const  dciEekKS6Cws = "p2uRrSFcN9oKLrKY";//密钥偏移量IV,可自定义var DES = {encrypt: function(text, key, iv){var secretkey = (CryptoJS.MD5(key).toString()).substr(0, 16);var secretiv = (CryptoJS.MD5(iv).toString()).substr(24, 8);secretkey = CryptoJS.enc.Utf8.parse(secretkey);secretiv = CryptoJS.enc.Utf8.parse(secretiv);var result = CryptoJS.DES.encrypt(text, secretkey, {iv: secretiv,mode: CryptoJS.mode.CBC,padding: CryptoJS.pad.Pkcs7});return result.toString();},decrypt: function(text, key, iv){var secretkey = (CryptoJS.MD5(key).toString()).substr(0, 16);var secretiv = (CryptoJS.MD5(iv).toString()).substr(24, 8);secretkey = CryptoJS.enc.Utf8.parse(secretkey);secretiv = CryptoJS.enc.Utf8.parse(secretiv);var result = CryptoJS.DES.decrypt(text, secretkey, {iv: secretiv,mode: CryptoJS.mode.CBC,padding: CryptoJS.pad.Pkcs7});return result.toString(CryptoJS.enc.Utf8);}
};var AES = {encrypt: function(text, key, iv) {var secretkey = (CryptoJS.MD5(key).toString()).substr(16, 16);var secretiv = (CryptoJS.MD5(iv).toString()).substr(0, 16);secretkey = CryptoJS.enc.Utf8.parse(secretkey);secretiv = CryptoJS.enc.Utf8.parse(secretiv);var result = CryptoJS.AES.encrypt(text, secretkey, {iv: secretiv,mode: CryptoJS.mode.CBC,padding: CryptoJS.pad.Pkcs7});return result.toString();},decrypt: function(text, key, iv) {var secretkey = (CryptoJS.MD5(key).toString()).substr(16, 16);var secretiv = (CryptoJS.MD5(iv).toString()).substr(0, 16);secretkey = CryptoJS.enc.Utf8.parse(secretkey);secretiv = CryptoJS.enc.Utf8.parse(secretiv);var result = CryptoJS.AES.decrypt(text, secretkey, {iv: secretiv,mode: CryptoJS.mode.CBC,padding: CryptoJS.pad.Pkcs7});return result.toString(CryptoJS.enc.Utf8);}
};var gethistory = (function(){function osZ34YC04S(obj){var newObject = {};Object.keys(obj).sort().map(function(key){newObject[key] = obj[key];});return newObject;
}
return function(name){m0fhOhhGL='GETMONTHDATA'var oNLhNQ={city:name}var aMFs = '3c9208efcfb2f5b843eec8d96de6d48a';var cVWG2 = 'WEB';var t5GECZQ = new Date().getTime();var pKmSFk8 = {appId: aMFs,method: m0fhOhhGL,timestamp: t5GECZQ,clienttype: cVWG2,object: oNLhNQ,secret: CryptoJS.MD5(aMFs + m0fhOhhGL + t5GECZQ + cVWG2 + JSON.stringify(osZ34YC04S(oNLhNQ))).toString(CryptoJS.enc.Hex)};console.log(pKmSFk8.secret)pKmSFk8 = CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse(JSON.stringify(pKmSFk8)));pKmSFk8 = AES.encrypt(pKmSFk8, acky6QolJSJi, acixHVhiNqmK);return pKmSFk8;
};
})();
function get_data(data) {data=CryptoJS.enc.Base64.parse(data).toString(CryptoJS.enc.Utf8)data = DES.decrypt(data, dskQCqpdBOGo, dsiqYiQHbZQp);data = AES.decrypt(data, ask4u6FbhGV8, asi2hhkBUJbo);data=CryptoJS.enc.Base64.parse(data).toString(CryptoJS.enc.Utf8)return JSON.parse(data)
}

然后是构造请求和解析解密后数据的py代码:
执行js代码要PyExecJS2库,没有则执行,pip install PyExecJS2

完整的py代码


import execjs
import requests
# 要用自己的cookie
cookies = {'Hm_lvt_6088e7f72f5a363447d4bafe03026db8': '1704421259','Hm_lpvt_6088e7f72f5a363447d4bafe03026db8': '1704422591',
}headers = {'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7','Accept-Language': 'zh-CN,zh;q=0.9','Connection': 'keep-alive','Referer': 'https://www.aqistudy.cn/historydata/','Sec-Fetch-Dest': 'document','Sec-Fetch-Mode': 'navigate','Sec-Fetch-Site': 'same-origin','Sec-Fetch-User': '?1','Upgrade-Insecure-Requests': '1','User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36','sec-ch-ua': '"Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"','sec-ch-ua-mobile': '?0','sec-ch-ua-platform': '"Windows"',
}# 要查什么城市自己改这个参数即可
city="天津"
# 读取js代码,构造js执行环境
with open("gethistory.js", 'r', encoding='utf-8') as f:js_code = f.read()
env=execjs.compile(js_code)
query=env.call("gethistory",city)
data = {'hA4Nse2cT': query
}
response = requests.post('https://www.aqistudy.cn/historydata/api/historyapi.php', cookies=cookies, headers=headers, data=data)
encrypted_data=response.text
result=(env.call("get_data",encrypted_data))
print(result)
time_datas=result['items']
for time_data in time_datas:print(time_data)

结果:
在这里插入图片描述
如果本文对您有帮助,请点个赞吧

这篇关于中国空气质量在线监测分析平台数据爬取的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:https://blog.csdn.net/oyf3085227433/article/details/135415616
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/840902

相关文章

IDEA下"File is read-only"可能原因分析及"找不到或无法加载主类"的问题

《IDEA下Fileisread-only可能原因分析及找不到或无法加载主类的问题》:本文主要介绍IDEA下Fileisread-only可能原因分析及找不到或无法加载主类的问题,具有很好的参... 目录1.File is read-only”可能原因2.“找不到或无法加载主类”问题的解决总结1.File

8种快速易用的Python Matplotlib数据可视化方法汇总(附源码)

《8种快速易用的PythonMatplotlib数据可视化方法汇总(附源码)》你是否曾经面对一堆复杂的数据,却不知道如何让它们变得直观易懂?别慌,Python的Matplotlib库是你数据可视化的... 目录引言1. 折线图(Line Plot)——趋势分析2. 柱状图(Bar Chart)——对比分析3

Spring Boot 整合 Redis 实现数据缓存案例详解

《SpringBoot整合Redis实现数据缓存案例详解》Springboot缓存,默认使用的是ConcurrentMap的方式来实现的,然而我们在项目中并不会这么使用,本文介绍SpringB... 目录1.添加 Maven 依赖2.配置Redis属性3.创建 redisCacheManager4.使用Sp

Dubbo之SPI机制的实现原理和优势分析

《Dubbo之SPI机制的实现原理和优势分析》:本文主要介绍Dubbo之SPI机制的实现原理和优势,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Dubbo中SPI机制的实现原理和优势JDK 中的 SPI 机制解析Dubbo 中的 SPI 机制解析总结Dubbo中

Python Pandas高效处理Excel数据完整指南

《PythonPandas高效处理Excel数据完整指南》在数据驱动的时代,Excel仍是大量企业存储核心数据的工具,Python的Pandas库凭借其向量化计算、内存优化和丰富的数据处理接口,成为... 目录一、环境搭建与数据读取1.1 基础环境配置1.2 数据高效载入技巧二、数据清洗核心战术2.1 缺失

C#继承之里氏替换原则分析

《C#继承之里氏替换原则分析》:本文主要介绍C#继承之里氏替换原则,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录C#里氏替换原则一.概念二.语法表现三.类型检查与转换总结C#里氏替换原则一.概念里氏替换原则是面向对象设计的基本原则之一:核心思想:所有引py

Python处理超大规模数据的4大方法详解

《Python处理超大规模数据的4大方法详解》在数据的奇妙世界里,数据量就像滚雪球一样,越变越大,从最初的GB级别的小数据堆,逐渐演变成TB级别的数据大山,所以本文我们就来看看Python处理... 目录1. Mars:数据处理界的 “变形金刚”2. Dask:分布式计算的 “指挥家”3. CuPy:GPU

使用Vue-ECharts实现数据可视化图表功能

《使用Vue-ECharts实现数据可视化图表功能》在前端开发中,经常会遇到需要展示数据可视化的需求,比如柱状图、折线图、饼图等,这类需求不仅要求我们准确地将数据呈现出来,还需要兼顾美观与交互体验,所... 目录前言为什么选择 vue-ECharts?1. 基于 ECharts,功能强大2. 更符合 Vue

Java如何根据word模板导出数据

《Java如何根据word模板导出数据》这篇文章主要为大家详细介绍了Java如何实现根据word模板导出数据,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... pom.XML文件导入依赖 <dependency> <groupId>cn.afterturn</groupId>

Python实现获取带合并单元格的表格数据

《Python实现获取带合并单元格的表格数据》由于在日常运维中经常出现一些合并单元格的表格,如果要获取数据比较麻烦,所以本文我们就来聊聊如何使用Python实现获取带合并单元格的表格数据吧... 由于在日常运维中经常出现一些合并单元格的表格,如果要获取数据比较麻烦,现将将封装成类,并通过调用list_exc