key words: Web Scraping, Download de dados, ETL
Entendo estruturas HTML e funcionalidades básicas do pacote rvest
Alguns sites não disponibilizam o download estruturado de dados. Isto é, que permite uma conversão fácil para uma estrutura de dados padrão como data frames, matrizes, etc. O conteúdo de sites disponíveis no próprio endereço da Web comumente utiliza a linguagem HTML para visualização do usuário por meio de um navegador.
Existem dois conceitos chaves relacionados a estrutura de comunicação entre uma máquina cliente e um servidor:
Uma requisição
GET
ocorre quando a máquina cliente envia uma url ao servidor passando parâmetros de consulta na própria url Por isso, nesse caso, os parâmetros são visíveis na url.Uma requisição
POST
ocorre quando a máquina cliente envia uma url ao servidor passando parâmetros de consulta no corpo da solicitação. Normalmente, ele é utilizado quando o usuário necessita preencher algum formulário para que a requisição seja aceita pelo servidor.
O pacote rvest
auxilia no envio dessas requisições e obtenção dessas informações e facilita a conversão para modelos de dados estruturados. Porém, antes de utilizá-lo cabe algumas considerações de conceitos relacionados a linguagem HTML.
O HTML é uma linguagem utilizada para construção de páginas na Web. A estrutura básica é composta por tags que indicam qual o tipo de conteúdo deve ser exibido. Por exemplo, títulos, tabelas, textos corridos, etc.
Cada tag possui um conjunto de “sub tags” ou filhos, que correspondem ao conteúdo daquela seção. Uma tag é aberta pelo comando <nome da tag> e fechada <’/’nome da tag>, como uma espécie de marcação de início e fim da seção.
Vamos ao seguinte exemplo.
library(rvest)
library(xml2)
html_ex<-"<!DOCTYPE html>
<html>
<h1>
TITULO DA PAGINA
</h1>
<p>
Primeiro paragrafo apresentado apos o titulo
</p>
<table border-collapse= collapse width= 100% border= '1px solid #dddddd'>
<tr>
<th>Nome</th>
<th>Sobrenome</th>
<th>Cidade</th>
<th>ID Cliente</th>
</tr>
<tr>
<td>Kauan</td>
<td>Gusman</td>
<td>Sao Paulo</td>
<td>1</td>
</tr>
<tr>
<td>Jonas</td>
<td>Matias</td>
<td>Sorocaba</td>
<td>2</td>
</tr>
<tr>
<td>Vitor</td>
<td>Castro</td>
<td>Belo Horizonte</td>
<td>3</td>
</tr>
</table>
<p>
</p>
<body>
Sequencia do conteudo da pagina.
</body>
</html>"
html_file<-htmltools::HTML(html_ex) %>% read_html()
O código acima cria um documento simples de HTML. Você pode (e recomendamos que faça) ver seu resultado clicando aqui. Copie e cole o código do objeto html_ex
e veja a página gerada.
Nesse código temos as seguintes tags expostas: html, h1, p, table, tr, td e body. Essencialmente (mas não somente) o que o pacote rvest
nos permite é acessar o conteúdo de cada uma dessas tags.
Vamos a uma lista das principais funções do pacote e suas funcionalidades:
Função | Descrição |
---|---|
html_session | Simula uma sessão com um navegador permitindo receber e enviar informações a um servidor |
html_nodes | Extrai tags de um HTML utilizando endereço XPath ou seletores CSS. |
html_table | Extrai e converte uma tag do tipo table para um data frame. |
html_text | Converte o conteúdo de um objeto HTML para um texto. |
html_attr | Extrai atributos de uma tag. |
html_form | Extrai as tags to tipo form de um objeto HTML. |
set_value | Altera o valor de entrada de uma tag do tipo form. |
read_html | Lê um arquivo HTML e permite extração de fragmentos. |
jump_to | Permite migrar uma sessão atualmente em execução para um novo url a partir do link fornecido pelo usuário. |
follow_link | Permite migrar uma sessão atualmente em execução para um novo url a partir do link fornecido por uma tag . |
config | Função do pacote httr (utilizado pelo rvest). Será útil para definição de proxy, cookies, portas, entre outras configurações necessárias para acessar o navegador, principalmente em ambientes de navegação protegidos cujo usuário não é administrador da máquina |
Veja o resultado da aplicação de algumas dessas funções abaixo.
html_file %>% html_nodes("*") %>% html_name() %>% unique() ## Lista todas as Tags presentes no objeto HTML
## [1] "body" "h1" "p" "table" "tr" "th" "td"
html_file %>% html_nodes("table") %>% html_table(fill = TRUE) ## Extrai todas as tags table do objeto HTML e converte-os para uma lista de data frames
## [[1]]
## # A tibble: 3 x 4
## Nome Sobrenome Cidade `ID Cliente`
## <chr> <chr> <chr> <int>
## 1 Kauan Gusman Sao Paulo 1
## 2 Jonas Matias Sorocaba 2
## 3 Vitor Castro Belo Horizonte 3
html_file %>% html_nodes("h1") %>% html_text() ## Extrai o conteúdo da tag h1 e converte em um arquivo de texto
## [1] "\nTITULO DA PAGINA\n"
html_file %>% html_nodes("table") %>% html_attr("width") ## Extrai o atributo de largura da tag table
## [1] "100%"
html_file %>% html_nodes("table,td") %>% html_text() ## Extrai da tag table as sub tags td
## [1] "Nome\n Sobrenome\n Cidade\n ID Cliente\n Kauan\n Gusman\n Sao Paulo\n 1\n Jonas\n Matias\n Sorocaba\n 2\n Vitor\n Castro\n Belo Horizonte\n 3\n "
## [2] "Kauan"
## [3] "Gusman"
## [4] "Sao Paulo"
## [5] "1"
## [6] "Jonas"
## [7] "Matias"
## [8] "Sorocaba"
## [9] "2"
## [10] "Vitor"
## [11] "Castro"
## [12] "Belo Horizonte"
## [13] "3"
html_file %>% html_nodes(xpath = '//table//td') %>% html_text() ## Extrai da tag table as sub tags td utilizando XPath
## [1] "Kauan" "Gusman" "Sao Paulo" "1"
## [5] "Jonas" "Matias" "Sorocaba" "2"
## [9] "Vitor" "Castro" "Belo Horizonte" "3"
html_file %>% html_nodes(xpath = '//table//tr[2]//td[3]') %>% html_text() ## Extrai a terceira célula da segunda linha da tabela (sendo a primeira o cabeçalho) utilizando XPath
## [1] "Sao Paulo"
Veja que para acessar as diferentes informações inseridas no documento é necessário entender como as camadas foram construídas. É possível extraí-los utilizando CSS Selectors ou comandos XPath. Agora você conhece a estrutura básica de um objeto HTML e sabe como o pacote rvest
permite interagir com as diferentes camadas dessa estrutura.
Coletando manchetes de um site de notícias
O primeiro exemplo prático consiste no exercício de extrair notícias de um endereço da Web.
Para isso, será necessário identificar o caminho dos elementos que desejamos extrair, isto é, o conjunto de CSS Selectors ou XPath que acessa-os. Uma forma fácil de fazer isso utiliza uma funcionalidade do próprio navegador. Para isso, siga os seguintes passos:
- Clique com o botão direito do mouse em cima do objeto que deseja extrair (tabela, texto, etc).
- Clique com o botão esquerdo do mouse na opção Inspecionar (ou algo derivado disso).
- Uma nova janela irá abrir com o endereço desse elemento. Informações como tag, classe, e outros atributos serão visíveis.
- Se você estiver utilizando o google chrome é possível copiar os seletores ou XPath do objeto diretamente. Para isso, clique com o botão direto do mouse sobre o texto destacado na nova janela. Siga para “copiar” e escolha a opção desejada.
Feito o reconhecimento do caminho HTML que nós leva ao elemento a ser extraído, podemos seguir para o R. Os procedimentos a seguir executam essa tarefa.
url<-"https://valor.globo.com/"
site_noticia<-url %>% html_session() ## Cria a sessão com o navegador
## A tag "a" com classe "bstn-dedupe-url" é a que guarda o título das notícias que vamos extrair. Veja o código fonte da página e use Ctrl+F para identificar esse elemento.
links<-site_noticia %>% html_nodes("a.bstn-dedupe-url") ## Extrai todos os elementos com classe igual a "bstn-dedupe-url" da tag "a" utilizando CSS Selectors
## site_noticia %>% html_nodes(xpath = '//a[@class="bstn-dedupe-url "]') ## Extrai todos os elementos com classe igual a "bstn-dedupe-url" da tag "a" utilizando XPath
noticias <- data.frame(
"Titulo"=links %>% html_text(trim = TRUE), ## Extrai o títulos das notícias
"Link"=links %>% html_attr("href") ## Extrai o link das notícias
)
É possível utilizar o rvest
para navegar entre páginas. Coletando urls disponíveis na sessão inicialmente aberta e seguindo para novos endereços. Com isso, seria possível utilizar os links coletados anteriormente para obter as notícias na integra, por exemplo.
Vamos refazer o processo anterior coletando as manchetes disponíveis na seção de finanças do Valor Econômico. Os seguintes procedimentos executam a tarefa.
novo_url<-site_noticia %>% html_nodes(xpath = "//li[@id='menu-1-financas']//a[1]") %>% html_attr("href") %>% .[1] ## Obtem o novo url da página a ser aberta
site_noticia<-jump_to(site_noticia, novo_url) ## Envia uma requisição para que a sessão migre para o novo url
links<-site_noticia %>% html_nodes("a.bstn-dedupe-url") ## Extrai todos os elementos com classe igual a "bstn-dedupe-url" da tag "a" utilizando CSS Selectors
noticias <- data.frame(
"Titulo"=links %>% html_text(trim = TRUE), ## Extrai o títulos das notícias
"Link"=links %>% html_attr("href") ## Extrai o link das notícias
)
Titulo | Link |
---|---|
Início do aperto monetário nos EUA torna incertas perspectivas para emergentes, diz FMI | https://valor.globo.com/financas/noticia/2022/01/10/inicio-do-aperto-monetario-nos-eua-torna-incertas-perspectivas-para-emergentes-diz-fmi.ghtml |
Fim de estímulos põe ativos de risco contra a parede | https://valor.globo.com/financas/noticia/2022/01/10/fim-de-estimulos-poe-ativos-de-risco-contra-a-parede.ghtml |
Juros futuros recuam e dólar comercial tem leve alta no início dos negócios | https://valor.globo.com/financas/noticia/2022/01/10/juros-futuros-recuam-e-dolar-comercial-tem-leve-alta-no-inicio-dos-negocios.ghtml |
Fundo imobiliário mostra resistência mesmo na crise | https://valor.globo.com/financas/noticia/2022/01/10/fundo-imobiliario-mostra-resistencia-mesmo-na-crise.ghtml |
XP compra Modal e acirra disputa | https://valor.globo.com/financas/noticia/2022/01/10/xp-compra-modal-e-acirra-disputa.ghtml |
Coletando informações de indicadores financeiros do site da B3
O segundo exemplo tem como objetivo coleta de algumas taxas diretamente do site da B3.
Inicialmente coletaremos a curva futura de juros, resultante dos contratos negociados na B3 cujo objeto é a taxa DI. Para isso, teremos que enviar uma requisição do tipo GET
preenchendo alguns parâmetros de consulta, são eles: O código da taxa pesquisada e a data de atualização.
url="http://www2.bmf.com.br/pages/portal/bmfbovespa/boletim1/TxRef1.asp"
site_B3<- url %>% html_session()
form_b3<- site_B3 %>% html_form() %>% .[[1]] ## ver todos os parâmetros do formulário
O comando html_form
permite visualizar quaisquer parâmetros de formulários presentes no URL. É possível alterar esses parâmetros e resubmeter os formulários utilizando uma requisição POST
que, utilizando algumas funções alteraria os parâmetros da consulta utilizado pela requisição GET
. Iremos fazer o segundo passo diretamente nesse caso.
Primeiramente, iremos obter uma lista das opções disponíveis para consulta no site.
Opcoes<-data.frame(
"Taxa"=site_B3 %>% html_nodes(xpath = '//table//select[@name="slcTaxa"]//option') %>% html_text(trim = TRUE), ## Opções de taxas que visíveis para o usuário
"Cod Taxa"=site_B3 %>% html_nodes(xpath = '//table//select[@name="slcTaxa"]//option') %>% html_attr("value") ## Código das taxas que são utilizadas como parâmetro de consulta
)
head(Opcoes)
## Taxa Cod.Taxa
## 1 Ajuste cupom ACC
## 2 Alumínio ALD
## 3 DI x Anbid AN
## 4 Anbid x pré ANP
## 5 Ajuste pré APR
## 6 IBrX-50 BRP
Agora, podemos seguir com a requisição GET
e obtenção dos dados. Deveremos informar o código da taxa desejada (slcTaxa) e a data de atualização (Data e Data1). O código PRE, representa a taxa DI (o qual é o padrão de abertura da página).
consulta <- list(slcTaxa="PRE",
Data1= "20220105",
Data="05/01/2022") ## Parâmetros de consulta sempre devem ser um objeto list
# requisicao de busca
result <- httr::GET(url, query = consulta) ## Busca o url utilizando os parâmetros de consulta determinados
tb=result %>% read_html() %>%
html_nodes("table") %>%
html_table(fill = TRUE, dec=",", header=FALSE) ## Obtem os dados e converte-os em data frame
Utilizando o comando result$url
é possível ver que o url foi modificado e os parâmetros da consulta constam em sua definição. Isso ocorre pela comunicação se dar via requisição do tipo GET
.
Infelizmente o resultado dessa consulta não facilita uma conversão direta para uma estrutura de dados analítica. Alguns tratamentos são necessários os quais podem ser vistos abaixo. Caso julgue necessário para entendimento, acesse nosso tutorial de Manuseio de Estrutura de Dados.
tb_aux<-data.frame("Valor"=as.numeric(t(as.data.frame(tb[2])[,-1])[-c(1:6),-2]))
colDias<-seq(3, nrow(tb_aux), 3)
colCDI252<-seq(1, nrow(tb_aux), 3)
dados<-data.frame("Dias Corridos"=c(1,tb_aux[colDias,][-length(tb_aux[colDias,])]),"CDI 252"=tb_aux[colCDI252,])
head(dados)
## Dias.Corridos CDI.252
## 1 1 9.15
## 2 7 9.15
## 3 12 9.15
## 4 14 9.15
## 5 16 9.15
## 6 21 9.15
Vamos buscar uma nova taxa utilizando a estrutura anterior. Buscaremos a Libor, taxa juros de empréstimo interbancário de instituições que operam em Londres.
consulta <- list(slcTaxa="LIB",
Data1= "20220105",
Data="05/01/2022") ## Alterando a taxa consultada para Libor
# requisicao de busca
result <- httr::GET(url, query = consulta) ## Busca o url utilizando os parâmetros de consulta determinados
tb=result %>% read_html() %>%
html_nodes("table") %>%
html_table(fill = TRUE, dec=",", header=FALSE) ## Obtem os dados e converte-os em data frame
Alterando um pouco os tratamentos executados anteriormente obtemos um formato de estrutura de dados analítica.
tb_aux<-data.frame("Valor"=as.numeric(t(as.data.frame(tb[2])[,-1])[-c(1:3),-2]))
colDias<-seq(1, nrow(tb_aux)-1, 2)
colLib<-seq(2, nrow(tb_aux), 2)
dados<-data.frame("Dias Corridos"=tb_aux[colDias,],"Libor"=tb_aux[colLib,])
head(dados)
## Dias.Corridos Libor
## 1 1 0.229
## 2 7 0.229
## 3 12 0.229
## 4 14 0.229
## 5 16 0.229
## 6 21 0.229
Muitos sites mais modernos utilizam estruturas de comunicação baseadas em javascript. O rvest
tem seu uso limitado nesses casos. A solução é simular a utilização de um navegador por meio de outras ferramentas disponíveis no R. A principal delas é o pacote RSelenium
que será tema de um tutorial futuro.