背景
最近在計劃明年從北京rebase到深圳去,所以最近在看深圳的各個方面。去年在深圳呆過一段時間,印象最深的是,深圳總是突然就下雨,還下好大的雨。對於我這種從小在南方長大但是後面又在北京呆了2年多的人來說,熟悉而又無奈。
今天早上本來想隨便瀏覽瀏覽一個天氣網站,看看深圳的歷史天氣如何的,但是,一不小心發現,這家網站竟然直接能用API來抓資料,這~~~還不抓一波,省的自己一個月一個月地看。
先上最後的效果圖:
所有的code都在我的GitHub上:boydfd
下面從幾個方面講一講我是怎麼做的:
- 爬取資料
- 用pandas顯示資料
- 功能擴充套件
- 遇到的坑
爬取資料
先是在http://tianqi.2345.com上面瀏覽了一下深圳的6月份天氣。然後發現點切換月份的時候,網址沒有變,那應該有請求API吧,看看這個API長啥樣吧。
發現返回值就是純JS程式碼,那就解析一下吧:
- 去掉
var =
和最後的;
。 - 用到
demjson
解析成Python的List[Dict]
物件。 - 轉成pandas的
DataFrame
- 加上我們的date欄位
date = '201905'
weather = requests.get('http://tianqi.2345.com/t/wea_history/js/{date}/59493_{date}.js'.format(date=date)).text.split('=')[1][:-1]
weather = demjson.decode(weather)['tqInfo']
df = pd.DataFrame(weather)
df['month'] = date
結果是這樣的:
用Pandas顯示資料
太多雨天
我們可以看到,有各種雷陣雨啊,陰轉雨啊,雨轉陰之類的,這樣看到的天氣太雜了,所以我就統一了一下,按照雨、多雲、陰、晴的順序來排序,先出出現的關鍵詞優先順序更高。
寫一個函式來處理之:
rain = '雨'
rain_index = ' ' + rain
cloudy = '多雲'
cloudy_index = ' ' + cloudy
overcast = '陰'
overcast_index = ' ' + overcast
sunny = '晴'
sunny_index = ' ' + sunny
def weath_category(row):
tianqi = row['tianqi']
if tianqi.find(rain) != -1:
return rain_index
if tianqi.find(overcast) != -1:
return overcast_index
if tianqi.find(cloudy) != -1:
return cloudy_index
return sunny_index
多個月的資料
一個月的資料不夠啊,我們想要很多個月的資料,那就寫得函式來生成月份吧。
def date_generate(start, end):
start = datetime.strptime(start, '%Y%m')
end = datetime.strptime(end, '%Y%m')
while True:
next_start = start + relativedelta(months=1)
yield start.strftime('%Y%m')
if next_start > end:
break
start = next_start
畫圖
分好類,爬了多個月份的資料,就剩最終的畫圖部分了。使用Pandas提供給我們的函式,可以很容易就畫出圖來。
def plot_weather(start, end):
df = read_weather(start, end).dropna().reset_index()
df['weather'] = df.apply(weath_category, axis=1)
from pylab import rcParams
rcParams['figure.figsize'] = 40, 10
weather_df = df.groupby(['month', 'weather']).aqi.count().unstack().reset_index()
weather_df.plot.bar(x='month', y=[rain_index, overcast_index, cloudy_index, sunny_index])
功能擴充套件
現在只能收集到一個月的資料,想收集多個月的資料,還都自己去頁面上找城市代表的code是啥,太低效了。
這個網站這麼容易爬,那就再試試能不能找到呼叫code的API。
啊哦,一不小心找到了所有的code,哈哈哈。
那就在JS裡面提取一下。
- 先把所有的JS程式碼都複製到瀏覽器的console裡, 結果長這樣:
- 將其轉換成字串。
provqx.flatMap(a => a).join('|')
- 在Python裡處理它。
def line_to_city_code(line):
return line.split(' ')[1].split('-')
def get_city_to_code():
city_code_list = list(map(line_to_city_code, city_code.split('|')))
return {city_code[0]: city_code[1] for city_code in city_code_list if len(city_code) == 2}
這樣我們就拿到所有的code了,只需要輸入城市,開始時間,結束時間,一張漂亮的圖就出來了,我還寫了個類稍微封裝了一下,只需要這樣就能使用了:
Weather('深圳').plot_weather('201701', '201906')
遇到的坑
以前在電腦裡面處理過一次,就是matplotlib畫圖中文亂碼的事情,這次換了新電腦又碰到了。所以又搞了一次,
大概的步驟可以參考https://www.jianshu.com/p/8ed59ac76c06
我為了以防下次再經歷一次,就寫了個指令碼自動處理這件事,目前只支援macOS和Python3。
指令碼也在我的GitHub:bash,
直接執行下面的bash指令碼就可以解決這個問題:
curl -o- https://raw.githubusercontent.com/boydfd/one_step_solve/master/matplotlib_chinese.sh | bash