Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
401 lines (252 sloc) 23.6 KB

Tutorial 8 - Cookies, sessions e formulários na web. Navegue na internet com o R!

Autores: Leonardo Sangali Barone e Rogério Jerônimo Barbosa

Até agora trabalhamos com exemplos nos quais retiramos informação de páginas em html, mas não enviamos nenhuma informação ao servidor com as quais estamos nos comunicando (exceto manualmente). Contudo, é muito comum nos depararmos com formulários em páginas que queremos raspar. Formulários, na maioria das vezes, aparecerão como caixas de consulta acompanhada de um botão.

Mecanismos de busca (Google, DuckDuckGo, etc) têm formulários nas suas páginas iniciais. Portais de notícia ou de Legislativos têm formulários de busca (como o que usamos manualmente no caso da Folha de São Paulo). Por vezes, mesmo para "passar" de página nos deparamos com um formulário.

Neste tutorial vamos aprender a preencher um formulário, enviá-lo ao servidor da página e capturar sua resposta.

Começaremos com um exemplo simples, como o buscador da Google, e depois faremos um exercício bastante mais complexo com o STF.

Cookies e Sessions

Guarde bem essas duas palavras: cookies e sessions. Elas representam conceitos fundamentais para uma adequada compreensão sobre o funcionamento da internet, dos navegadores -- e importantíssimos para a raspagem de dados ( Web Scraping ) de certas páginas.

Cookies

Cookies são pequenos arquivos de texto que guardam informações necessárias para a sua navegação. Por exemplo: se você vai até o site do IBGE e escolhe a opção de vê-lo na versão em inglês, como o IBGE sabe que a partir dali todas as próximas páginas e opções também deverão ser apresentadas em inglês? No momento em que você clicou naquela opção, o seu navegador automaticamente baixou um arquivo que guarda características e opções que serão usadas novamente depois. Frequentemente, é também por meio de cookies que são guardadas informações sobre o que está na sua "cesta de compras", quando você marca produtos que deseja adquirir num site de vendas. Cookies são muito úteis e importantes -- essenciais para a navegação.

Até agora, quando queremos raspar dados de uma lista com várias páginas de resultado, fizemos sempre alterações na própria URL, informando o número da página que desejamos acessar. Mas nem sempre isso é possível, pois nem todos os sites guardam no próprio endereço a informação sobre o número da página em que estamos. Não raro, essa informação está guardada num cookie! Sim, um pequeno arquivo de texto, guardado em algum lugar do seu próprio computador, traz informações que serão dinamicamente acessadas por seu navegador e atualizadas.

Talvez você já tenha ouvido algo bastante ruim sobre cookies por aí... dizendo até que são usados por hackers etc. A história é mais complicada do que isso... A princípio, somente o site que gerou o cookie pode consultá-lo para saber sobre suas opções. Mas, às vezes, esse site pode passar essas informações para outro (várias empresas são associadas, certo?). Então, quando você vai até a Amazon e escolhe alguns produtos para sua cesta, pode ser que, algum tempo depois você veja aqueles mesmos itens num anúncio na página do Google ou até de outro lugar. Provavelmente aquela informação estava num cookie e a Amazon pode ter liberado o acesso para outra empresa. O objetivo, é tornar sua navegação mais customizada e personalizada, oferecendo para você apenas o que é relevante. As empresas querem vender para você aquilo que você realmente tem probabilidade de comprar...

Este pequeno vídeo aqui (em inglês) traz um bom resumo e panorama sobre o que são cookies. Quer algo um pouquinho mais técnico? Veja este curto vídeo aqui então.

"Roubar" informações de cookies é possível. Mas não é isso o que está ocorrendo quando você visita um site e os anúncios de coisas que você já viu estão lá. Quer saber mais sobre roubo de Cookies? Veja este vídeo aqui.

Cookies, no entanto, devem ser pequenos. Afinal, são informações continuamente trocadas entre você (cliente) e o computador que armazena o site no qual você navega (servidor). Se ele fosse um arquivo grande, o fluxo de uploads e downloads seriam muito intenso e a navegação se tornaria muito mais lenta. Por esta razão, existem sessions, que servem para guardar conteúdos maiores.

Sessions

Sessions são a relação estabelecida entre o servidor (o site) e o cliente (o seu navegador). Um arquivo ou um conjunto de arquivos guardam um complexo de informações sobre você -- que vão muito além da capacidade dos cookies (que usualmente não podem ultrapassar 4kb). Essas informações podem estar guardadas tanto localmente (no seu próprio coputador) como remotamente (no servidor). Se estiverem guardadas localmente, a navegação é geralmente mais rápida -- visto que você não precisa trocar requisições usualmente com o servidor.

