网页抓取解密(pm2.5AQI空气质量数据网站的参数加密解密(组图) )
优采云 发布时间: 2022-01-06 02:10网页抓取解密(pm2.5AQI空气质量数据网站的参数加密解密(组图)
)
前言:
pm2.5 数据源较多,但历史数据较少。很多网站都不是很完整。本次AQI空气质量数据网站的数据属于同一类别网站,相当全面,界面简洁明了。刚开始爬这个网站的时候,看到网页结构很简单,以为一个简单的构建请求就可以爬取数据。没想到,打开开发者工具后,发现它的日常数据是通过js加解密的方式加载到页面上的。
遇到这样的网站,第一反应就是通过selenium用浏览器脚本一个一个加载每个页面。但是如果爬取的数据量很大,比如全国。一一加载的时间开销比较大。第一次爬的时候用selenium通过多线程爬取。即使使用多线程,速度仍然不能令人满意。花了两个小时才把所有的数据记录下来。所以这不是一个完美的解决方案。
分析过程:
一个网站,为了加载页面上的数据,无非是js或者一些模板。我们先来一一分析网站的数据加载过程,如图
getServerData()获取数据并通过showTable()加载到页面后,总体来说是一个比较正常的操作。但是在当前文件中找不到getServerData()代码的具体实现。我们继续寻找其他js文件:通过搜索,我们在jquery.min.js中找到了类似的字段。
但不要高兴得太早。我们发现,
这个方法在js代码中混淆了。现在我正在寻找一个反混淆工具。找出真正的代码。
我们将代码从 eval 复制到屏幕截图中的注释部分。然后找一个反混淆工具。我用过这个:
然后得到原来的js代码,复制保存成文本文件
分析js文件。. . . (下图是完整的getServerData())
我们发现请求参数和响应已经被加密和解密。看到这里,我们可以使用execjs调用加解密方法来构造请求。你可以得到我们需要的数据。
参数加密过程:
从图中可以看出,过程比较繁琐,不需要再次使用python来实现。最好直接调用js方法。
响应解密过程:
base64解码》DES解码》AES解码》base64解码
这些方法可以在这个js中找到。有兴趣的可以慢慢了解。
下面是一个完整的爬虫,爬取一个城市的所有历史数据:(如果js代码太多,放在github上) 代码地址:
import json
from lxml import etree
import execjs
import requests
# 获取一个城市所有的历史数据 by lczCrack qq1124241615
# 加密参数
def encryption_params(city, date_time, ctx):
method = 'GETDAYDATA'
js = 'getEncryptedData("{0}", "{1}", "{2}")'.format(method, city, date_time)
return ctx.eval(js)
# 解码response对象
def decode_info(info, ctx):
js = 'decodeData("{0}")'.format(info)
data = ctx.eval(js)
data = json.loads(data)
return data
def get_response(params):
url = 'https://www.aqistudy.cn/historydata/api/historyapi.php'
headers = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Accept-Encoding': 'gzip, deflate',
'Accept-Language': 'zh-CN,zh;q=0.8',
'Content-Type': 'application/x-www-form-urlencoded',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36'
}
data = {
'hd': params
}
html_info = requests.post(url, data=data)
return html_info.text
def get_city():
url = 'https://www.aqistudy.cn/historydata/'
html_info = requests.get(url)
html = etree.HTML(html_info.text) # 初始化生成一个XPath解析对象
items = html.xpath('//div[@class="all"]//a/text()')
return items
def get_all_info_by_city(city):
years = [str(i + 2013) for i in range(7)]
month = [str(i if i > 9 else '0' + str(i)) for i in range(1, 13)]
node = execjs.get()
ctx = node.compile(open('decrypt.js', encoding='utf-8').read())
for y in years:
for m in month:
date_time = y + m # 201805
html_info = get_response(encryption_params(city, date_time, ctx))
item = decode_info(html_info, ctx)
for i in item['result']['data']['items']:
print(i)
if __name__ == '__main__':
get_all_info_by_city('北京')
结果: