API是得到Web數據的重要途徑之一。想不想了解如何用R調用API,提取和整理你須要的免費Web數據呢?本文一步步爲你詳盡展現操做流程。html
俗話說「巧婦難爲無米之炊」。即使你已經掌握了數據分析的十八般武藝,沒有數據也是苦惱的事情。「拔劍四顧心茫然」說的大概就是這種情境吧。git
數據的來源有不少。Web數據是其中數量龐大,且相對容易得到的類型。更妙的是,許多的Web數據,都是免費的。github
在這個號稱大數據的時代,你是如何得到Web數據的呢?web
許多人會使用那些別人整理好而且發佈的數據集。編程
他們很幸運,工做能夠創建在別人的基礎上。這樣效率最高。json
可是不見得每一個人都有這樣的幸運。若是你須要用到的數據,偏巧沒有人整理和發佈過,怎麼辦?api
其實,這樣的數據數量更爲龐大。咱們難道對它們視而不見嗎?瀏覽器
若是你想到了爬蟲,那麼你的思考方向是對的。爬蟲幾乎能夠把一切看得見的(甚至是看不見的) Web數據,都通通幫你弄下來。然而編寫和使用爬蟲是有很高的成本的。包括時間資源、技術能力等。若是面對任何Web數據獲取問題,你都不假思索「上大錘」,有時候極可能是「殺雞用了牛刀」。服務器
在「別人準備好的數據」和「須要本身爬取的數據」之間,還有很寬廣的一片地帶,這裏就是API的天地。微信
API是什麼?
它是Application Programming Interface的縮寫。具體而言,就是某個網站,有不斷積累和變化的數據。這些數據若是整理出來,不只耗時,並且佔地方,何況剛剛整理好就有過時的危險。大部分人須要的數據,其實都只是其中的一小部分,時效性的要求卻可能很強。所以整理儲存,而且提供給大衆下載,是並不經濟划算的。
但是若是不能以某種方式把數據開放出來,又會面對無數爬蟲的騷擾。這會給網站的正常運行帶來不少煩惱。折中的辦法,就是網站主動提供一個通道。當你須要某一部分數據的時候,雖然沒有現成的數據集,卻只須要利用這個通道,描述你本身想要的數據,而後網站審覈(通常是自動化的,瞬間完成)以後,認爲能夠給你,就馬上把你明確索要的數據發送過來。雙方皆大歡喜。
從此你找數據的時候,也不妨先看看目標網站是否提供了API,以免作無用功。
這個github項目裏,有一份很是詳盡的列表,涵蓋了目前常見的主流網站API資源情況。做者還在不斷整理修訂,你能夠把它收藏起來,慢慢看。
若是咱們得知某個網站提供API,而且經過看說明文檔,知道了咱們須要的數據就在其中,那問題就變成了——該如何經過API來得到數據呢?
下面咱們用一個實際的例子,爲你全程展現操做步驟。
咱們找的樣例,是維基百科。
維基百科的API總覽,請參考這個頁面。
假設咱們關心的,是某一個時間段內,指定維基百科文章頁面的訪問量。
維基百科專門爲咱們提供了一類數據,叫作度量數據(metrics),其中就涵蓋了頁面訪問次數這個關鍵值。對應API的介紹頁面,在這裏。
頁面裏有一個樣例。假設你須要得到2015年10月,愛因斯坦這個詞條頁面的訪問數量,就能夠這樣調用:
GET http://wikimedia.org/api/rest_v1/metrics/pageviews/per-article/en.wikipedia/all-access/all-agents/Albert_Einstein/daily/2015100100/2015103100
複製代碼
咱們能夠把GET後面這一長串的網址,輸入到瀏覽器的地址欄,而後回車,看看會獲得什麼結果。
咱們在瀏覽器裏,看到上圖中那一長串文字。你可能感受很奇怪——這是什麼玩意兒?
恭喜你,這就是咱們須要得到的數據了。只不過,它使用了一種特殊的數據格式,叫作JSON。
JSON是目前互聯網上數據交互的主流格式之一。若是你想搞清楚JSON的含義和用法,能夠參考這個教程。
咱們在瀏覽器裏,初始只能看到數據最開頭的一部分。可是裏面已經包含了頗有價值的內容:
{"items":[{"project":"en.wikipedia","article":"Albert_Einstein","granularity":"daily","timestamp":"2015100100","access":"all-access","agent":"all-agents","views":18860}
複製代碼
這一段裏,咱們看到項目名稱(en.wikipedia),文章標題(Albert Einstein),統計粒度(天),時間戳(2015年10月1日),訪問類型(所有),終端類型(所有),以及訪問數量(18860)。
咱們用滑動條拖拽返回的文本到最後,會看到以下的信息:
{"project":"en.wikipedia","article":"Albert_Einstein","granularity":"daily","timestamp":"2015103100","access":"all-access","agent":"all-agents","views":16380}]}
複製代碼
與10月1日的數據對比,只有時間戳(2015年10月31日)和訪問數量(16380)發生了變化。
中間咱們跳過的,是10月2日到10月30日之間的數據。存儲格式都是同樣的,也只是日期和訪問量兩項數據值在變化。
須要的數據都在這裏,你只須要提取出相應的信息,就能夠了。可是若是讓你手動來作(例如拷貝須要的項,粘貼到Excel中),顯然效率很低,並且很容易出錯。下面咱們來展現一下,如何用R編程環境來自動化完成這一過程。
在正式用R調用API前,咱們須要進行一些必要的準備工做。
首先是安裝R。
請先到這個網址下載R基礎安裝包。
R的下載位置有不少。建議你選擇清華大學的鏡像,能夠得到比較高的下載速度。
請根據你的操做系統平臺,選擇其中對應的版本下載。我用的是macOS版本。
下載獲得pkg文件。雙擊就能夠安裝。
安裝了基礎包以後,咱們繼續安裝集成開發環境RStudio。它能夠幫助你輕鬆地以交互方式和R溝通。RStudio的下載地址在這裏。
依據你的操做系統狀況,選擇對應的安裝包。macOS安裝包爲dmg文件。雙擊打開後,把其中的RStudio.app圖標拖動到Applications文件夾中,安裝就完成了。
下面咱們從應用目錄中,雙擊運行RStudio。
咱們先在RStudio的Console中,運行以下語句,安裝一些須要用到的軟件包:
install.packages("tidyverse")
install.packages("rlist")
複製代碼
安裝完畢後,選擇菜單裏的File->New
,從如下界面中選擇 R Notebook。
R Notebook默認提供給咱們一個模板,附帶一些基礎使用說明。
咱們嘗試點擊編輯區域(左側)代碼部分(灰色)的運行按鈕。
當即就能夠看到繪圖的結果了。
咱們點擊菜單欄上的Preview按鈕,來看整個兒代碼的運行結果。運行結果會以圖文並茂的HTML文件方式展現出來。
熟悉了環境後,咱們該實際操做運行本身的代碼了。咱們把左側編輯區的開頭說明區保留,把其他部分刪除,而且把文件名改爲有意義的web-data-api-with-R
。
至此,準備工做就緒。下面咱們就要開始實際操做了。
實際操做過程當中,咱們從維基百科上換另一篇維基文章做爲樣例,以證實本操做方法的通用性。選擇的文章是咱們在介紹詞雲製做時使用過的,叫作「Yes, Minisiter」。這是一部1980年代的英國喜劇。
咱們首先在瀏覽器裏嘗試一下,可否修改API樣例裏的參數,來得到「Yes, Minister」文章訪問統計數據。做爲測試,咱們暫時只收集2017年10月1日到2017年10月3日 ,共3天的數據。
相對樣例,咱們須要替換的內容包括起止時間和文章標題。
咱們在瀏覽器的地址欄輸入:
https://wikimedia.org/api/rest_v1/metrics/pageviews/per-article/en.wikipedia/all-access/all-agents/Yes_Minister/daily/2017100100/2017100300
複製代碼
返回結果以下:
數據可以正常返回,下面咱們在RStudio中採用語句方式來調用。
注意下面的代碼中,程序輸出部分的開頭會有##
標記,以便和執行代碼自己相區別。
一上來,咱們就須要設置一下時區。否則後面處理時間數據的時候,會遇到錯誤。
Sys.setenv(TZ="Asia/Shanghai")
複製代碼
而後,咱們調用tidyverse軟件包,它是個合集,一次性加載許多咱們後面要用到的功能。
library(tidyverse)
## Loading tidyverse: ggplot2
## Loading tidyverse: tibble
## Loading tidyverse: tidyr
## Loading tidyverse: readr
## Loading tidyverse: purrr
## Loading tidyverse: dplyr
## Conflicts with tidy packages ----------------------------------------------
## filter(): dplyr, stats
## lag(): dplyr, stats
複製代碼
這裏可能會遇到一些警告內容,不要理會就能夠。對我們的操做絕不影響。
根據前面的例子,咱們定義須要查詢的時間跨度,而且指定要查找的維基文章名稱。
注意與Python不一樣,R語言中,賦值採用<-
標記,而不是=
。不過R語言其實挺隨和,你要是非得堅持用=
,它也能認得,並不會報錯。
starting <- "20171001"
ending <- "20171003"
article_title <- "Yes Minister"
複製代碼
根據已經設定的參數,咱們就能夠生成調用的API地址了。
url <- paste("https://wikimedia.org/api/rest_v1/metrics/pageviews/per-article/en.wikipedia/all-access/all-agents",
article_title,
"daily",
starting,
ending,
sep = "/")
複製代碼
這裏咱們使用的是paste函數,它幫助咱們把幾個部分串接起來,最後的sep
指的是連接幾個字符串部分時,須要使用的鏈接符。由於咱們要造成的是相似於目錄格式的網址數據,因此這裏用的是分隔目錄時常見的斜線。
咱們檢查一下生成的url地址是否是正確:
url
## [1] "https://wikimedia.org/api/rest_v1/metrics/pageviews/per-article/en.wikipedia/all-access/all-agents/Yes Minister/daily/20171001/20171003"
複製代碼
檢查完畢,結果正確。下面咱們須要實際執行GET
函數,來調用API,得到維基百科的反饋數據。
要執行這一功能,咱們須要加載另一個軟件包,httr
。它相似於Python中的request軟件包,相似於Web瀏覽器,能夠完成和遠端服務器的溝通。
library(httr)
複製代碼
而後咱們開始調用。
response <-GET(url, user_agent="my@email.com this is a test")
複製代碼
咱們看看調用API的結果:
response
## Response [https://wikimedia.org/api/rest_v1/metrics/pageviews/per-article/en.wikipedia/all-access/all-agents/Yes Minister/daily/20171001/20171003]
## Date: 2017-10-13 03:10
## Status: 200
## Content-Type: application/json; charset=utf-8
## Size: 473 B
複製代碼
注意其中的status
一項。咱們看到它的返回值爲200。以2開頭的狀態編碼是最好的結果,意味着一切順利;若是狀態值的開頭是數字4或者5,那就有問題了,你須要排查錯誤。
既然咱們很幸運地沒有遇到問題,下面就打開返回內容看看裏面都有什麼吧。由於咱們知道返回的內容是JSON格式,因此咱們加載jsonlite
軟件包,以便用清晰的格式把內容打印出來。
library(jsonlite)
##
## Attaching package: 'jsonlite'
## The following object is masked from 'package:purrr':
##
## flatten
複製代碼
而後咱們打印返回JSON文本的內容。
toJSON(fromJSON(content(response, as="text")), pretty = TRUE)
## {
## "items": [
## {
## "project": "en.wikipedia",
## "article": "Yes_Minister",
## "granularity": "daily",
## "timestamp": "2017100100",
## "access": "all-access",
## "agent": "all-agents",
## "views": 654
## },
## {
## "project": "en.wikipedia",
## "article": "Yes_Minister",
## "granularity": "daily",
## "timestamp": "2017100200",
## "access": "all-access",
## "agent": "all-agents",
## "views": 644
## },
## {
## "project": "en.wikipedia",
## "article": "Yes_Minister",
## "granularity": "daily",
## "timestamp": "2017100300",
## "access": "all-access",
## "agent": "all-agents",
## "views": 578
## }
## ]
## }
複製代碼
能夠看到,3天的訪問數量統計信息,以及包含的其餘元數據,都正確地從服務器用API反饋給了咱們。
咱們把這個JSON內容存儲起來。
result <- fromJSON(content(response, as="text"))
複製代碼
檢查一下存儲的內容:
result
## $items
## project article granularity timestamp access agent
## 1 en.wikipedia Yes_Minister daily 2017100100 all-access all-agents
## 2 en.wikipedia Yes_Minister daily 2017100200 all-access all-agents
## 3 en.wikipedia Yes_Minister daily 2017100300 all-access all-agents
## views
## 1 654
## 2 644
## 3 578
複製代碼
咱們看看解析以後,存儲的類型是什麼:
typeof(result)
## [1] "list"
複製代碼
存儲的類型是列表(list)。但是爲了後續的分析,咱們但願把其中須要的信息提取出來,組成數據框(dataframe)。方法很簡單,使用rlist
這個R包,就能夠輕鬆辦到。
library(rlist)
複製代碼
咱們須要使用其中的兩個方法,一個是list.select
,用來把指定的信息抽取出來;一個是list.stack
,用來把列表生成數據框。
df <- list.stack(list.select(result, timestamp, views))
複製代碼
咱們看看結果:
df
## timestamp views
## 1 2017100100 654
## 2 2017100200 644
## 3 2017100300 578
複製代碼
數據抽取是正確的,包括了日期和瀏覽數量。可是這個日期格式不是標準格式,後面分析會有問題。咱們須要作轉化。
處理時間日期格式,最好的辦法是用lubridate
軟件包。咱們先調用它。
library(lubridate)
##
## Attaching package: 'lubridate'
## The following object is masked from 'package:base':
##
## date
複製代碼
因爲日期字符串後面還有表示時區的兩位(這裏都是0),咱們須要調用stringr
軟件包,將其截取掉。而後才能正確轉換。
library(stringr)
複製代碼
而後咱們開始轉換,先用str_sub
函數(來自於stringr
軟件包)把日期字符串的後兩位抹掉,而後用lubridate
軟件包裏面的ymd
函數,將原先的字符串轉換爲標準日期格式。修改後的數據,咱們存儲回df$timestamp
。
df$timestamp <- ymd(str_sub(df$timestamp, 1, -3))
複製代碼
咱們再來看看此時的df
內容:
df
## timestamp views
## 1 2017-10-01 654
## 2 2017-10-02 644
## 3 2017-10-03 578
複製代碼
至此,咱們須要的數據都格式正確地保留下來了。
不過,若是爲了處理每一篇文章的閱讀數量,咱們都這樣一條條跑語句,效率很低,並且不免會出錯。咱們把剛纔的輸入語句整理成函數,後面使用起來會更加方便。
整理函數的時候,咱們順便採用dplyr
包的「管道」(即你會看到的%>%
符號)格式改寫一下前面的內容,這樣能夠省卻中間變量,並且看起來更爲清晰明確。
get_pv <- function(article_title, starting, ending){
url <- paste("https://wikimedia.org/api/rest_v1/metrics/pageviews/per-article/en.wikipedia/all-access/all-agents",
article_title,
"daily",
starting,
ending,
sep = "/")
df <- url %>%
GET(user_agent="my@email.com this is a test") %>%
content(as="text") %>%
fromJSON() %>%
list.select(timestamp, views) %>%
list.stack() %>%
mutate(timestamp = timestamp %>%
str_sub(1,-3) %>%
ymd())
df
}
複製代碼
咱們用新定義的函數,從新嘗試一下剛纔的API數據獲取:
starting <- "20171001"
ending <- "20171003"
article_title <- "Yes Minister"
get_pv(article_title, starting, ending)
## timestamp views
## 1 2017-10-01 654
## 2 2017-10-02 644
## 3 2017-10-03 578
複製代碼
結果正確。
不過若是隻是抓取3天的數據,咱們這麼大費周章就沒有意思了。下面咱們擴展時間範圍,嘗試抓取自2014年初至2017年10月10日的數據。
starting <- "20141001"
ending <- "20171010"
article_title <- "Yes Minister"
df <- get_pv(article_title, starting, ending)
複製代碼
咱們看看運行結果:
head(df)
## timestamp views
## 1 2015-07-01 538
## 2 2015-07-02 588
## 3 2015-07-03 577
## 4 2015-07-04 473
## 5 2015-07-05 531
## 6 2015-07-06 500
複製代碼
有意思的是,數據的統計並非從2014年開始,而是2015年7月。這到底是因爲"Yes, Minister"維基文章是2015年7月才發佈?仍是由於咱們調用的API對檢索時間範圍有限制?抑或是其餘緣由?這個問題留做思考題,歡迎把你的答案和分析過程分享給你們。
下面,咱們把得到的數據用ggplot2
軟件包繪製圖形。用一行語句,看看幾年以內,"Yes, Minister"維基文章訪問數量的變化趨勢。
ggplot(data=df, aes(timestamp, views)) + geom_line()
複製代碼
做爲一部30多年前的劇集,今天還不斷有人訪問其維基頁面,可見它的魅力。從圖中能夠很是明顯看到幾個峯值,你能解釋它們出現的緣由嗎?這將做爲今天的另一道習題,供你思考。
簡單回顧一下,本文咱們接觸到了如下重要知識點:
但願讀過本文,你能初步掌握上述內容,而且根據文中提供的連接和教程資源拓展學習相關知識。
你以前利用API獲取過Web數據嗎?除了R之外,你還使用過哪些API的調用工具?與本文的介紹比起來,這些工具備什麼特色?歡迎留言,把你的心得經驗分享給你們,咱們一塊兒交流討論。
喜歡請點贊。還能夠微信關注和置頂個人公衆號「玉樹芝蘭」(nkwangshuyi)。
若是你對數據科學感興趣,不妨閱讀個人系列教程索引貼《如何高效入門數據科學?》,裏面還有更多的有趣問題及解法。