如何用 Python 和 API 收集與分析網絡數據?

本文以一款阿里雲市場歷史天氣查詢產品爲例,爲你逐步介紹如何用 Python 調用 API 收集、分析與可視化數據。但願你觸類旁通,輕鬆應對從此的 API 數據收集與分析任務。html

雷同

上週的研究生課,學生分組展現實踐環節第二次做業,主題是利用 API 獲取、分析與可視化數據。python

你們作的內容,確實五花八門。git

例如這個組,調查對象是動畫片《小豬佩奇》(英文名 「Peppa Pig」,又譯做《粉紅豬小妹》)。這部片子聽說最近很火。github

猜猜看,下面這一組調查對象是什麼?編程

沒錯,是《權力的遊戲》(Game of Thrones)。一部很好看的美劇。json

主題豐富多彩,作得有聲有色。api

做爲老師,我在下面,應該很開心吧?瀏覽器

不,我簡直啼笑皆非。bash

14個組中,有一多半都和他們同樣,作的是維基百科頁面訪問量分析。微信

爲何會這樣呢?

由於我在佈置做業的時候,很貼心地給了一個樣例,是我以前寫的一篇教程《如何用R和API免費獲取Web數據?》。

因而,他們就都用 R 語言,來分析維基百科頁面訪問量了。

這些同窗是否是太懶惰了?

聽了他們的講述,我發覺,其中很多同窗,是很是想作些新東西的。

他們找了國內若干個雲市場,去找 API 產品。

其中要價太高的 API ,被他們自動過濾了。

可即適合練手的低價或免費 API ,也很多。

問題是,他們花了很長時間,也沒能搞定

考慮到做業展現日程迫近,他們只好按照個人教程,去用 R 分析維基百科了。

因而,多組做業,都雷同。

講到這裏,他們一副很差意思的表情。

我卻發覺,這裏蘊藏着一個問題。

幾乎全部國內雲市場的 API 產品,都有豐富的文檔。很多還乾脆給出了各類編程語言對應調用代碼。

既然示例代碼都有了,爲何你還作不出來呢?

下課後,我讓有疑問的同窗留下,我帶着他們實際測試了一款 API 產品,嘗試找到讓他們遭遇困境的緣由。

市場

咱們嘗試的,是他們找到的阿里雲市場的一款 API 產品,提供天氣數據。

它來自於易源數據,連接在這裏

這是一款收費 API ,100次調用的價格爲1分錢。

做爲做業練習,100次調用已經足夠了。

這價格,他們表示能夠接受。

我本身走了一遍流程。

點擊「當即購買」按鈕。

你會被引領到付費頁面。若是你沒有登陸,能夠根據提示用淘寶帳號登陸。

支付1分錢之後,你會看到以下的成功提示。

以後,系統會提示給你一些很是重要的信息。

注意上圖中標紅的字段。

這是你的AppCode,是後面你調用 API 接口獲取數據,最爲重要的身份認證手段,請點擊「複製」按鈕把它存儲下來。

點擊上圖中的商品名稱連接,回到產品介紹的頁面。

這個產品的 API 接口,提供多種數據獲取功能。

學生們嘗試利用的,是其中「利用id或地名查詢歷史天氣」一項。

請注意這張圖裏,有幾樣重要信息:

  • 調用地址:這是咱們訪問 API 的基本信息。就好像你要去見朋友,總得知道見面的地址在哪裏;
  • 請求方式:本例中的 GET ,是利用 HTTP 協議請求傳遞數據的主要形式之一;
  • 請求參數:這裏你要提供兩個信息給 API 接口,一是「地區名稱」或者「地區id」(二選一),二是月份數據。需注意格式和可供選擇的時間範圍。

咱們往下翻頁,會看到請求示例。

默認的請求示例,是最簡單的 curl 。

若是你的操做系統裏面已經安裝了 curl (沒有安裝的話,能夠點擊這個連接,尋找對應的操做系統版本下載安裝),嘗試把上圖中 curl 開頭的那一行代碼拷貝下來,複製到文本編輯器裏面。

就像這樣:

curl -i -k --get --include 'https://ali-weather.showapi.com/weatherhistory?area=%E4%B8%BD%E6%B1%9F&areaid=101291401&month=201601'  -H 'Authorization:APPCODE 你本身的AppCode'
複製代碼

而後,必定要把其中的「你本身的AppCode」這個字符串,替換爲你真實的 AppCode 。

把替換好的語句複製粘貼到終端窗口裏面運行。

運行結果,以下圖所示:

看見窗口下方包含中文的數據了嗎?

利用 API 獲取數據,就是這麼簡單。

既然終端執行一條命令就能夠,那咱們幹嗎還要編程呢?

好問題!

由於咱們須要的數據,可能不是一次調用就能所有得到。

你須要重複屢次調用 API ,並且還得不斷變化參數,積累得到數據。

每次如果都這樣手動執行命令,效率就過低了。

API 的提供方,會爲用戶提供詳細的文檔與說明,甚至還包括樣例。

上圖中,除了剛纔咱們使用的 curl ,還包括如下語言訪問 API 接口的樣例說明:

  • Java
  • C#
  • PHP
  • Python
  • Object C

咱們以 Python 做爲例子,點開標籤頁看看。

你只須要把樣例代碼所有拷貝下來,用文本編輯器保存爲「.py」爲擴展名的 Python 腳本文件,例如 demo.py 。

再次提醒,別忘了,把其中「你本身的AppCode」這個字符串,替換爲你真實的 AppCode,而後保存。

在終端下,執行:

python demo.py
複製代碼

若是你用的是 2.7 版本的 Python ,就當即能夠正確得到結果了。

爲何許多學生作不出來結果呢?

我讓他們實際跑了一下,發現確實有的學生粗枝大葉,忘了替換本身的 AppCode 。

可是大部分同窗,因爲安裝最新版本的 Anaconda (Python 3.6版),都遇到了下面的問題:

你可能會認爲這是由於沒有正確安裝 urllib2 模塊,因而執行

pip install urllib2
複製代碼

你可能會看到下面的報錯提示:

你也許嘗試去掉版本號,只安裝 urllib,即:

pip install urllib
複製代碼

可是結果依然不美妙:

有些 Python 開發者看到這裏,可能會嘲笑咱們:Python 3版本里面,urllib 被拆分了啊!地球人都知道,你應該……

請保持一顆同理心。

想一想一個普通用戶,憑什麼要了解不一樣版本 Python 之間的語句差別?憑什麼要對這種版本轉換的解決方式內心有數?

在他們看來,官方網站提供的樣例,就應該是能夠運行的。報了錯,又不能經過本身的軟件包安裝「三板斧」來解決,就會慌亂和焦慮。

更進一步,他們也不太瞭解 JSON 格式。

雖然,JSON已經是一種很是清晰的、人機皆可通讀的數據存儲方式了。

他們想了解的,是怎麼把問題遷移到本身可以解決的範圍內。

例如說,可否把 JSON 轉換成 Excel 形式的數據框?

若是能夠,他們就能夠調用熟悉的 Excel 命令,來進行數據篩選、分析與繪圖了。

他們還會想,假如 Python 自己,能一站式完成數據讀取、整理、分析和可視化全流程,那天然更好。

可是,樣例,樣例在哪裏呢?

在我《Python編程遇問題,文科生怎麼辦?》一文中,我曾經提到過,這種樣例,對於普通用戶的重要性。

沒有「葫蘆」,他們又如何「照葫蘆畫瓢」呢?

既然這個例子中,官方文檔沒有提供如此詳細的代碼和講解樣例,那我就來爲你繪製個「葫蘆」吧。

下面,我給你逐步展現,如何在 Python 3 下,調用該 API 接口,讀取、分析數據,和繪製圖形。

環境

首先咱們來看看代碼運行環境。

前面提到過,若是樣例代碼的運行環境,和你本地的運行環境不一,計時代碼自己沒問題,也沒法正常執行

因此,我爲你構建一個雲端代碼運行環境。(若是你對這個代碼運行環境的構建過程感興趣,歡迎閱讀個人《如何用iPad運行Python代碼?》一文。)

