Tutorial 11: Acessando Dados via APIs

Acessando dados via APIs.

No nosso último encontro, começamos a aprender sobre acesso de dados via meios digitais. Focamos em dois pontos:

  1. Introdução a acesso de dados digitais, melhores práticas e ética.

  2. Raspagem de dados em sites estáticos via rvest

Faltou cobrirmos uma segunda forma, e mais recomendada, de acesso a dados digitais: acesso via APIs. Este será o tema deste tutorial e de nossa últimas aula mais geral de programação em R.

Nossos planos para hoje.

Pretendo cobrir os seguintes pontos neste tutorial:

  1. Introdução a APIs. O que fazem? Onde vivem? O que comem?

  2. Acessando APIs via http.

    • Estudo de caso na API do Fogo Cruzado.
  3. Exemplos de pacotes de R para acessar APIs

    • Fogo Cruzado
    • Dados do Congresso (congressbr)

Introdução a APIs. O que fazem? Onde vivem? O que comem?

A sigla API corresponde ao termo em inglês “Application Programming Interface“. No português “Interface de Programação de Aplicações”. Em linguagem não técnica, uma API é um repositório on-line construído para facilitar troca de informações entre usuários de dados e os portadores de determinados dados. Muita empresas constroem este repositórios para diversas funções, entre elas, compartilhar dados, receber dados, gerenciamento conjunto de banco de dados, e fornecer funções ou maquinas de inteligência artificial para uso do público.

Vamos pensar um exemplo capaz de motivar a criação de uma API. Imagine que você é o dono do Twitter. Você teria zilhões de hackers todos os dias tentar fazer raspagem dos seus dados, isso tornaria seu site mais instável, e inseguro. Qual a solução? Você cria uma API, e passa a controlar quem acessa a informação, quando acessam, qual tipo de informação você torna disponível. De quebra, quando você cria uma API você ainda ainda ganha uns pontos com os defensores da política de dados abertos.

Para entender como uma API funciona, a metáfora do Matrix feita pelo Curso-R é excelente. Lembrem-se do filme Matrix. Os sobreviventes no mundo Matrix podiam conectar à uma maquina e pedir para aprender determinada habilidade - kung-fu, programação, língua, etc. Isso é exatamente o que uma API faz. Você conecta a site e pede dados, e recebe-os em retorno. É como enviar um email, porém fazendo tudo via linguagem de programação.

Usos de API

Há duas formas principais em que nós acadêmicos comumente utilizamos APIs.

  1. Acessar dados compartilhados por Empresas e ONGs.

  2. Processar nossos dados em Algoritmos desenvolvidos por terceiros.

Nosso foco será no primeiro. Porém, podemos no decorrer do curso aprender mais sobre o uso de API para acessar modelos disponíveis na internet.

APIs hoje são a principal forma de compartilhamento de dados. Há muitas APIs disponíveis para acesso. Por exemplo, Twitter, Facebook, Spotify, Yelp, Portal da Transparência, IPEA.. a lista é longa. Veja abaixo.

E mais recentemente há diversas APIs sendo criadas para permitir análise de dados usando inteligência artificial. Por exemplo, você pode acessar o algoritmo do google para detectar toxicidade em comentários online para classificação de imagens, ou para detectar gastos suspeitos de políticos no Brasil. Entre mutas outras opções.

Vamos cobrir neste workshop somente o acesso a dados via APIs. Porém, fiquem de olho na segunda onda de inovações. Tudo se move muito rápido neste campo.

Uso de API para acesso à dados.

Em seu formato mais simples, uma API é somente uma url. Veja o exemplo abaixo:

http://meusite.com/key?param_1;param_2

Principais elementos:

  • http://meusite.com/: a base da API, chamado nas documentações de end-point.
  • key: as credenciais que muitos sites pedem - nem todos, como veremos.
  • ?param_1;param_2 parametros, ou filtros para refinar a busca da API.

Com R, podemos simplesmente usar os parâmetros que informam a API para nos fornecer os dados. Acessamos e limpamos via R, e teremos nossos dados organizados. Para acessar a API utilizarmos o pacote httr. E para limpar, usaremos diversas das funções do tidyverse que aprendemos anteriormente

Como Acessar Essa API?

Nosso primeiro passo é descobrir se determinado site ou dados que estamos interessados possui API. Se sim, teremos duas formas de acessar esta API.

