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

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

來自專欄 玉樹芝蘭

本文以一款阿里雲市場歷史天氣查詢產品為例,為你逐步介紹如何用 Python 調用 API 收集、分析與可視化數據。希望你舉一反三,輕鬆應對今後的 API 數據收集與分析任務。

雷同

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

大家做的內容,確實五花八門。

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

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

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

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

作為老師,我在下面,應該很開心吧?

不,我簡直哭笑不得。

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 文檔的網址放在了這裡(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/weatherhistorypayload = {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 objectaqiInfo objectaqiLevel objectmax_temperature objectmin_temperature objecttime objectweather objectwind_direction objectwind_power objectareaname objectdtype: object

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

什麼叫 object

在這個語境里,你可以將它理解為字元串類型。

但是,咱們不能把它們都當成字元串來處理啊。

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

AQI的取值,如果看作字元串,那怎麼比較大小呢?

所以我們需要轉換一下數據類型。

先轉換日期列:

df.time = pd.to_datetime(df.time)

再轉換 AQI 數值列:

df.aqi = pd.to_numeric(df.aqi)

看看此時 df 的數據類型:

df.dtypes

aqi int64aqiInfo objectaqiLevel objectmax_temperature objectmin_temperature objecttime datetime64[ns]weather objectwind_direction objectwind_power objectareaname objectdtype: object

這次就對了,日期和 AQI 都分別變成了我們需要的類型。其他數據,暫時保持原樣。

有的是因為本來就該是字元串,例如城市名稱。

另一些,是因為我們暫時不會用到。

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

讀入繪圖工具包 plotnine 。

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

import matplotlib.pyplot as plt%matplotlib inlinefrom 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度角,這樣避免重疊,一目了然。

為了讓圖中的中文正常顯示,我們需要指定中文字體,這裡我們選擇的是開源的「文泉驛微米黑」。

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

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

從圖中,你可以分析出什麼結果呢?

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

小結

讀過本教程,希望你已經掌握了以下知識:

  • 如何在 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)。

如果本文可能對你身邊的親友有幫助,也歡迎你把本文通過微博或朋友圈分享給他們。讓他們一起參與到我們的討論中來。

延伸閱讀

如何高效入門數據科學?


推薦閱讀:

Python 函數參數
創建 ed2k link
一文詳解python爬蟲技巧,那些你知道和不知道的~~~
從零到搭建一個能提供API介面的網站,過程是怎樣的?

TAG:Python | 數據分析 | 數據挖掘 |