請點擊這個連接t.cn/R3us4Ao),直接進入我們的實驗環境。

不須要在本地計算機安裝任何軟件包。只要有一個現代化瀏覽器(包括Google Chrome, Firefox, Safari和Microsoft Edge等)就能夠了。所有的依賴軟件,我都已經爲你準備好了。

打開連接以後,你會看見這個頁面。

這個界面來自 Jupyter Lab。

圖中左側分欄,是工做目錄下的所有文件。

右側打開的,是我們要使用的ipynb文件。

根據個人講解,請你逐條執行,並仔細觀察運行結果。

本例中,咱們主要會用到如下兩個新的軟件包。

首先是號稱「給人用」(for humans)的HTTP工具包requests。

這款工具,不只符合人類的認知與使用習慣,並且對 Python 3 更加友好。做者 Kenneth Reitz 甚至在敦促全部的 Python 2 用戶,趕忙轉移到 Python 3 版本。

The use of Python 3 is highly preferred over Python 2. Consider upgrading your applications and infrastructure if you find yourself still using Python 2 in production today. If you are using Python 3, congratulations — you are indeed a person of excellent taste. —Kenneth Reitz

咱們將用到的一款繪圖工具,叫作 plotnine

它實際上本不是 Python 平臺上的繪圖工具,而是從 R 平臺的 ggplot2 移植過來的。

要知道,此時 Python 平臺上,已經有了 matplotlib, seaborn, bokeh, plotly 等一系列優秀的繪圖軟件包。

那爲何還要費時費力地,移植 ggplot2 過來呢?

由於 ggplot2 的做者,是大名鼎鼎的 R 語言大師級人物 Hadley Wickham 

他創造 ggplot2,並不是爲 R 提供另外一種繪圖工具,而是提供另外一種繪圖方式

ggplot2 徹底遵照而且實現了 Leland Wilkinson 提出的「繪圖語法」(Grammar of Graphics),圖像的繪製,從本來的部件拆分,變成了層級拆分。

這樣一來,數據可視化變得史無前例地簡單易學,且功能強大。

我會在後文的「代碼」部分,用詳細的敘述,爲你展現如何使用這兩個軟件包。

我建議你先徹底按照教程跑一遍,運行出結果。

若是一切正常,再將其中的數據,替換爲你本身感興趣的內容

以後,嘗試打開一個空白 ipynb 文件,根據教程和文檔,本身敲代碼,而且嘗試作調整。

這樣會有助於你理解工做流程和工具使用方法。

下面咱們來看代碼。

代碼

首先,讀入HTTP工具包requests。

import requests
複製代碼

第二句裏面,有「Your AppCode here」字樣,請把它替換爲你本身的AppCode,不然下面運行會報錯。

appcode = 'Your AppCode here'
複製代碼

咱們嘗試獲取麗江5月份的天氣信息。

在API信息頁面上,有城市和代碼對應的表格。

位置比較隱蔽,在公司簡介的上方。