Entenda as sessions como uma espécie de "conta" que você abre no servidor (dessas contas que uma pessoa pode abrir num estabelecimento de comércio e que guardam suas informações e interações). E os cookies são usados nesse caso apenas como uma espécie de ID (seu cartão de identidade) para registrar você frente ao servidor.

Quando você loga na sua conta de e-mail, está abrindo uma session. Está dizendo ao seu navegador para estabelecer essa relação duradoura com o servidor (o site do Gmail, por exemplo). Assim, quando você navega dentro do seu email, não precisa a todo momento fazer o login de novo. Suas informações estão guardadas nesse conjunto de arquivos que representa a session. E cookies serão frequentemente usados, para mostrar ao site do Gmail o seu ID.

Quer saber mais sobre sessions? Neste vídeo (em inglês) há uma rápida e intuitiva apresentação.

rvest, formulários e Google

Agora, com aqueles dois conceitos na ponta da língua -- cookies e sessions -- vamos passar às nossas análises.

Vamos começar carregando o pacotes rvest:

library(rvest)

Vamos explorar o buscador do Google. Isso mesmo, vamos fazer uma busca no Google a partir do R! O buscador do Google é constituído basicamente de um campo de texto que deve ser preenchido e enviado de volta ao servidor. Quando temos que informar qualquer tipo de informação e enviar de volta (mesmo que seja apenas marcar uma opção de "ok"), estamos preenchendo um formulário.

Este é justamente um caso em que termemos estabelecer uma conexão com o servidor. O Google exige o estabelecimento de uma session -- e isso é feito antes mesmo de capturar a página na qual o formulário está.

Utilizamos a função html_session para isso:

google_url <- "https://www.google.com"

google_session <- html_session(google_url)

Estabelecida a conexão, precisamos conhecer o formulário. Começamos, obviamente, obtendo o código HTML da página do formulário, tal como sempre fizemos. Mas desta vez, ao invés de passar a própria URL da página como argumento principal da função, passamos o objeto que contém as informações da session estabelecida.

Veja: o objeto google_session guarda, além das informações sobre a conexão, também o próprio conteúdo HTML.

read_html(google_session)

O objeto google_session pode ser alvo de nossas atividades de raspagem e coleta, sem que precisemos executar sobre ele novamente a extração do conteúdo HTML. Esse conteúdo já está lá!

Se quiséssemos raspar todos os links da página, faríamos:

html_nodes(google_session, xpath = "//a")

Nesse caso, nosso interesse é extrair formulário, isto é o trecho de HTML que contém o campo de busca onde digitamos o que queremos que o Google encontre para nós. Como tabelas em HTML, fomrulários tem suas tags próprias: html <form> e html </form>. E contamos, no pacote rvest, com uma função que extrai uma lista contendo todos os formulários da página:

google_form_list <- html_form(google_session)
google_form_list

O resultado da função html_form é uma lista (observe o [[1]] no topo dos resultados), tal como também ocorria no html_table.

No caso do buscador da Google, há apenas um formulário na página. Com dois colchetes, extraímos então o item que está na primeira posição da lista de formulários (você verá que no próximo exemplo, do site do STF, obteremos mais de um formulário e precisaremos identificar qual queremos).

google_form <- google_form_list[[1]]

class(google_form)

google_form

Examine o objeto que contém o formulário. Ele é um objeto da classe "form" e podemos observar todos os parâmetros que o compõe, ou seja, tudo aquilo que pode ser preenchido para envio ao servidor, ademais dos botões de submissão.

Vá para o navegador e inspecione a caixa de busca da Google e os botões de busca e "Estou com sorte". Você observará que cada "campo" do formulário é uma tag "input". O atributo "type", define se será oculto ("hidden"), texto ("text") ou botão de submissão ("submit"). Por sua vez, o atributo "name" dá nome ao campo.

Alguns "inputs" já contêm valores (no atributo "values"). No nosso exemplo, os botões e campos ocultos. Estes últimos jáidentificaram o idioma ("hl") e o enconding ("ie") com o qual trabalhamos. Ou seja, o Google já deixou preenchidas partes do formulário!!. É por esta razão que recebemos resultados preferencialmente em português e relacionados a sites brasileiros!

