Web scraping é o nome que se dá para técnicas de raspagem (extração/coleta, se preferir) de dados de páginas da internet. Consiste, basicamente, em especificar um site, determinar o trecho do código HTML que contém a informação desejada e, por fim, dispor essa informação num formato adequado para a análise dos dados. Entretanto, essa é só metade da história. Para tornar a raspagem relevante é necessário dar “escala” para essa tarefa. Nesse texto procuro explorar essas possibilidades em R, usando como exemplo o site “Allrecipes”, de onde vou extrair os ingredientes de todas as receitas de bolo de cenoura. (Código completo no github).

Em síntese, o que desejamos é obter um banco de dados com todos os ingredientes das receitas de bolo de cenoura, bem como seus nomes e links. Para chegar nesse produto final, vamos cumprir algumas etapas. Realizada a busca (com o navegador), existem várias páginas de resultado (mais precisamente, 6). O primeiro passo é obter o link dessas 6 páginas. Obtido o link dos resultados da busca, o segundo passo é obter o link das receitas. Por fim, com o link das receitas, resta raspar a informação “Ingredientes” dentro delas. Pacotes do R exigidos: rvest, stringr, purr, dplyr.

Raspagem
Desde o pacote rvest, a raspagem de dados virou uma coisa acessível, mesmo pra quem (como eu) entende bem pouquinho de HTML. O processo pode ser resumido em três etapas: (1) selecionar o site, (2) definir o nódulo (alguns traduzem como “nó”) onde está a informação a ser raspada e (3) transformar o código HTML em texto. Eventualmente, é necessário estabelecer o tipo de informação desejada dentro do nó.
Desses três processos descritos, o único que leva mais de 10 segundos é a obtenção do nódulo. Felizmente, existe uma extensão do Google Chrome chamada “Selector gadget” que nos ajuda a obtê-lo. Instalado o gadget, à medida que movemos o cursor, vemos um retângulo envolvendo pedaços do site. Qual nosso objetivo nessa seleção? É incluir no retângulo a informação a ser raspada e excluir todo o resto. No vídeo abaixo mostro o Gadget Selector em ação (ative a legenda).

Aqui eu optei por importar toda a receita como um bloco. Poderia ser diferente? Sim: há várias formas de importar uma mesma informação. O nódulo selecionado, então, é “.recipeIngredients h2+ ul”. E pronto. Agora são três linhas de código para raspar essa receita:

exemplo <- read_html("http://allrecipes.com.br/receita/2453/bolo-de-cenoura-com-cobertura-de-chocolate-i.aspx") %>%
  html_nodes(".recipeIngredients h2+ ul") %>%
  html_text() %>%
  data.frame()

Muito bem, muito bem, mas agora só falta raspar as outras 52 receitas. Como repetir esse processo? Escrever o mesmo código 52 vezes, só alterando o link de cada receita.

Passo 1: obter os links das páginas de resultado
A primeira coisa a fazer é descobrir como o link é alterado de uma página de resultado para a outra. Esta é a página da busca. No “Allrecipes”, quando eu clico em “52 receitas”, ele me direciona para a página 2 das receitas. Logo, observando o link, vi que apenas muda o número no fim, sendo o de final “1” a primeira página, o de final “2” a segunda e assim sucessivamente. (Nem sempre é tão mole assim).
Assim, vamos criar todos os links das paginas de resultado da busca. São 6 páginas de resultado, então vamos criar seis links. Basta juntar a primeira parte do link e juntar com final 1, depois com final 2 (…).

total_p <- c(1:6)
result_pages <- paste0("http://allrecipes.com.br/receitas/etiqueta-572/receitas-de-bolo-de-cenoura.aspx?page=", total_p)

E pronto! Temos os links das páginas de resultados da busca.

Passo 2: obter os links das receitas
Onde estão o link das receitas? Ora, em cada nome da receita existe um hiperlink que direciona da página de busca para a receita propriamente dita. Basta, então, usar o selector gadget para selecionar os nomes das receitas (e nada mais que isso). Nesse caso, o nódulo é para o nome é “.col-sm-7 a”. Aqui entra um pouco de conhecimento de HTML: quando queremos um link, temos que especificar o atributo “href”, como veremos adiante.
É fácil montar um código para pegar as receitas de uma página de busca. Mas como obter de todas as páginas? Aqui entra o ganho de escala: temos que combinar a raspagem com loop, ou métodos análogos ao loop. Aqui usaremos a função “map_df”, do pacote “purrr”.
A função map_df opera assim: você especifica uma função (criada por você, ou de um pacote) e também um vector ao longo do qual aquela função será executada. A map_df simplesmente vai executar a função para a linha 1, depois para a linha dois (…) e assim sucessivamente.
Nesse caso, iremos criar a nossa própria função, que vai ler o código da página (read_html), ler determinado nó (“.col-sm-7 a”) e obter o link contido nesse nó através do atributo para links (“href”). Segue a função:

link_recipes <- function(x)
{
read_html(x) %>%
html_nodes(".col-sm-7 a")
%>% html_attr("href") # esse é o atributo link
%>% data.frame()
}

Note que, ao criar a função, eu deixei o “x”, justamente no espaço onde deve ser inserido o link da página de busca. Pronto: agora podemos rodar a função map_df e obter todos os links das receitas.

link_receitas<- map_df(result_pages, link_recipe)

Passo 3: raspar os ingredientes de todas receitas
Finalmente, vamos raspar todas os ingredientes dessas receitas. Trata-se de reproduzir algo semelhante ao que acabamos de fazer. Primeiro vamos criar uma função para raspar os ingredientes da receita e depois usar o map_df para repetir o mesmo processo para todas as receitas.

get_ingredients<- function(x)
{
read_html(x) %>%
html_nodes(".recipeIngredients h2+ ul") %>%
html_text() %>%
str_to_lower() %>%
data.frame()
}
ingredientes<- map_df (link_receitas, get_ingredients)

Agora vamos apenas juntar os dois bancos, o que contém os ingredientes e o que contém os links.

dados<- bind_cols(ingredientes, links)

Raspar receitas de bolo de cenoura, para mim, é uma brincadeira. Cada qual pode definir um site e as informações que deseja raspar. Mas já que estamos com esses dados, por que não brincar um pouco? Word clouds de bolo de cenoura? Lista de bolos de cenoura que não levam açúcar? Sem trigo? Sem ovos?

Nuvem de palavras dos ingredientes (bolo de cenoura)

Bolo de cenoura