1. Verificar se há um pacote de R criado para acessar esta API.

Muita vezes, outros programadores precisaram dos meus dados que você. E por isso, estes colegas já criaram um pacote de R para acessar estes dados. Se o pacote existir, use-o. Isto lhe salvará muito tempo. Site como youtube, twitter, New York Times, Congresso Brasileiro, IPEA, entre muitos outros, possuem API já desenhada para acesso dos dados.

2. Caso não haja um pacote, crie suas funções para acessar os dados.

Vamos iniciar pelo segundo caso para aprender como funcionam APIs, e depois veremos diversos exemplos de pacotes já desenvolvidos.

Uso simple de APIs.

Vamos iniciar com um exemplo de API onde não precisamos solicitar senha de acesso. Começaremos com uma API bem simples chamada DOG API. Esta API contêm um repositório de imagens de cachorros, e a cada acesso a ela pegaremos uma imagem diferente.

Primeiro Passo: Encontre as Endpoints

Abra o site e leia a documentação. A primeira informação que precisamos encontrar é quais são os endpoints de cada API. Os endpoints contêm o tipo de informação que a API disponibiliza.

Esta API possui diversos endpoints

Segundo Passo: Procure Filtros

Esta API é bem simples. Não possui filtros mais complexos. Vamos ver outros exemplos com filtros mais na frente.

Terceiro Passo: Acesse via Get.

Todo o acesso para solicitar informação de uma API ocorre a partir da função GET. O retorno será uma arquivo do tipo “response”, e descreve exatamente o retorno da API.

Vamos acessar uma foto aleatória da API.

library(httr)
library(tidyverse)
# Acesse a API
endpoint = "https://dog.ceo/api/breeds/image/random"

acesso = GET(endpoint)

# O que é este arquivo?
class(acesso) 
[1] "response"

Quarto Passo: Acesse os dados via content.

A função content dará a você o acesso as respostas de cada GET request da API. A partir daí, podemos limpar e usar nossos dados.

# Veja os elementos
content(acesso)
$message
[1] "https://images.dog.ceo/breeds/finnish-lapphund/mochilamvan.jpg"

$status
[1] "success"
# Acesso a mensagem de resposta
link_image <- content(acesso)$message
download.file("https://images.dog.ceo/breeds/poodle-standard/n02113799_1439.jpg", 
              destfile = "cao.png")

Para cada tipo de API, a estrutura de dados de retorno será distinta. No entanto, a maioria retorna dados no formato JSON. Vamos mais na frente aprender um pouco mais sobre eles.

Fogo Cruzado API

Vamos agorar usar como exemplo a API do projeto Fogo Cruzado.

Neste caso, a API exige credenciais, e este sempre deve ser nosso primeiro passo. Aqui vocês encontram a descrição sobre como ganhar sua senha de acesso. Vamos ver como fazer no código abaixo.

Acessando sua senha

# Pacotes para acessar APIs
library(httr) 
library(jsonlite)
library(tidyverse)

# Solicitar a senha
get_jwt <- httr::POST("https://api.fogocruzado.org.br/api/v1/auth/login",
                 query = list(email = "venturat@umd.edu", password = "xxxxxx"))


# Pegar a senha
token <- httr::content(get_jwt)$access_token

# token = sua senha de acesso à API. Nao compartilhe por ai.

Agora que temos nossa credencial, vamos olhar a documentação da API. Neste link vocês encontrarão a documentação.

Base da API (End Point)

Filtros da API

A principal informação do site do fogo cruzado são as ocorrências de tiroteio no Rio de Janeiro e Recife. Na documentação, indica-se que há três endpoints principais para a APIÇ: cidades, estados e ocorrências.

Vamos começar pelo endpoint cidades, um fácil que não exige filtros. Abre este link da documentação

# Passo 1. Crie a Url
base_url <- "https://api.fogocruzado.org.br/api/v1"
cities <- "/cities"
api <- paste0(base_url, cities)
print(api)
[1] "https://api.fogocruzado.org.br/api/v1/cities"

GET Request

Para solicitar os dados de uma API

# Passo 2: Acesse a API

response <- GET(api,  
                add_headers('Authorization' = paste("Bearer", token, sep = " ")))