O que nos interesse preencher, obviamente, é o "input" chamado "q". Em várias ferramentas de busca, "q" (acronismo para "query") é a caixa de texto onde fazemos a busca.

Vamos, então, preencher o campo "q" com a função set_values:

google_form <- set_values(google_form,
                          'q' = "merenda")

Simples, não? Colocamos o objeto do formulário no primeiro parâmetro da função e os campos a serem preenchidos na sequência, tal como no exemplo.

Reexamine agora o formulário. Você verá que "q" está preenchido:

google_form

Legal! Agora vamos fazer a submissão do formulário. No buscador da Google, há duas possibilidades de submissão. Vamos usar "Pesquisa Google" e não "Estou com sorte". Na submit_form, precismos informar a sessão que criamos (conexão com o servidor), o formulário que vamos submeter e o nome do botão de submissão.

Foi por isso, desde o início, que criamos a session! O argumento principal da função do rvest que faz a submissão do formulário requer necessariamente que você informe a session.

Veja o exemplo:

google_submission <- submit_form(session  = google_session, 
                                 form     = google_form, 
                                 submit   = "btnG")

Pronto! Fizemos a submissão do formulário. Notou o argumento submit = "btnG"? Ele é justamente o valor que estava no atributo <input submit> do formulário. Já falamos dele. Dê uma checada. Ele é como se fosse o nosso botão de "OK", que envia a busca. Temos que informar isso! E note que há duas possibilidades de submissão nesse formulário: a "Pesquisa Google", que é a busca tradicional, e a "Estou com Sorte". Essas duas opções também existem no seu navegador.

Como output da submissão, recebemos um resultado, salvo agora no objeto google_submission. Mas que objeto é esse? Nos termos da linguagem R, de que classe ele é?

Ora! Vejamos:

class(google_submission)

Ele é uma session! Tal como também era nosso objeto google_session. Isso significa que então nossa relação duradoura com o Google está mantida, nossa conexão está estabelecida. O Google, por meio de cookies e outras informações, têm então memória dos passos anteriores que percorremos até aqui. Sabe que estivemos numa página anterior e que submetemos um termo de busca.

Tudo se passa como se tivessemos digitado algo para pesquisar no google (no caso, "merenda"), e então clicado no botão "Pesquisa Google" ou apertado a tecla Enter. O resultado dessa ação vai ser a navegação até outra página, onde agora estão exibidos os resultados da pesquisa. Você já não está mais na mesma página. Você navegou com o R!! E a página que obtivemos está guardada no objeto que resulta da função submit_form, juntamente com as informações sobre sua conexão (afinal, trata-se de uma session).

Veja:

read_html(google_submission)

Agora basta raspar o resultado como já haviámos feito antes. Como você já sabe, podemos aplicar nossas operações diretamente no objeto que resulta da session, sem a necessidade de utilizar a função read_html.

Faça no seu navegados (fora do R) a mesma busca utilizando o Google. Examine a página, inspecione o código fonte. E então, tente entender o código abaixo:

nodes_resultados <- html_nodes(google_submission, xpath = "//h3/a")

titulos <- html_text(nodes_resultados)
links   <- html_attr(nodes_resultados, name = "href")
links   <- paste0("https://www.google.com", links)

Os títulos dos resultados de busca estão em tags do tipo h3. E as tags 'a' são "filhas" (child) das tags dos títulos. Já vimos isso antes várias vezes. Então você já sabe o que fazer. Certo? (se ainda tiver dúvidas, volte aos quatro primeiros tutoriais).

Certo... Mas como você deve saber por sua experiência prática com o buscador, os resultados de uma busca no Google não são exibidos numa única página. Há várias páginas de resultado. Temos que clicar em "Mais" (veja no seu navegador).

Inspecione a estrutura esse link, clicando com o botão direito sobre ele e selecionando a opção "Inspecionar" (ou equivalente) em seu navegador. Você vai notar que a tag 'a' desse link traz um atributo "class" com valor igual a "pn". Então este é, aparentemente, o link em que queremos clicar para ir para as próximas páginas, certo?

Vejamos se esse link existe no nosso objeto do R:

html_nodes(google_submission, xpath = "//a[@class='pn']")

Vixe... não existe! Mas como isso é possível!?

Ocorre que a página que está guardada dentro do nosso objeto google_submission não é exatamente idêntica àquela que está no seu navegador. A session aberta entre o Google e o R não é a mesma session entre o Google e seu navegador. Você provavelmente já utilizou o Google várias vezes no seu navegador -- e, em algum lugar do seu computador, há cookies e outros arquivos que registraram parte das interações e opções que você selecionou no passado. E a estrutura HTML gerada pelos resultados da busca no Google varia de acordo com isso.