我把這個 Excel 文檔的網址放在了這裏(http://t.cn/R3T7e39),你能夠直接點擊下載。

下載該 Excel 文件後打開,根據表格查詢,咱們知道「101291401」是麗江的城市代碼。

咱們將其寫入areaid變量。

日期咱們選擇本文寫做的月份,即2018年5月。

areaid = "101291401"
month = "201805"
複製代碼

下面咱們就設置一下 API 接口調用相關的信息。

根據API信息頁面上的提示,咱們的要訪問的網址爲:https://ali-weather.showapi.com/weatherhistory,須要輸入的兩個參數,就是剛纔已經設置的areaidmonth

另外,咱們須要驗證身份,證實本身已經付費了。

點擊上圖中藍色的「API 簡單身份認證調用方法(APPCODE)」,你會看到如下示例頁面。

看來咱們須要在HTTP數據頭(header)中,加入 AppCode。

咱們依次把這些信息都寫好。

url = 'https://ali-weather.showapi.com/weatherhistory'
payload = {'areaid': areaid, 'month': month}
headers = {'Authorization': 'APPCODE {}'.format(appcode)}
複製代碼

下面,咱們就該用 requests 包來工做了。

requests 的語法很是簡潔,只須要指定4樣內容:

  • 調用方法爲「GET」
  • 訪問地址 url
  • url中須要附帶的參數,即 payload (包含 areaidmonth的取值)
  • HTTP數據頭(header)信息,即 AppCode
r = requests.get(url, params=payload, headers=headers)
複製代碼

執行後,好像……什麼也沒有發生啊!

咱們來查看一下:

r
複製代碼

Python 告訴咱們:

<Response [200]>
複製代碼

返回碼「200」的含義爲訪問成功。

回顧一下,《如何用R和API免費獲取Web數據?》一文中,咱們提到過:

以2開頭的狀態編碼是最好的結果,意味着一切順利;若是狀態值的開頭是數字4或者5,那就有問題了,你須要排查錯誤。

既然調用成功,咱們看看 API 接口返回的具體數據內容吧。

調用返回值的 content 屬性:

r.content
複製代碼

這一屏幕,密密麻麻的。

其中許多字符,甚至都不能正常顯示。這可怎麼好?

不要緊,從 API 信息頁上,咱們得知返回的數據,是 JSON 格式。

那就好辦了,咱們調用 Python 自帶的 json 包。

import json
複製代碼

用 json 包的字符串處理功能(loads)解析返回內容,結果存入 content_json

content_json = json.loads(r.content)
複製代碼

看看 content_json 結果:

content_json
複製代碼

能夠看到,返回的信息很完整。並且剛剛沒法正常顯示的中文,此時也都顯現了廬山真面目。

下一步很關鍵。

咱們把真正關心的數據提取出來。

咱們不須要返回結果中的錯誤碼等內容。

咱們要的,是包含每一每天氣信息的列表。

觀察發現,這一部分的數據,存儲在 'list' 中,而 'list' ,又存儲在 'showapi_res_body' 裏面

因此,爲選定列表,咱們須要指定其中的路徑:

content_json['showapi_res_body']['list']
複製代碼

冗餘信息都被去掉了,只剩下咱們想要的列表。

可是對着一個列表操做,不夠方便與靈活。

咱們但願將列表轉換爲數據框。這樣分析和可視化就簡單多了。

大不了,咱們還能夠把數據框直接導出爲 Excel 文件,扔到熟悉的 Excel 環境裏面,去繪製圖形。

讀入 Python 數據框工具 pandas 。

import pandas as pd
複製代碼

咱們讓 Pandas 將剛剛保留下來的列表,轉換爲數據框,存入 df 。

df = pd.DataFrame(content_json['showapi_res_body']['list'])
複製代碼

看看內容:

df
複製代碼

此時,數據顯示格式很是工整,各項信息一目瞭然。

寫到這裏,你基本上搞懂了,如何讀取某個城市、某個月份的數據,而且整理到 Pandas 數據框中。

可是,咱們要作分析,顯然不能侷限在單一月份與單一城市。

每次加入一組數據,若是都得從頭這樣作一遍,會很辛苦。並且語句多了,執行起來,不免顧此失彼,出現錯誤。

因此,咱們須要把剛剛的代碼語句整合起來,將其模塊化,造成函數。

這樣,咱們只須要在調用函數的時候,傳入不一樣的參數,例如不一樣的城市名、月份等信息,就能得到想要的結果了。

綜合上述語句,咱們定義一個傳入城市和月份信息,得到數據框的完整函數。

def get_df(areaid, areaname_dict, month, appcode):

    url = 'https://ali-weather.showapi.com/weatherhistory'
    payload = {'areaid': areaid, 'month': month}
    headers = {'Authorization': 'APPCODE {}'.format(appcode)}

    r = requests.get(url, params=payload, headers=headers)

    content_json = json.loads(r.content)

    df = pd.DataFrame(content_json['showapi_res_body']['list'])
    df['areaname'] = areaname_dict[areaid]

    return df
複製代碼

注意除了剛纔用到的語句外,咱們爲函數增長了一個輸入參數,即areaname_dict

它是一個字典,每一項分別包括城市代碼,和對應的城市名稱。

根據咱們輸入的城市代碼,函數就能夠自動在結果數據框中添加一個列,註明對應的是哪一個城市。

當咱們獲取多個城市的數據時,某一行的數聽說的是哪一個城市,就能夠一目瞭然。

反之,若是隻給你看城市代碼,你很快就會眼花繚亂,不知所云了。

可是,只有上面這一個函數,仍是不夠高效。

畢竟咱們可能須要查詢若干月、若干城市的信息。若是每次都調用上面的函數,也夠累的。

因此,咱們下面再編寫一個函數,幫咱們自動處理這些髒活兒累活兒。

def get_dfs(areaname_dict, months, appcode):
    dfs = []
    for areaid in areaname_dict:
        dfs_times = []
        for month in months:
            temp_df = get_df(areaid, areaname_dict, month, appcode)
            dfs_times.append(temp_df)
        area_df = pd.concat(dfs_times)
        dfs.append(area_df)
    return dfs
複製代碼

說明一下,這個函數接受的輸入,包括城市代碼-名稱字典、一系列的月份,以及咱們的 AppCode。

它的處理方式,很簡單,就是個雙重循環。

外層循環負責遍歷全部要求查詢的城市,內層循環遍歷所有指定的時間範圍。

它返回的內容,是一個列表。

列表中的每一項,都分別是某個城市一段時間(可能包含若干個月)的天氣信息數據框。

咱們先用單一城市、單一月份來試試看。

仍是2018年5月的麗江。

areaname_dict = {"101291401":"麗江"}
months = ["201805"]
複製代碼

咱們將上述信息,傳入 get_dfs 函數。

dfs = get_dfs(areaname_dict, months, appcode)
複製代碼

看看結果:

dfs
複製代碼

返回的是一個列表。

由於列表裏面只有一個城市,因此咱們只讓它返回第一項便可。

dfs[0]
複製代碼

此次顯示的,就是數據框了:

測試經過,下面咱們趁熱打鐵,把天津、上海、麗江2018年初至今全部數據都讀取出來。

先設定城市:

areaname_dict = {"101030100":"天津", "101020100":"上海", "101291401":"麗江"}
複製代碼

再設定時間範圍:

months = ["201801", "201802", "201803", "201804", "201805"]
複製代碼

我們再次執行 get_dfs 函數。

dfs = get_dfs(areaname_dict, months, appcode)
複製代碼

看看此次的結果:

dfs
複製代碼

結果仍是一個列表。

列表中的每一項,對應某個城市2018年年初到5月份本文寫做時,這一段時間範圍天氣數據。

假設咱們要綜合分析幾個城市的天氣信息,那麼就能夠把這幾個數據框整合在一塊兒。

用到的方法,是 Pandas 內置的 concat 函數。

它接收一個數據框列表,把其中每個個數據框沿着縱軸(默認)鏈接在一塊兒。

df = pd.concat(dfs)
複製代碼

看看此時的總數據框效果:

df
複製代碼

這是開頭部分:

這是結尾部分:

3個城市,4個多月的數據都正確讀取和整合了。

下面咱們嘗試作分析。

首先,咱們得搞清楚數據框中的每一項,都是什麼格式:

df.dtypes
複製代碼
aqi                object
aqiInfo            object
aqiLevel           object
max_temperature    object
min_temperature    object
time               object
weather            object
wind_direction     object
wind_power         object
areaname           object
dtype: object
複製代碼

全部的列,全都是按照 object 處理的。

什麼叫 object

在這個語境裏,你能夠將它理解爲字符串類型。

可是,我們不能把它們都當成字符串來處理啊。

例如日期,應該按照日期類型來看待,不然怎麼作時間序列可視化?

AQI的取值,若是看做字符串,那怎麼比較大小呢?

因此咱們須要轉換一下數據類型。

先轉換日期列:

df.time = pd.to_datetime(df.time)
複製代碼

再轉換 AQI 數值列:

df.aqi = pd.to_numeric(df.aqi)
複製代碼

看看此時 df 的數據類型:

df.dtypes
複製代碼
aqi                         int64
aqiInfo                    object
aqiLevel                   object
max_temperature            object
min_temperature            object
time               datetime64[ns]
weather                    object
wind_direction             object
wind_power                 object
areaname                   object
dtype: object
複製代碼

此次就對了,日期和 AQI 都分別變成了咱們須要的類型。其餘數據,暫時保持原樣。

有的是由於原本就該是字符串,例如城市名稱。

另外一些,是由於咱們暫時不會用到。

下面咱們繪製一個簡單的時間序列對比圖形。

讀入繪圖工具包 plotnine 。

注意咱們同時讀入了 date_breaks,用來指定圖形繪製時,時間標註的間隔。

import matplotlib.pyplot as plt
%matplotlib inline
from plotnine import *
from mizani.breaks import date_breaks
複製代碼

正式繪圖:

(ggplot(df, aes(x='time', y='aqi', color='factor(areaname)')) + geom_line() +
 scale_x_datetime(breaks=date_breaks('2 weeks')) +
 xlab('日期') +
 theme_matplotlib() +
 theme(axis_text_x=element_text(rotation=45, hjust=1)) +
 theme(text=element_text(family='WenQuanYi Micro Hei'))
 )
複製代碼

咱們指定橫軸爲時間序列,縱軸爲 AQI,用不一樣顏色的線來區分城市。

繪製時間的時候,以「2周」做爲間隔週期,標註時間上的數據統計量信息。

咱們修改橫軸的標記爲中文的「日期」。

由於時間顯示起來比較長,若是按照默認樣式,會堆疊在一塊兒,很差看,因此咱們讓它旋轉45度角,這樣避免重疊,一目瞭然。

爲了讓圖中的中文正常顯示,咱們須要指定中文字體,這裏咱們選擇的是開源的「文泉驛微米黑」。

數據可視化結果,以下圖所示。

png

怎麼樣,這張對比圖,繪製得還像模像樣吧?

從圖中,你能夠分析出什麼結果呢?

反正我看完這張圖,很想去麗江

小結

讀過本教程,但願你已經掌握瞭如下知識:

  • 如何在 API 雲市場上,根據提示選購本身感興趣的產品;
  • 如何獲取你的身份驗證信息 AppCode ;
  • 如何用最簡單的命令行 curl 方式,直接調用 API 接口,得到結果數據;
  • 如何使用 Python 3 和更人性化的 HTTP 工具包 requests 調用 API 得到數據;
  • 如何用 JSON 工具包解析處理得到的字符串數據;
  • 如何用 Pandas 轉換 JSON 列表爲數據框;
  • 如何將測試經過後的簡單 Python 語句打包成函數,以反覆調用,提升效率;
  • 如何用 plotnine (ggplot2的克隆)繪製時間序列折線圖,對比不一樣城市 AQI 歷史走勢;
  • 如何在雲環境中運行本樣例,而且照葫蘆畫瓢,自行修改。

但願這份樣例代碼,能夠幫你創建信心,嘗試本身去搜集與嘗試 API 數據獲取,爲本身的科研工做添磚加瓦。

若是你但願在本地,而非雲端運行本樣例,請使用這個連接t.cn/R3usDi9)下載本文用到的所有源代碼和運行環境配置文件(Pipenv)壓縮包。

若是你知道如何使用github,也歡迎用這個連接t.cn/R3usEti)訪問對應的github repo,進行clone或者fork等操做。

固然,要是能給個人repo加一顆星,就更好了。

討論

你以前嘗試過用 Python 和 API 獲取數據嗎?你使用了哪些更好用的軟件包進行數據獲取、處理、分析與可視化呢?你還使用過哪些其餘的數據產品市場?歡迎留言,把你的經驗和思考分享給你們,咱們一塊兒交流討論。

喜歡請點贊。還能夠微信關注和置頂個人公衆號「玉樹芝蘭」(nkwangshuyi)

若是你對數據科學感興趣,不妨閱讀個人系列教程索引貼《如何高效入門數據科學?》,裏面還有更多的有趣問題及解法。

相關文章
相關標籤/搜索