-
Notifications
You must be signed in to change notification settings - Fork 9
/
01c-apis.Rmd
162 lines (132 loc) · 5.51 KB
/
01c-apis.Rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
---
title: "Scraping data from APIs"
author: "Pablo Barbera"
date: "June 26, 2017"
output: html_document
---
### Scraping web data from APIs
To understand how APIs work, we'll take the New York Times API as an example. This API allows users to search articles by string and dates, and returns counts of articles and a short description of each article (but not the full text). You can find the documentation [here](http://developer.nytimes.com/docs/read/article_search_api_v2). Get a new API token and paste it here:
```{r}
apikey <- '05e7c6c6f4896fba6c08531808067326:10:67731177'
```
The fist step is to identify the base url and the parameters that we can use to query the API. Now we can do a first API call using the `httr` package. (You can use my API key for now, let's hope we don't hit the rate limit!)
```{r}
base_url <- "http://api.nytimes.com/svc/search/v2/articlesearch.json"
# install.packages("httr")
library(httr)
r <- GET(base_url, query=list(q="inequality","api-key"=apikey))
r
```
From the output of `r`, we can see that the query was successful (`Status: 200`), the content is in `json` format, and its size is `17.3kB`.
To extract the text returned by this API call, you can use `content`. You can write it to a file to take a look at it.
```{r, eval=FALSE}
content(r, 'text')
```
```{r}
writeLines(content(r, 'text'), con=file("nyt.json"))
```
We can save the output into an object in R to learn more about its structure.
```{r}
json <- content(r, 'parsed')
class(json); names(json) # list with 3 elements
json$status # this should be "OK"
names(json$response) # the actual data
json$response$meta # metadata
```
If we check the documentation, we find that we can subset by date with the `begin_date` and `end_date` parameters. Let's see how this works...
```{r}
r <- GET(base_url, query=list(q="inequality",
"api-key"=apikey,
"begin_date"=20160101,
"end_date"=20161231))
json <- content(r, 'parsed')
json$response$meta
```
Between these two dates, there were 1840 articles in the NYT mentioning "inequality".
Now imagine we want to look at the evolution of mentions of this word over time. Following the best coding practices we introduced earlier, we want to write a function that will take a word and a set of dates as arguments and return the counts of articles.
This would be a first draft of that function:
```{r}
nyt_count <- function(q, date1, date2){
r <- GET(base_url, query=list(q=q,
"api-key"=apikey,
"begin_date"=date1,
"end_date"=date2))
json <- content(r, "parsed")
return(json$response$meta$hits)
}
nyt_count(q="inequality", date1=20160101, date2=20160131)
```
Ok, so this seems to work. But we want to run this function multiple times, so let's write another function that helps us do that.
```{r}
nyt_years_count <- function(q, yearinit, yearend){
# sequence of years to loop over
years <- seq(yearinit, yearend)
counts <- rep(NA, length(years))
# loop over periods
for (i in 1:length(years)){
# information message to track progress
message(years[i])
# retrieve count
counts[i] <- nyt_count(q=q, date1=paste0(years[i], "0101"),
date2=paste0(years[i], "1231"))
}
return(counts)
}
```
```{r, eval=FALSE}
# and let's see what happens...
nyt_years_count(q="inequality", yearinit=1950, yearend=2012)
```
Oops! What happened? Why the error? We're querying the API too fast. Let's modify the function to add a `while` loop that will wait a couple of seconds in case there's an error:
```{r}
nyt_count <- function(q, date1, date2){
r <- GET(base_url, query=list(q=q,
"api-key"=apikey,
"begin_date"=date1,
"end_date"=date2))
json <- content(r, "parsed")
## if there is no response
while (r$status_code!=200){
Sys.sleep(2) # wait a couple of seconds
# try again:
r <- GET(base_url, query=list(q=q,
"api-key"=apikey,
"begin_date"=date1,
"end_date"=date2))
json <- content(r, "parsed")
}
return(json$response$meta$hits)
}
```
And let's see if this does the trick...
```{r}
counts <- nyt_years_count(q="inequality", yearinit=1980, yearend=2012)
plot(1980:2012, counts, type="l", main="Mentions of inequality on the NYT, by year",
xlab="Year", ylab="Article count")
```
Let's try to generalize the function even more so that it works with any date interval, not just years:
```{r}
nyt_dates_count <- function(q, init, end, by){
# sequence of dates to loop over
dates <- seq(from=init, to=end, by=by)
dates <- format(dates, "%Y%m%d") # changing format to match NYT API format
counts <- rep(NA, length(dates)-1)
# loop over periods
for (i in 1:(length(dates)-1)){ ## note the -1 here
# information message to track progress
message(dates[i])
# retrieve count
counts[i] <- nyt_count(q=q, date1=dates[i],
date2=dates[i+1])
}
# improving this as well so that it returns a data frame
df <- data.frame(date = as.Date(dates[-length(dates)], format="%Y%m%d"), count = counts)
return(df)
}
```
And now we can count articles at the month level...
```{r}
counts <- nyt_dates_count(q="trump", init = as.Date("2015/01/01"), end = as.Date("2017/05/30"), by="month")
plot(counts$date, counts$count, type="l", main="Mentions of 'Trump' in the NYT, by month",
xlab="Month", ylab="Article count")
```