# Qual o resultado?
response
Response [https://api.fogocruzado.org.br/api/v1/cities]
  Date: 2022-06-19 15:12
  Status: 200
  Content-Type: application/json
  Size: 6.89 kB

Limpando Resultado

O retorno da API é um arquivo em JSON - que é um tipo mais eficiente para salvar arquivos grandes - e possui status 200 - que significa seu acesso funcionou. Vamos agora limpar esse arquivo JSON.

Vamos limpar arquivos de json pode ser complicado, por isso, vamos tocar somente a superficie aqui.

# Converter para um json
json_fogo_cruzado <- content(response, as="text", encoding = "UTF-8")

Isto é o que um arquivo de JSON parece. O código acima somente converte a conexão com a API para um longo texto. Esse texto possui separadores - muitas vezes hierarquicos - e chamamos esse arquivo de JSON. O pacote de R rjson permite-nos transformar este arquivo em um banco de dados

# Limpando Jsons
output <- fromJSON(json_fogo_cruzado) %>%
              tibble::as_tibble()
output
# A tibble: 36 × 9
   CidadeId EstadoId Cidade                 CodigoIBGE Gentilico Populacao  Area
      <int>    <int> <chr>                       <int> <chr>         <int> <int>
 1     3253       17 Goiana                    2606200 "goianen…     75644 44581
 2     3185       17 Abreu e Lima              2600054 "abreu-l…     94429 12619
 3     3196       17 Araçoiaba                 2601052 "araçoia…     18156  9638
 4     3215       17 Cabo de Santo Agostin…    2602902 "cabense"    185025 44874
 5     3221       17 Camaragibe                2603454 "camarag…    144466  5126
 6     3259       17 Igarassu                  2606804 "igarass…    102021 30556
 7     3261       17 Ilha de Itamaracá         2607604 "itamara…     21884  6668
 8     3264       17 Ipojuca                   2607208 "ipojuqu…     80637 52711
 9     3270       17 Itapissuma                2607752 "itapiss…     23769  7424
10     3272       17 Jaboatão dos Guararap…    2607901 "jaboatã…    644620 25869
# … with 26 more rows, and 2 more variables: DensidadeDemografica <chr>,
#   PIBPrecoCorrente <lgl>

API com Filtros.

Todas as APIs de verdade possuem parâmetros para filtrar os acessos. Por exemplo, quando usamos a API do twitter e pedimos para buscar tweets mencionando determinada palavra, precisamos adicionar um filtro ao chamado principal da API.

No caso do Fogo Cruzado, precisamos de filtros para o período de busca. Para acessarmos as ocorrências de tiroteio, é preciso pedir dados de no máximo 7 meses de intervalo.

Para isso, devemos adicionar uma query de filtros na função GET. Estes filtros devem ser solicitados de acordo com a documentação da API. Veja aqui alguns exemplos

# url basica de ocorrencias.
base_url <- "https://api.fogocruzado.org.br/api/v1"
occurences <- "/occurrences"
api <- paste0(base_url, occurences)
print(api)
[1] "https://api.fogocruzado.org.br/api/v1/occurrences"
# Cria Query
query_list <- list(data_ocorrencia="2019-01-01", 
                   nome_cidade= "Rio de Janeiro")
# GET
response <- GET(api,
                query=query_list,
              add_headers('Authorization' = paste("Bearer", token, sep = " ")))

output <- jsonlite::fromJSON(httr::content(response, as="text", encoding = "UTF-8")) %>%
    tibble::as_tibble()

View(output)

Escrever o endpoint inteiro

Uma opção é escrever o endpoint junto com os filtros diretamente. Qualquer caminho funciona bem, apesar do uso de query ser mais geral.

# url basica de ocorrencias.
base_url <- "https://api.fogocruzado.org.br/api/v1"
occurences <- "/occurrences"
filter= "?data_ocorrencia[gt]=2019-01-01&data_ocorrencia[lt]=2019-05-01&CidadeID[]=3661"
api <- paste0(base_url, occurences, filter)
print(api)
[1] "https://api.fogocruzado.org.br/api/v1/occurrences?data_ocorrencia[gt]=2019-01-01&data_ocorrencia[lt]=2019-05-01&CidadeID[]=3661"
# GET
response <- GET(api,
              add_headers('Authorization' = paste("Bearer", token, sep = " ")))

output <- jsonlite::fromJSON(httr::content(response, as="text", encoding = "UTF-8")) %>%
    tibble::as_tibble()
GET()
Error in handle_url(handle, url, ...): Must specify at least one of url or handle
output
# A tibble: 3,334 × 67
   id_ocorrencia local_ocorrencia              latitude_ocorre… longitude_ocorr…
           <int> <chr>                                    <dbl>            <dbl>
 1         26653 Parque Sao Jose, Belford Rox…            -22.7            -43.3
 2         24842 Malvinas, Vila Kennedy - Ban…            -22.9            -43.5
 3         23155 R. Carbonita - Bráz De Pina,…            -22.8            -43.3
 4         23157 Pavão-Pavãozinho, Copacabana…            -23.0            -43.2
 5         23158 R. Miguel Cervantes - Cacham…            -22.9            -43.3
 6         23159 Senador Camará, Rio de Janei…            -22.9            -43.5
 7         23162 R. Mario Behring - Vila Rosá…            -22.7            -43.3
 8         23163 R. Miguel Cervantes - Cacham…            -22.9            -43.3
 9         23169 Copacabana - Copacabana, Rio…            -23.0            -43.2
10         23170 Mangueirinha, Periquitos, Du…            -22.8            -43.3
# … with 3,324 more rows, and 63 more variables: data_ocorrencia <chr>,
#   hora_ocorrencia <chr>, presen_agen_segur_ocorrencia <int>,
#   qtd_morto_civil_ocorrencia <int>, qtd_morto_agen_segur_ocorrencia <int>,
#   qtd_ferido_civil_ocorrencia <int>, qtd_ferido_agen_segur_ocorrencia <int>,
#   estado_id <int>, cidade_id <int>, nome_cidade <chr>, cod_ibge_cidade <int>,
#   gentilico_cidade <chr>, populacao_cidade <int>, area_cidade <int>,
#   densidade_demo_cidade <chr>, nome_estado <chr>, uf_estado <chr>, …

Visualização com Mapas

Podemos visualizar essa informação com mapas. A função get_googlemap na verdade acessa a API do google maps e faz download do mapa do Rio de Janeiro. Para isso, você precisa liberar o seu acesso à API via google aqui. Isso fica como um exercício para quarta-feira.

library(ggmap)
library(RColorBrewer)
ggmap(get_googlemap("rio de janeiro", zoom = 11, maptype = "roadmap", scale=2)) +
    geom_point(data = output, 
               aes(x = longitude_ocorrencia, 
               y = latitude_ocorrencia), 
               color="tomato2", alpha=.8, size = 2)
Error in aperm.default(map, c(2, 1, 3)): invalid first argument, must be an array

But! Nós chechamos se havia algum pacote de R disponível para o Fogo Cruzado?

Uma das maiores vantagens do uso de R reside no fato de se tratar de uma linguagem gratuita e de código aberto. Como resultado, há milhares de programadores trabalhando em desenvolver pacotes e tornando-os acessíveis ao público. Há dois lugares onde esses pacotes são hospedados. O repositório oficial do R chamado CRAN, ou no github - um local onde programadores tornam seus códigos disponíveis.

Se alguem tiver desenvolvido um pacote para acessar a API, você vai ganhar muito tempo usando o pacote diretamente, ao invés de entender toda a documentação da API, como fizemos. Vamos buscar o pacote de R para acessar o Fogo Cruzado.

Este link contém o site do pacote de R, e as recomendações de como utilizá-lo. Segue a instalação:

#Instalação do pacote
install.packages("devtools") # pacote para acessar o github
devtools::install_github("voltdatalab/crossfire")
library(crossfire)

# Registra usuario e senha, e envia sua senha da API

fogocruzado_signin(email = "venturat@umd.edu", password = "xxxxxx")

# Extrair os dados que pegamos manualmente antes

fogocruzado_rj <- get_fogocruzado(state= "RJ", security_agent = 1)
?get_fogocruzado
# Colocar em gráfico mais uma vez. 

ggmap(get_googlemap("rio de janeiro", zoom = 11, maptype = "roadmap", scale=2)) +
    geom_point(data = fogocruzado_rj, 
               aes(x = longitude_ocorrencia, 
               y = latitude_ocorrencia), 
                alpha=.8, size = 0.5, color="darkred")
Error in aperm.default(map, c(2, 1, 3)): invalid first argument, must be an array

Muito mais fácil!

Congressbr

Para terminar o dia de hoje, vamos praticar um pouco mais com o excelente pacote congressbr. Este artigo sobre o pacote oferece uma excelente introdução à como utilizá-lo, e um sumário de outros pacotes para facilitar acesso à APIs com dados brasileiros.

# Instale o pacote
install.packages("congressbr")
devtools::install_github("RobertMyles/congressbr")

O pacote permite o acesso às APIs da Câmara e do Senado. Há quatro funções principais no pacote

  • cham_votes(): retorna informação sobre votações na Câmara.
  • cham_bills(): retorna informação sobre atividade legislativa na Câmara.
  • sen_votes(): retona informação de voto no Senado.
  • sen_bill_search(): Procura por atividade legislativa no Senado.

Caso você queira entender mais sobre cada uma das funções, há alguns caminhos. Em primeiro lugar, procure a documentação da API. Em segundo, leia a documentação do pacote ou a sua página no github. Outra opção, é pedir ajuda para entender a função de seu interesse.

Vamos ver algumas das funções do pacote abaixo. Em primeiro lugar, darei um exemplo de como pedir ajudar, e entender a função.

library(congressbr)

# Ajuda em R
?cham_legislator_list

Essa imagem irá aparecer diretamente no seu R Studio. Ela explica o que a função faz, o que retorna, e exemplo de como utilizá-la. Somente de copiar e colar o exemplo, você já terá uma boa dimensão da função.

all <- cham_legislator_list()
glimpse(all)
Rows: 513
Columns: 13
$ legislator_id               <chr> "73701", "73696", "73801", "74848", "74459…
$ legislator_status           <chr> "Titular", "Titular", "Titular", "Titular"…
$ legislator_name             <chr> "BENEDITA SOUZA DA SILVA SAMPAIO", "ANGELA…
$ legislator_cham_name        <chr> "Benedita da Silva", "Angela Amin", "Renil…
$ legislator_photo_url        <chr> "http://www.camara.gov.br/internet/deputad…
$ legislator_gender           <chr> "feminino", "feminino", "masculino", "femi…
$ legislator_state            <chr> "RJ", "SC", "PE", "RJ", "PI", "PR", "PE", …
$ legislator_party            <chr> "PT", "PP", "PCdoB", "PCdoB", "PP", "CIDAD…
$ legislator_cabinet          <chr> "330", "252", "915", "622", "640", "916", …
$ legislator_building_address <chr> "4", "4", "4", "4", "4", "4", "4", "4", "4…
$ legislator_telephone_number <chr> "3215-5330", "3215-5252", "3215-5915", "32…
$ legislator_email            <chr> "dep.beneditadasilva@camara.leg.br", "dep.…
$ legislator_comissions       <chr> "", "", "", "", "", "", "", "", "", "", ""…

Abaixo, vou dar um exemplo de uso do pacote.

Número de Proposições por ano

ano<-c(2002:2018)

proposicoes <- map(ano, ~ 
                     cham_plenary_bills(.x) %>%
                     mutate(ano=.x)) 

# Vamos combinar tudo 

proposicoes <- bind_rows(proposicoes) 

# Eliminar repeticoes 

proposicoes <- proposicoes %>% distinct()

# Agregar por ano

proposicoes_ano <- proposicoes %>% count(ano)

# Marcar anos pre eleitorais
proposicoes_ano <- proposicoes_ano %>% 
                      mutate(ano_eleitoral=ifelse(ano==2002|ano==2006|
                                                    ano==2010|ano==2014|ano==2018, "Ano Eleitoral", 
                                                  "Ano Não Eleitoral"))

ggplot(proposicoes_ano, aes(y=n, x=ano, fill=ano_eleitoral)) +
  geom_col() +
  scale_fill_manual(name="", values = c("darkred", "darkblue")) +
  theme_minimal() +
  xlab("ano") + ylab("Proposições Votadas") 

Mais uma vez. O pacote permite fácil e rápido acesso a uma quantidade enorme de dados. Basta nós cientistas sociais aprendermos como utilizar-los.

Exercício (Quarta-Feira)

  1. Pratique o que aprendemos buscando informações na API do Congresso. Aqui segue o endereço da API. O desafio é você conseguir acertar alguns requests usando GET e depois limpar os dados do json.
  1. Encontre uma API com um pacote de R escrito. Instale o pacote, e utilize-o corretamente. A API do new york times, o youtube, o google maps, o twitter e o spotify podem ser boas opções. Façam o cadastro necessário, acessam suas senhas, e brinquem com as apis.