Python爬蟲獲取城市歷史AQI數(shù)據(jù)

一净嘀、預(yù)期結(jié)果:

空氣質(zhì)量歷史數(shù)據(jù)查詢網(wǎng)站上可以查詢到每個(gè)城市的空氣質(zhì)量歷史數(shù)據(jù)报咳。我們的計(jì)劃是用Python獲取并打印某個(gè)城市某個(gè)月當(dāng)中每一天的空氣質(zhì)量數(shù)據(jù),包括pm2.5挖藏,pm10暑刃,SO2等數(shù)據(jù)。如下圖效果:

執(zhí)行結(jié)果

二膜眠、爬取數(shù)據(jù)過程:

1岩臣、需要爬取頁面

html頁面

很顯然由網(wǎng)頁的url看到,只需要兩個(gè)參數(shù)宵膨,一個(gè)city一個(gè)month架谎,就可以獲取這個(gè)網(wǎng)頁內(nèi)容。但是由于這些數(shù)據(jù)是動態(tài)加載的辟躏,而不是直接出現(xiàn)在原始的html源文件中狐树,所以直接用requests庫的get方法是拿不到數(shù)據(jù)內(nèi)容的。

2鸿脓、查找js代碼

既然直接get看不到實(shí)際顯示的數(shù)據(jù)抑钟,那么就很容易想到數(shù)據(jù)加載是通過js完成的。我們在該頁面包含的js代碼中去找野哭。通過用chrome瀏覽器的調(diào)試在塔,我發(fā)現(xiàn)這些空氣質(zhì)量數(shù)據(jù)實(shí)際是用js代碼向下面這個(gè) url 發(fā)送post請求獲得的。
所以我們的任務(wù)就變成用python模擬執(zhí)行這個(gè)post請求拨黔。

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

js代碼里面找到下面的部分,根據(jù)getServerData函數(shù)名很容易判斷數(shù)據(jù)就是通過這里獲取的篱蝇。

源代碼js函數(shù)

我們用Chrome瀏覽器調(diào)試贺待,在這里設(shè)置斷點(diǎn),刷新頁面零截,中斷后逐步調(diào)試麸塞,就找到了實(shí)際獲取數(shù)據(jù)使用的代碼,居然隱藏在jquery.min.js文件里面一個(gè)很隱蔽的地方涧衙,并且明顯經(jīng)過了混淆哪工,代碼顯示成了一大堆看不懂的數(shù)字,在網(wǎng)上找一個(gè)js的反混淆工具弧哎,解析之后部分代碼如下:
反混淆前雁比,chrome調(diào)試內(nèi)容

反混淆后部分js代碼

這里已經(jīng)可以很明顯看出post請求的參數(shù):urldata了,我們只要按照這個(gè)格式發(fā)送一個(gè)post請求就解決問題了撤嫩。

4偎捎、用python模擬執(zhí)行js

第3步已經(jīng)很接近實(shí)現(xiàn)了,但是這里post請求的參數(shù)是經(jīng)過加密的。如果我們要用python來直接獲取數(shù)據(jù)茴她,就需要研究清楚它加密和解密的方法寻拂,再用python把這個(gè)js代碼加密解密的過程重寫一下,但是這樣很花時(shí)間還容易錯败京,完全沒有必要兜喻。我們用Python的PyExecJs庫,只要在Python中直接調(diào)用這部分js代碼就行了赡麦。還是使用原封不動的js代碼朴皆,使用getParam()將參數(shù)加密并上傳,獲取到服務(wù)器的返回?cái)?shù)據(jù)后泛粹,再使用decodeData()將數(shù)據(jù)解析出來遂铡。

三、代碼

代碼:因?yàn)閖s文件太大就不貼了晶姊。

import requests
import execjs
import json


def createParams(city, month, ctx):
    '''由城市名扒接、年月得出經(jīng)js加密后的post參數(shù),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):
    '''由城市名、年月向服務(wù)器發(fā)送post請求并解密返回?cái)?shù)據(jù),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
    # 解析數(shù)據(jù)
    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環(huán)境们衙,這里用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質(zhì)量等級')
        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('數(shù)據(jù)獲取失敗!')

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末钾怔,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子蒙挑,更是在濱河造成了極大的恐慌宗侦,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,576評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件忆蚀,死亡現(xiàn)場離奇詭異矾利,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)馋袜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評論 3 399
  • 文/潘曉璐 我一進(jìn)店門男旗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人欣鳖,你說我怎么就攤上這事察皇。” “怎么了观堂?”我有些...
    開封第一講書人閱讀 168,017評論 0 360
  • 文/不壞的土叔 我叫張陵让网,是天一觀的道長。 經(jīng)常有香客問我师痕,道長,這世上最難降的妖魔是什么而账? 我笑而不...
    開封第一講書人閱讀 59,626評論 1 296
  • 正文 為了忘掉前任胰坟,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘笔横。我一直安慰自己竞滓,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,625評論 6 397
  • 文/花漫 我一把揭開白布吹缔。 她就那樣靜靜地躺著商佑,像睡著了一般。 火紅的嫁衣襯著肌膚如雪厢塘。 梳的紋絲不亂的頭發(fā)上茶没,一...
    開封第一講書人閱讀 52,255評論 1 308
  • 那天,我揣著相機(jī)與錄音晚碾,去河邊找鬼抓半。 笑死,一個(gè)胖子當(dāng)著我的面吹牛格嘁,可吹牛的內(nèi)容都是我干的笛求。 我是一名探鬼主播,決...
    沈念sama閱讀 40,825評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼糕簿,長吁一口氣:“原來是場噩夢啊……” “哼探入!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起懂诗,我...
    開封第一講書人閱讀 39,729評論 0 276
  • 序言:老撾萬榮一對情侶失蹤蜂嗽,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后响禽,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體徒爹,經(jīng)...
    沈念sama閱讀 46,271評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,363評論 3 340
  • 正文 我和宋清朗相戀三年芋类,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了隆嗅。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,498評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡侯繁,死狀恐怖胖喳,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情贮竟,我是刑警寧澤丽焊,帶...
    沈念sama閱讀 36,183評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站咕别,受9級特大地震影響技健,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜惰拱,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,867評論 3 333
  • 文/蒙蒙 一雌贱、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦欣孤、人聲如沸馋没。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽篷朵。三九已至,卻和暖如春婆排,著一層夾襖步出監(jiān)牢的瞬間声旺,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評論 1 272
  • 我被黑心中介騙來泰國打工泽论, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留艾少,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,906評論 3 376
  • 正文 我出身青樓翼悴,卻偏偏與公主長得像缚够,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子鹦赎,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,507評論 2 359

推薦閱讀更多精彩內(nèi)容