美文网首页
Python爬虫获取城市历史AQI数据

Python爬虫获取城市历史AQI数据

作者: 不知名程序员小号 | 来源:发表于2019-01-16 17:26 被阅读0次

一、预期结果:

空气质量历史数据查询网站上可以查询到每个城市的空气质量历史数据。我们的计划是用Python获取并打印某个城市某个月当中每一天的空气质量数据,包括pm2.5pm10SO2等数据。如下图效果:

执行结果

二、爬取数据过程:

1、需要爬取页面
html页面
很显然由网页的url看到,只需要两个参数,一个city一个month,就可以获取这个网页内容。但是由于这些数据是动态加载的,而不是直接出现在原始的html源文件中,所以直接用requests库的get方法是拿不到数据内容的。
2、查找js代码

既然直接get看不到实际显示的数据,那么就很容易想到数据加载是通过js完成的。我们在该页面包含的js代码中去找。通过用chrome浏览器的调试,我发现这些空气质量数据实际是用js代码向下面这个 url 发送post请求获得的。
所以我们的任务就变成用python模拟执行这个post请求。

https://www.aqistudy.cn/historydata/api/historyapi.php
3、解析js代码

js代码里面找到下面的部分,根据getServerData函数名很容易判断数据就是通过这里获取的。

源代码js函数
我们用Chrome浏览器调试,在这里设置断点,刷新页面,中断后逐步调试,就找到了实际获取数据使用的代码,居然隐藏在jquery.min.js文件里面一个很隐蔽的地方,并且明显经过了混淆,代码显示成了一大堆看不懂的数字,在网上找一个js的反混淆工具,解析之后部分代码如下:
反混淆前,chrome调试内容
反混淆后部分js代码
这里已经可以很明显看出post请求的参数:urldata了,我们只要按照这个格式发送一个post请求就解决问题了。
4、用python模拟执行js

第3步已经很接近实现了,但是这里post请求的参数是经过加密的。如果我们要用python来直接获取数据,就需要研究清楚它加密和解密的方法,再用python把这个js代码加密解密的过程重写一下,但是这样很花时间还容易错,完全没有必要。我们用Python的PyExecJs库,只要在Python中直接调用这部分js代码就行了。还是使用原封不动的js代码,使用getParam()将参数加密并上传,获取到服务器的返回数据后,再使用decodeData()将数据解析出来。

三、代码

代码:因为js文件太大就不贴了。

import requests
import execjs
import json


def createParams(city, month, ctx):
    '''由城市名、年月得出经js加密后的post参数,ctx由js代码解析得到'''
    method = 'GETDAYDATA'
    js = 'getEncryptedData("{0}", "{1}", "{2}")'.format(method, city, month)
    params = ctx.eval(js)
    return {'hd': params}


def getResponseData(city, month, ctx):
    '''由城市名、年月向服务器发送post请求并解密返回数据,ctx由js代码解析得到'''
    apiUrl = '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'
    }

    response = requests.post(apiUrl, data=createParams(
        city, month, ctx), headers=headers, timeout=10)
    if response.status_code != 200:
        return None
    # 解析数据
    js = 'decodeData("{0}")'.format(response.text)
    decrypted_data = ctx.eval(js)
    data = json.loads(decrypted_data)
    return data['result']['data']['items']


if __name__ == '__main__':
    # js环境,这里用nodeJS
    node = execjs.get()
    # compile javascript
    ctx = node.compile(open('encryption.js', encoding='utf-8').read())

    city = input('请输入城市名(如: 西安):')
    year = input('请输入年份(如: 2018):')
    month = input('请输入月份(如: 5):').zfill(2)
    items = getResponseData(city, year + month, ctx)

    if items is not None:
        print('\n')
        print('日期\tAQI\tPM2.5\tPM10\tSO2\tNO2\tCO\tO3\t质量等级')
        for item in items:
            print(item['time_point'], end='\t')
            print(item['aqi'], end='\t')
            print(item['pm2_5'], end='\t')
            print(item['pm10'], end='\t')
            print(item['so2'], end='\t')
            print(item['no2'], end='\t')
            print(item['co'], end='\t')
            print(item['o3'], end='\t')
            print(item['quality'])
    else:
        print('数据获取失败!')

相关文章

网友评论

      本文标题:Python爬虫获取城市历史AQI数据

      本文链接:https://www.haomeiwen.com/subject/gabmdqtx.html