Mas então como saber onde está o nosso link "Mais", no qual poderíamos clicar para ir para as próximas páginas de resultados?

Uma forma simples de saber é a seguinte.

  1. Extraia o conteúdo HTML da página de resultados com a função read_html.
  2. Usando a função as.character, transforme esse objeto (que pertence simultâneamente às classes xml_document e xml_node) num vetor de tipo character
  3. Usando a função writeLines, salve o conteúdo desse vetor num arquivo de texto com a extensão .html.
  4. Abra esse arquivo salvo por você no navegador e inspecione o link "Mais"

Com isso, garantimos que a página inspecionada é mesmo aquela que foi coletada pelo R, usando de nossa session:

pagina <- read_html(google_submission)
pagina <- as.character(pagina)
writeLines(pagina, "/users/rogerio/desktop/pagina.html")

Inspecionou? Encontrou a tag 'a' que contém o link "Mais"? O atributo "class" dessa tag tem valor igual a "fl" e não "pn", como tínhamos visto antes.

O problema, vejam, é que há muitas tags 'a' com html @class="fl":

html_nodes(google_submission, xpath = '//a[@class="fl"]')

Qual delas queremos? É melhor dar uma inspecionada novamente e sermos mais específicos.

Veja agora abaixo. Que tal isso?

html_nodes(google_submission, xpath = '//td[@class="b"]/a[@class="fl"]')

Parece que deu certo. Temos apenas um resultado. Mas como saber, como ter certeza? É só verificar se de fato o conteúdo da tag é a palavra "Mais":

link = html_nodes(google_submission, xpath = '//td[@class="b"]/a[@class="fl"]')
html_text(link)

Viva! Encontramos o link para ir para a próxima página. Haveria, contudo, várias formas de obter esse mesmo link -- usando tanto XPath como CSS. Você consegue compreeender a linha abaixo, que gera o mesmo resultado?

html_nodes(google_submission, xpath = "//span[text()='Mais']/..")

Agora temos é que clicar no link e segui-lo, para chegar até a próxima página de resultados. Fazemos isso com a função follow_link():

google_submission_page2 <- follow_link(google_submission, xpath = "//span[text()='Mais']/..")

Como você pode imaginar, a classe desse objeto é, novamente, uma session:

class(google_submission_page2)

Se é uma session, continuamos conectados. Mas agora, estamos na página 2. Então podemos coletar os novos resultados:

nodes_resultados2 <- html_nodes(google_submission_page2, xpath = "//h3/a")

titulos2 <- html_text(nodes_resultados2)
links2   <- html_attr(nodes_resultados2, name = "href")
links2   <- paste0("https://www.google.com", links)

Você está literalmente navegando na internet com o R, clicando em links e até mesmo fazendo pesquisa no Google! Lindo, não?

Que tal fazer um loop para coletar as primeiras 10 páginas de resultado então?

google_url <- "https://www.google.com"

# Estabelecendo a conexão e coletando a primeira página
google_session <- html_session(google_url)

# Raspando o formulário, retirando-o da lista e preenchendo o campo de busca 
google_form <- html_form(google_session)[[1]]
google_form <- set_values(google_form, 
                          'q' = "merenda")

# Submetendo os resultados (usando o botão da "Pesquisa Google", o "btnG")
google_session <- submit_form(session = google_session, 
                              form    = google_form,
                              submit  = "btnG")

data_resultados <- data_frame()
for(i in 1:10){
        
        print(i)
        
        # Coletando os nodes dos resultados
        nodes_resultados <- html_nodes(google_session, xpath = "//h3/a")
        
        # Raspando títulos e links
        titulos <- html_text(nodes_resultados)
        links   <- html_attr(nodes_resultados, name = "href") %>% 
                paste0("https://www.google.com", .)
        
        # Compilando os resultados num data frame
        data_resultados <- bind_rows(data_resultados,
                                     data_frame(titulos, links))
        
        # Navegando até a próxima página
        google_session <- follow_link(google_session, xpath = '//span[text()="Mais"]/..')
        
        # Um tempinho para o navegador respirar, carregar a página (e o Google não nos 
        # bloquear por excesso de requições)
        Sys.sleep(.5)

}

Vejamos agora os nossos links coletados das 10 primeiras páginas de resultados do Google:

