flowchart LR
A[rvest] --> E[Extração de HTML]
B[httr2] --> F[Requisições HTTP]
C[xml2] --> G[Parsing de XML/HTML]
D[polite] --> H[Scraping ético]
I[dplyr + stringr] --> J[Limpeza e organização]
Coletando dados do IBGE diretamente no R
Use, compartilhe, a aula está no github dentro do meu site.
Aproveitem muito!!! Ahhh essa é a meguy, minha gatinha linda, ela está na logo do café com R!
Todos os códigos da aula estão funcionais. Prontos para reproduzir.
Web scraping é o processo automatizado de extrair dados de páginas web.
Note
Antes de coletar dados, verifique sempre o arquivo robots.txt e os termos de uso do site.
| Característica | Web scraping | API |
|---|---|---|
| Estrutura dos dados | HTML não estruturado | JSON/XML estruturado |
| Estabilidade | Frágil (depende do layout) | Estável |
| Permissão | Verificar terms of use | Geralmente documentada |
| Facilidade | Média | Alta |
flowchart LR
A[rvest] --> E[Extração de HTML]
B[httr2] --> F[Requisições HTTP]
C[xml2] --> G[Parsing de XML/HTML]
D[polite] --> H[Scraping ético]
I[dplyr + stringr] --> J[Limpeza e organização]
O pacote principal da aula. Desenvolvido pela Posit, faz parte do ecossistema tidyverse.
read_html()html_element() e html_elements()html_text2()html_table()html_attr()Responsável por fazer as requisições HTTP de forma controlada.
Dica
Em muitos casos simples, o rvest já faz a requisição internamente. O httr2 é útil para casos mais avançados.
Biblioteca de baixo nível que o rvest usa internamente.
Pacote que garante um scraping respeitoso e ético.
robots.txt automaticamenteuser-agent descritivocrawl_delay)Warning
Usar o polite é uma boa prática. Scraping agressivo pode sobrecarregar servidores e resultar em bloqueio de IP.
Seletores CSS identificam elementos dentro do HTML.
| Seletor | O que seleciona |
|---|---|
h1 |
Todas as tags <h1> |
.descricao |
Elementos com class descricao |
#dados |
Elemento com id dados |
table td |
<td> dentro de <table> |
a[href] |
Links com atributo href |
F12)Dica
No Chrome e Firefox, a extensão SelectorGadget facilita muito a identificação de seletores CSS.
library(rvest)
url <- "https://www.ibge.gov.br/cidades-e-estados.html"
pagina <- read_html(url)
pagina{html_document}
<html lang="pt-BR">
[1] <head>\n<meta name="viewport" content="width=device-width, initial-scale= ...
[2] <body>\r\n<!-- Google Tag Manager (noscript) -->\r\n<noscript><iframe src ...
O objeto retornado é um documento HTML que pode ser navegado com os demais verbos do rvest.
library(polite)
sessao <- bow(
url = "https://www.ibge.gov.br",
user_agent = "Aula Web Scraping - Cafe com R / contato@exemplo.com"
)
sessao<polite session> https://www.ibge.gov.br
User-agent: Aula Web Scraping - Cafe com R / contato@exemplo.com
robots.txt: 57 rules are defined for 1 bots
Crawl delay: 5 sec
The path is scrapable for this user-agent
O bow() verifica se a coleta é permitida e configura o intervalo de espera sugerido pelo servidor.
Note
O scrape() substitui o read_html() quando usamos o fluxo do polite. Ele respeita o crawl_delay automaticamente.
O IBGE disponibiliza uma tabela pública com os códigos dos municípios brasileiros.
Rows: 27
Columns: 2
$ UFs <chr> "Acre", "Alagoas", "Amapá", "Amazonas", "Bahia", "Ceará", "Dis…
$ Códigos <chr> "12ver municípios", "27ver municípios", "16ver municípios", "1…
A página ibge.gov.br/cidades-e-estados.html carrega os dados via JavaScript dinâmico.
read_html() captura apenas o HTML estático.tile__estado retornam vazio porque o conteúdo não existe no HTML brutoNote
Este é um caso real e muito comum no scraping. A solução correta é usar a API oficial do IBGE, que retorna os mesmos dados de forma estruturada.
F12)Dica
Quando o html_elements() retorna um vetor vazio, suspeite de conteúdo dinâmico. O DevTools revela a API por trás da página.
O IBGE disponibiliza uma API REST pública e gratuita.
servicodados.ibge.gov.br/api/v1/localidades/estadosO request() cria a requisição e req_perform() a executa.
O resp_body_string() extrai o conteúdo como texto e o fromJSON() converte para data frame automaticamente.
library(ggplot2)
estados <- estados |>
count(nome_regiao) |>
ggplot(aes(
x = reorder(nome_regiao, n),
y = n,
fill = nome_regiao)) +
geom_col(show.legend = FALSE) +
coord_flip() +
labs(
title = "Quantidade de estados por região.",
x = NULL,
y = "Número de estados",
caption = "Fonte: API IBGE Localidades | Café com R.") +
theme_classic(base_size = 13) +
theme(plot.title = element_text(face = "bold"))Continuando com a tabela de municípios do Exemplo 1:
library(stringr)
tabela_limpa <- tabela_municipios |>
rename_with(~ str_to_lower(str_replace_all(., " ", "_"))) |>
mutate(across(where(is.character), str_squish))
glimpse(tabela_limpa)Rows: 27
Columns: 2
$ ufs <chr> "Acre", "Alagoas", "Amapá", "Amazonas", "Bahia", "Ceará", "Dis…
$ códigos <chr> "12ver municípios", "27ver municípios", "16ver municípios", "1…
Quando uma coluna contém dois dados juntos, como "São Paulo - SP":
library(tidyr)
# Exemplo com vetor simulado para demonstração
exemplo <- tibble(
municipio_uf = c("São Paulo - SP", "Rio de Janeiro - RJ", "Belo Horizonte - MG"))
exemplo |>
separate(
col = municipio_uf,
into = c("municipio", "uf"),
sep = " - ",
extra = "merge")# A tibble: 3 × 2
municipio uf
<chr> <chr>
1 São Paulo SP
2 Rio de Janeiro RJ
3 Belo Horizonte MG
O IBGE disponibiliza dados de PIB per capita dos estados via API.
sidrar para acessar diretamenteNote
O sidrar é o pacote oficial para acessar o banco de dados SIDRA do IBGE diretamente no R, sem precisar de scraping.
library(sidrar)
library(dplyr)
# Tabela 5938 — PIB per capita dos estados (último ano disponível)
pib_bruto <- get_sidra(
x = 5938,
variable = 37,
geo = "State",
period = "last")
glimpse(pib_bruto)Rows: 27
Columns: 11
$ `Nível Territorial (Código)` <chr> "3", "3", "3", "3", "3", "3", "3", "3"…
$ `Nível Territorial` <chr> "Unidade da Federação", "Unidade da Fe…
$ `Unidade de Medida (Código)` <chr> "40", "40", "40", "40", "40", "40", "4…
$ `Unidade de Medida` <chr> "Mil Reais", "Mil Reais", "Mil Reais",…
$ Valor <dbl> 76456179, 26291321, 161794976, 2512480…
$ `Unidade da Federação (Código)` <chr> "11", "12", "13", "14", "15", "16", "1…
$ `Unidade da Federação` <chr> "Rondônia", "Acre", "Amazonas", "Rorai…
$ `Ano (Código)` <chr> "2023", "2023", "2023", "2023", "2023"…
$ Ano <chr> "2023", "2023", "2023", "2023", "2023"…
$ `Variável (Código)` <chr> "37", "37", "37", "37", "37", "37", "3…
$ Variável <chr> "Produto Interno Bruto a preços corren…
pib <- pib_bruto |>
select(
estado = `Unidade da Federação`,
pib_per_capita = Valor) |>
filter(!is.na(pib_per_capita)) |>
mutate(pib_per_capita = as.numeric(pib_per_capita)) |>
arrange(desc(pib_per_capita))
head(pib, 3) estado pib_per_capita
1 São Paulo 3444814033
2 Rio de Janeiro 1172871443
3 Minas Gerais 971977551
grafico_pib_completo <- grafico_pib +
scale_fill_manual(values = paleta_cafe) +
scale_y_continuous(
labels = scales::label_number(
big.mark = ".",
decimal.mark = ",")) +
labs(
title = "10 estados com maior PIB per capita.",
subtitle = "Dados coletados via API SIDRA do IBGE.",
x = NULL,
y = "PIB per capita (R$)",
caption = "Fonte: IBGE - SIDRA | Café com R.") +
theme_classic(base_size = 13) +
theme(
plot.title = element_text(face = "bold", color = "#3E2723"),
plot.subtitle = element_text(color = "#6D4C41"),
axis.text = element_text(color = "#4E342E"),
axis.title.y = element_text(color = "#3E2723"),
plot.caption = element_text(color = "#6D4C41"))Muitos sites organizam o conteúdo em várias páginas. Para coletar tudo, é preciso iterar sobre as URLs.
purrr::map() ou um loopbind_rows()library(purrr)
library(rvest)
# Função para extrair uma página
extrair_pagina <- function(url) {
Sys.sleep(1) # intervalo obrigatório entre requisições
read_html(url) |>
html_element("table") |>
html_table()
}
# Vetor de URLs com padrão de paginação
urls <- paste0(
"https://quotes.toscrape.com/page/",
1:5, "/")
# Iteração segura com possibly() para não parar em erros
resultado <- map(urls, possibly(extrair_pagina, NULL)) |>
compact() |>
bind_rows()Warning
Sempre inclua Sys.sleep() entre requisições. O possibly() evita que um erro em uma página quebre toda a coleta.
robots.txt (site.com/robots.txt)user-agent descritivoSys.sleep() entre requisiçõesNote
O IBGE possui uma API oficial (SIDRA e IBGE API) que deve ser preferida ao scraping direto sempre que os dados estiverem disponíveis por lá.
library(sidrar)
# Exemplo: população estimada por estado
populacao <- get_sidra(
x = 6579,
variable = 9324,
geo = "State",
period = "last")
populacao |>
select(
estado = `Unidade da Federação`,
pop = Valor) |>
arrange(desc(pop)) |>
head(10) estado pop
1 São Paulo 46081801
2 Minas Gerais 21393441
3 Rio de Janeiro 17223547
4 Bahia 14870907
5 Paraná 11890517
6 Rio Grande do Sul 11233263
7 Pernambuco 9562007
8 Ceará 9268836
9 Pará 8711196
10 Santa Catarina 8187029
| Função | Pacote | Para que serve |
|---|---|---|
read_html() |
rvest | Ler página HTML |
html_elements() |
rvest | Selecionar múltiplos elementos |
html_element() |
rvest | Selecionar um elemento |
html_text2() |
rvest | Extrair texto |
html_table() |
rvest | Extrair tabela |
| Função | Pacote | Para que serve |
|---|---|---|
html_attr() |
rvest | Extrair atributo |
bow() / scrape() |
polite | Scraping ético |
request() |
httr2 | Requisição HTTP |
fromJSON() |
jsonlite | Converter JSON para data frame |
rvest torna o processo simples e integrado ao tidyversequotes.toscrape.comsidrar para dados tabularesImagem: Allison Horst.
Continue praticando e explorando!
Esta apresentação é parte do projeto Café com R! É OPEN, USE, COMPARTILHE!
Fique por dentro das aulas, conteúdos, newsletter!
Que cada gole desperte uma nova ideia.
Que cada script abra uma nova conversa.
Que o Café com R, se torne um ponto de encontro nosso!

Jennifer Lopes | Café com R