Introdução
Esse post faz parte de uma série sobre acesso à APIs com R! O primeiro post foi uma introdução sobre como acessar APIs com R.
Neste post mostraremos um exemplo usando a API do GitHub.
O GitHub é uma plataforma onde conseguimos hospedar repositórios (pastas com nossos códigos e arquivos) com controle de versão usando o Git, e podemos fazer muitas coisas utilizando a sua API. E como dissemos no post anterior: “o primeiro passo para acessar qualquer API é procurar uma documentação”. A boa notícia é que a documentação da API do GitHub está disponível em Português e é bem detalhada!
Existem muitas ações possíveis utilizando essa API. O que escolhemos para esse exemplo é buscar os repositórios que pertencem à uma organização.
Segundo a documentação, para consultar os repositórios que pertencem à organização octokit, podemos utilizar a seguinte busca:
GET /orgs/octokit/repos
O equivalente a isso usando o pacote httr
é:
# url_base - nunca muda na mesma API
url_base <- "https://api.github.com"
# endpoint - é o que muda o resultado
endpoint <- "/orgs/octokit/repos"
# precisamos colar os textos para criar o link
u_github <- paste0(url_base, endpoint)
# ver como o texto ficou colado
# u_github
# > "https://api.github.com/orgs/octokit/repos"
# fazer a requisição do tipo GET
r_github <- httr::GET(u_github)
r_github
## Response [https://api.github.com/orgs/octokit/repos]
## Date: 2022-02-19 16:45
## Status: 200
## Content-Type: application/json; charset=utf-8
## Size: 181 kB
## [
## {
## "id": 417862,
## "node_id": "MDEwOlJlcG9zaXRvcnk0MTc4NjI=",
## "name": "octokit.rb",
## "full_name": "octokit/octokit.rb",
## "private": false,
## "owner": {
## "login": "octokit",
## "id": 3430433,
## ...
Podemos acessar o resultado usando a função httr::content()
, porém não vamos colocar o resultado no post pois ficaria muito longo.
# httr::content(r_github)
O que é o pacote gh
?
O pacote gh permite acessar a API do GitHub. A lógica mostrada anteriormente se aplica para esse pacote também: precisaremos consultar a documentação para verificar como fazer alguma tarefa com a API.
Primeiro exemplo com o pacote gh
Neste exemplo, vamos buscar as informações sobre os repositórios que são organização Curso-R no GitHub, gerar um dataframe, e ao final fazer uma visualização simples.
Informações gerais da organização Curso-R
Podemos buscar informações sobre a organização da Curso-R no GitHub:
gh_curso_r <- gh::gh("GET /orgs/{org}",
org = "curso-r")
A sintaxe do pacote gh
é similar ao glue
. Quando queremos buscar uma informação que está em uma variável (no caso “curso-r”), colocamos os {variavel}
no primeiro argumento e escrevemos variavel=
nos argumentos seguintes.
Como vimos, o resultado é uma lista. Para consultar o número de repositórios públicos, podemos usar o $
para acessar essa informação dentro da lista:
gh_curso_r$public_repos
## [1] 303
Olha só, a Curso-R tem atualmente 303 repositórios públicos no GitHub! Temos muitos repositórios pois criamos um diferente para cada curso, para que quem faz aula com a gente tenha sempre um lugar para olhar todos os materiais, de forma organizada e independente.
Acessando informações de repositórios
Podemos buscar informações sobre os repositórios que pertencem à organização Curso-R no GitHub:
repositorios_cursor <- gh::gh("GET /orgs/{org}/repos", org = "curso-r")
# A classe que retorna é uma lista
class(repositorios_cursor)
## [1] "gh_response" "list"
# É uma lista grande!
length(repositorios_cursor)
## [1] 30
Esse código retornou informações de apenas 30 repositórios. Portanto, precisamos repetir o processo para obter informações de todos os repositórios.
Iterando com purrr e o pacote gh
A documentação do pacote aponta que é possível buscar informações de 100 repositórios por vez. Se queremos buscar todos os repositórios, primeiro precisamos calcular quantas vezes vamos repetir o processo todo:
numero_repos_publicos <- gh_curso_r$public_repos
# podemos buscar 100 repositórios por vez,
# então podemos dividir o numero de repositorios
# por 100, e arredondar "para cima"
# (é para isso que a função ceiling() serve!)
numero_paginas <- ceiling(numero_repos_publicos/100)
numero_paginas
## [1] 4
Precisaremos repetir 4 vezes! Agora podemos usar a função purrr::map()
para repetir o acesso à API quantas vezes forem necessárias para obter as informações de todos os repositórios da Curso-R:
repos_cursor <- purrr::map(1:numero_paginas,
.f = ~gh::gh(
"GET /orgs/{org}/repos",
org = "curso-r", # organizacao
type = "public", # tipo de repositorio
sort = "updated", # forma de ordenar a busca
per_page = 100, # numero de resultados por pagina
page = .x # numero da pagina que será substituido
))
O resultado ainda é uma lista… podemos usar a magia do pacote purrr e transformar essa lista em um dataframe:
lista_repos <- repos_cursor |>
purrr::flatten() |>
purrr::map(unlist, recursive = TRUE) |>
purrr::map_dfr(tibble::enframe, .id = "id_repo") |>
tidyr::pivot_wider() |>
janitor::clean_names()
lista_repos
## # A tibble: 303 × 108
## id_repo id node_id name full_name private owner_login owner_id
## <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
## 1 1 311969160 MDEwOlJlcG9za… chess curso-r/… FALSE curso-r 10060716
## 2 2 154844030 MDEwOlJlcG9za… auth0 curso-r/… FALSE curso-r 10060716
## 3 3 316277552 MDEwOlJlcG9za… main… curso-r/… FALSE curso-r 10060716
## 4 4 431987860 R_kgDOGb-clA 2022… curso-r/… FALSE curso-r 10060716
## 5 5 249453848 MDEwOlJlcG9za… tree… curso-r/… FALSE curso-r 10060716
## 6 6 272436141 MDEwOlJlcG9za… base… curso-r/… FALSE curso-r 10060716
## 7 7 445626145 R_kgDOGo-3IQ main… curso-r/… FALSE curso-r 10060716
## 8 8 163780826 MDEwOlJlcG9za… shin… curso-r/… FALSE curso-r 10060716
## 9 9 436799548 R_kgDOGgkIPA scryr curso-r/… FALSE curso-r 10060716
## 10 10 431978304 R_kgDOGb93QA 2022… curso-r/… FALSE curso-r 10060716
## # … with 293 more rows, and 100 more variables: owner_node_id <chr>,
## # owner_avatar_url <chr>, owner_gravatar_id <chr>, owner_url <chr>,
## # owner_html_url <chr>, owner_followers_url <chr>, owner_following_url <chr>,
## # owner_gists_url <chr>, owner_starred_url <chr>,
## # owner_subscriptions_url <chr>, owner_organizations_url <chr>,
## # owner_repos_url <chr>, owner_events_url <chr>,
## # owner_received_events_url <chr>, owner_type <chr>, …
Vamos fazer mais uma etapa de organização dos dados: são muitas colunas, e não precisaremos de todas para terminar o post. Também filtramos a base para remover os forks, já que não seriam repositórios da Curso-R originalmente.
df_repos_cursor <- lista_repos |>
dplyr::filter(fork == FALSE) |>
dplyr::select(
name,
created_at,
default_branch
) |>
dplyr::mutate(
data_criacao = readr::parse_datetime(created_at),
ano_criacao = as.Date(lubridate::floor_date(data_criacao, "year"))
)
Exemplo de visualização com os dados obtidos!
Em 2020, o Caio escreveu um post sobre o uso do termo ‘master’ no GitHub. Lá no post é explicado sobre a questão da substituição do termo ‘master’. Em 2020 a GitHub anunciou que faria a transição para o termo main (principal), e desde então muitas pessoas e organizações estão renomeando a branch principal de seus repositórios para ‘main’(inclusive existe um post no blog da RStudio sobre isso).
Usando os dados obtidos nesse post, vamos explorar os repositórios da Curso-R e averiguar qual é o nome da branch principal dos repositórios ao longo do tempo?
library(ggplot2)
main_percent <- mean(df_repos_cursor$default_branch == "main")
main_percent <- scales::percent(main_percent)
df_repos_cursor |>
dplyr::count(ano_criacao, default_branch) |>
ggplot() +
geom_col(aes(y = n, x = ano_criacao, fill = default_branch)) +
theme_bw() +
scale_x_date(date_labels = "%Y", date_breaks = "1 year") +
scale_fill_brewer(palette = "Pastel1") +
labs(x = "Ano de criação", y = "Número de repositórios", fill = "Nome da Branch")
É possível ver que em 2021 o uso do termo ‘main’ para nomear as branches principais foi muito mais usado! Atualmente, o percentual de repositórios main
é de 25% e esperamos que isso aumente com o tempo. Outra coisa legal do gráfico é ver como a criação de repositórios na organização da Curso-R foi crescendo ao longo do tempo!
É isso! Dúvidas, sugestões e críticas, mande aqui nos comentários. Postem também quais exemplos, dentre os que foram listados, vocês gostariam de saber mais!!
Se você quiser saber mais sobre acessar APIs, o curso de Web Scraping é uma ótima oportunidade!
Até a próxima!