data_resultados

Obtendo informações sobre processos no STF

Diferentemente dos Executivos e Legislativos no Brasil, os órgãos do Judiciário, em diversas esferas, têm avançado bem pouco em transparência e no estabelecimento de política de dados abertos. O STF não é exceção. A despeito do grande número de pesquisa sobre o STF, ainda é preciso raspar os dados diretamente do potal do Tribunal para construir bases de dados sobre processos que lá tramitam.

Vamos rapidamnte ver um exemplo de como obter dados do STF.

O primeiro passo será encarar este formulário aqui. Queremos buscar os processos por seu número (afinal de contas, no futuro vamos pegar todos os processos de um determinado tipo de 1 até "n"). O formuláio está logo no centro da página.

Vamos proceder como fizemos no caso do buscador da Google. Começaremos estabelecendo uma sessão (conexão) com o servidor. A seguir, vamos raspar a página que contém o formulário e produzir uma lista de fomulários:

stf_url <- "http://www.stf.jus.br/portal/processo/pesquisarProcesso.asp"

stf_session   <- html_session(stf_url)

stf_form_list <- html_form(stf_session)

stf_form_list

Veja que há três formulários diferentes na página. Como decidir entre eles? Precisamos examinar o código HTML. Em geral, inspecionando o campo da busca já teremos um indicativo de qual é o formulário que nos interessa.

Neste caso, queremos fazer a busca por número de processo e o campo de busca se chama "numero". Qual é o formulário que contém tal informação? O terceiro:

  stf_form <- stf_form_list[[3]]

  stf_form

Escolhido o formulário, precisamos preenchê-lo. Aqui nos depararemos com um novo problema: como saber qual campo é de preenchimento obrigatório? Esta informação pode esta até visível na página, e esta será nossa primeira pista. No caso do STF, não está. "dropmsgoption", por exemplo, é um campo obrigatório que ainda não está preenchido no formulário que capturamos. Tentativa e erro é o recurso final e tente se colocar no lugar de quem criou o formulário para preenchê-lo.

Note que o campo "dropmsgoption" é uma tag "select" e não "input". As tags "select" vêm acompanhadas, em geral, de suas opções, que relacionam o texto da opção com seu código. Vejam:

stf_form$fields$dropmsgoption

No nosso caso, queremos o código "1"!

Vamos, então, preencher os dois parâmetos do formulário. Por uma razão que não convém explicar, vamos omitir na nossa busca o nome do processo (ex: ADI, AC, Pet, etc). Buscaremos apenas o número.

stf_form <- set_values(stf_form,
                          'dropmsgoption' = 1,
                          'numero' = "500")
stf_form                  

Note agora que o botão de preenchimento do formulário não tem nome. Não tem problema. A função submit_form é bastante inteligente e procurará o campo de submissão do formulário se você não preenhcer o parâmetro "submit" (e vai apontar o nome dele e m uma mensagem. Da mesma forma, se você escrever um nome inexistente, receberá uma mensagem de erro com todos os nomes possíveis dos campos de submissão.

stf_submission <- submit_form(session = stf_session, 
                                 form = stf_form)

Faça o mesmo processo manualmente. O resultado é uma tabela com os links para todos os diferentes tipos de processos com o número buscado. Podemos extrair a tabela (você já sabe fazer isso):

tabela_processos <- html_table(stf_submission)[[1]]

E os links dos processos (você também sabe fazer isso). Os links precisarão de um pouco de limpeza e utilizaremos a função str_sub do pacote stringr para resolver este problema:

nodes_links_processos <- html_nodes(stf_submission, xpath = "//table//a")
links_processos       <- html_attr(nodes_links_processos, name = "href")

library(stringr)
links_processos <- str_sub(links_processos, 8, str_length(links_processos))
links_processos <- paste0("https://www.stf.jus.br/portal/processo/", links_processos)

Vamos adicionar os links à tabela:

tabela_processos$links <- links_processos

Excelente! Podemos escolher o link de um processo usando a tabela (ADI, por exemplo):

link_adi <- tabela_processos$links[str_detect(tabela_processos$Processo, "ADI")]

E capturar a tabela com as infomações do processo.

pagina_adi       <- read_html(link_adi)
lista_tabela_adi <- html_table(pagina_adi, fill = T)
tabela_adi       <- lista_tabela_adi[[2]]

Desafio

Organize o código para pegar (em loop) uma sequência de processos (por exemplo, 500 a 509) de um tipo específico (ADI).