自從有娃之後,好久沒有時間好好摸一摸編程了,週末的時候正好出門溜娃,就想到了空氣質量的問題,雖然有APP啊上海發佈啊之類的能夠查,但我恰恰就是手賤要爬米帝領事館的數據。(PM2.5監測網上的監測站點更多,但數據彷佛老是低於美國領事館公佈的AQI)python
是時候撿起python了!因而回到家,等孩子他媽哄睡了寶寶以後(媽媽真辛苦),我開始研究怎麼實現我想要的功能。首先我上了一下數據監測頁面http://aqicn.org/city/shanghai/quanshipingjun/cn/正則表達式
右側就是咱們想要的每一個監測點的數據,然而這是個動態網頁,讓我一個只學過怎麼爬靜態網頁的人一臉懵逼……一番搜刮學習以後我找到了解決之道——一切沒解決的問題均可以先用F12解決!編程
打開瀏覽器的F12,切換到Network標籤,觀察下面的JS或者XHR,試着拖動那張地圖,看看下方的列表有沒有變化,果真,每拖動一次XHR下就會生成1條記錄,點進去一看,嘿~嘿~嘿~這不就是咱們要的東西嗎?json
點擊左側的Headers還能看到動態網頁的網址:api
咱們把這個網址複製下來,並解析,形如:瀏覽器
https://api.waqi.info/mapq/bounds/?bounds=31.043521630684204,121.19293212890624,31.466153715024294,121.84112548828125&inc=placeholders&k=_2Y2EzVxxIDVsfIydASBRWXmldZA4+LREbFkY3ZQ==&_=1497098578289微信
好長……是否是有點累覺不愛了?不過仔細看一下的話,bounds=後面的東西像不像座標?這應該指的就是這張圖的邊界。多抓幾回就會發現,每次不同的除了座標就只有最後那串數字,每次都不同。我猜測這個會不會和時間有關呢?因而我每隔10秒移動一次地圖,發現果真,每十秒鐘差很少增長10000單位,那就是時間戳沒跑了(其實後來發現,及時每次提交的時間戳即便一毛同樣數據也會自動刷新……)。知道了這些以後就是寫網頁解析的代碼了,這個就沒太大難度了。數據結構
1 url ="https://api.waqi.info/mapq/bounds/?bounds=31.064698120353743,121.201171875,31.487235582017444,121.84936523437499&inc=placeholders&k=_2Y2EzVxxIDVsfIydASBRWXmldZA4+LREbFkY3ZQ==&_=1497098578289" #動態網頁網址 2 3 user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)' 4 headers = { 'User-Agent' : user_agent } #頭文件 5 6 try: 7 request = urllib.request.Request(url,headers = headers) 8 response = urllib.request.urlopen(request) #獲取數據 9 except urllib.error.URLError as e: 10 if hasattr(e,"code"): 11 print(e.code) 12 if hasattr(e,"reason"): 13 print(e.reason) 14 15 aqi_trans=json.load(response) #json數據的解析 16 """ 17 aqi_trans[0]數據結構: 18 {'aqi': '68', 19 'city': 'Yangpu Sipiao, Shanghai (上海楊浦四漂)', 20 'idx': 702, 21 'img': '_c_azCs5IzEvPSMzUfzZ3xbOty57Onv1sTxMA', 22 'lat': 31.2659, 23 'lon': 121.536, 24 'pol': 'pm25', 25 'stamp': 1497160800, 26 'tz': '+0800', 27 'utime': '2017-06-11 14:00:00', 28 'x': '482'} 29 共11個數據點 30 """
這裏咱們獲得的aqi_trans是一個列表,列表中的每一個元素都是一個字典,字典的內容就是監測站的名稱啦,AQI指數啦,經緯度啦之類的信息。app
以後就是簡單的數據整理,這就交給pandas了。這裏我選擇了4個數據,idx應該是監測點的索引(但隔一段時間會換,不過序列狀況不變),utime就是監測的時間,city是監測點的名字,用正則表達式去掉括號,AQI就是空氣質量指數啦~不過AQI只有數值,沒有等級,但這個很簡單,一個cut函數就解決了。函數
1 aqi_table=pd.DataFrame(columns=['Idx','Time','Site','AQI']) 2 3 for i in range(len(aqi_trans)): 4 pos_idx=int(aqi_trans[i]['idx']) 5 record_time=aqi_trans[i]['utime'] 6 site=re.findall(r'上海\S*(?=\))',aqi_trans[i]['city'])[0] #正則,提取括號裏的文字 7 aqi=int(aqi_trans[i]['aqi']) 8 aqi_table=aqi_table.append({'Idx':pos_idx,'Time':record_time,'Site':site,'AQI':aqi},ignore_index=True) 9 aqi_table=aqi_table.sort_values('Idx',ascending=True) 10 aqi_table=aqi_table.set_index(pd.Series(range(0,11))) 11 aqi_table['Level']=pd.cut(aqi_table['AQI'],[0,50,100,150,200,300,10000],labels=[u'優',u'良',u'輕度污染',u'中度污染',u'重度污染',u'嚴重污染']) #打標籤
最後我決定寫一個函數,把獲取AQI的功能打包一下,而後定時運行一下這個函數(領事館數據貌似每小時更新一次),監測的數據就放進一個csv文件裏,方便後續處理。彙總一下代碼以下:
import urllib #python 3中urllib和urllib2合併了,不少語法不一樣了 import json import re import pandas as pd def Get_AQI(t): url ="https://api.waqi.info/mapq/bounds/?bounds=31.064698120353743,121.201171875,31.487235582017444,121.84936523437499&inc=placeholders&k=_2Y2EzVxxIDVsfIydASBRWXmldZA4+LREbFkY3ZQ==&_=%d"%(t) #這裏用的是動態時間戳,但時間戳好像並不影響數據的實時性,那隻要隔一段時間提交一次一樣的網址就好了 user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)' headers = { 'User-Agent' : user_agent } try: request = urllib.request.Request(url,headers = headers) response = urllib.request.urlopen(request) except urllib.error.URLError as e: if hasattr(e,"code"): print(e.code) if hasattr(e,"reason"): print(e.reason) aqi_trans=json.load(response) aqi_table=pd.DataFrame(columns=['Idx','Time','Site','AQI']) for i in range(len(aqi_trans)): pos_idx=int(aqi_trans[i]['idx']) record_time=aqi_trans[i]['utime'] site=re.findall(r'上海\S*(?=\))',aqi_trans[i]['city'])[0] aqi=int(aqi_trans[i]['aqi']) aqi_table=aqi_table.append({'Idx':pos_idx,'Time':record_time,'Site':site,'AQI':aqi},ignore_index=True) aqi_table=aqi_table.sort_values('Idx',ascending=True) aqi_table=aqi_table.set_index(pd.Series(range(0,11))) aqi_table['Level']=pd.cut(aqi_table['AQI'],[0,50,100,150,200,300,10000],\ labels=[u'優',u'良',u'輕度污染',u'中度污染',u'重度污染',u'嚴重污染']) #分段打標籤 return aqi_table import time sleep_time=1800 #30分鐘運行一次 if __name__ == '__main__': while True: now_time=int(time.time()*1000) aqi_database=Get_AQI(now_time) aqi_database.to_csv('D:\\AQI\\aqi_database.csv',mode='a+') #記錄csv文件,a+爲追加寫入 print(time.ctime()) print(Get_AQI(now_time)) time.sleep(sleep_time)
終於磕磕絆絆地碼完了,發現本身果真忘了很多東西,python3和python2也有不少不一樣點了,還須要學習一個,提升姿式水平啊~心滿意足之餘,我還YY了一下把這功能放在一個樹莓派裏,而後定時給微信推送,白日夢真是美妙極了~
啊~白日夢……