Cultivando datos de la web

En esta entrada, cultivaremos datos de la web con la biblioteca de Rrvest. Actualmente, innumerables aplicaciones dependen directamente de la tecnología de raspado o cultivo de información, la cual nos permite generar información actualizada por medio de la extracción directa de la misma a partir de la red. La librería rvest contiene funciones que facilitan este proceso. Por otro lado, utilizaremos la librería stringr para manipular cuerdas de texto por medio de patrones y expresiones regulares.

En este ejemplo se aprecia el proceso de cultivo de los datos de artistas amigos a partir de información del artista de interés. La información mencionada será raspada de la página web https://www.discogs.com automáticamente. Con lo que podemos escribir un script, de unas veinte líneas, que reciba un nombre de artista y regrese una lista de sus colaboradores.

Primero ingresemos a la página web https://www.discogs.com y hagamos una búsqueda de algún músico. En la siguiente imagen vemos que se realiza una búsqueda del artista Lassi Nikko. Observe la dirección de enlace en la barra de direcciones de su explorador después de realizar la búsqueda (encerrada en un elipse rojo en la siguiente imagen).

search_artiststructure
Estructura de enlace de búsqueda de artista

Aquí se presenta el enlace

En él podemos ver que la estructura parece ser: el nombre del artista separado por signos de más, es decir, «lassi+nikko». Patrón que se encuentra entre “https://www.discogs.com/search/?q=” y “&type=all”. Nos gustaría que el programa reciba el nombre de un artista y que de forma automática se realice la búsqueda de dicho artista, en caso de existir, que se ingrese a la página respectiva del artista, para raspar y regresar una lista con los colaboradores.

Para recibir el nombre de artista ingresado por el usuario utilizaremos la función de R base readLines

# INPUT
cat("Nombre Artista: "); # instrucción para usuario
nombre_artista <- readLines("stdin", n = 1); # almacenando entrada
cat("Usted ingreso") # Mensaje de confirmación
str(nombre_artista);
cat( "\n Generando lista de artista amigos \n" ) # Mensaje de espera

Utilizaremos la función de R base paste0 para construir la petición http con lainformación mencionada anteriormente y almacenarla en la variable «busqueda», no sin antes cambiar los posibles espacios que ingresó el usuario por signos de más «+», para después realizar la petición http con la función read_html, contenida en rvest

#PROCESS
library(stringr)
nombre_artista <- str_replace_all(nombre_artista, " ", "+") #cambiando espacio por +
busqueda <- paste0("https://www.discogs.com/search/?q=", nombre_artista, "&type=all") # construyendo petición de búsqueda (URL) con paste0
library(rvest)
html_page <- read_html(busqueda) # guardando respuesta a petición

Ahora vamos a extraer a partir del primer elemento de la lista de resultados, el enlace que nos lleva a la pagina del artista. Para lo cual nos serviremos de la tecnología css, utilizada ampliamente para dar estilo a las páginas html. Utilizaremos la ruta css de los elementos requeridos de forma que podamos extraer información de nodos html. Para conocer dicha ruta vamos a hacer uso del  «inspector de código» que viene en casi cualquier navegador web. Aquí se observa cómo abrirlo en el explorador de internet firefox

inspector
Inspector de código (firefox)

Haciendo flotar el cursor sobre los distintos elementos de la página web, el inspector resaltará el código respectivo al elemento en la parte inferior, como se muestra en la siguiente imagen. Al dar clic sobre algún elemento se resalta el bloque de código al que corresponde

css_id
«ruta CSS»

Se puede ver que la información que requerimos (subdirectorio de artista) se encuentra dentro del divisor seleccionado, con ruta «div.card.card…»

Para obtener la información de secciones css podemos utilizar la función html_nodes, la cual recibe como argumentos la pagina web y la ruta css de los elementos de los cuales queremos extraer el código.

enlace_artista <- html_nodes(html_page, "div.card.card_large.float_fix.shortcut_navigable")
enlace_artista

## {xml_nodeset (50)}
##  [1] <div class="card card_large float_fix\n        \n        shortcut_n ...
##  [2] <div class="card card_large float_fix\n        \n        shortcut_n ...
##  [3] <div class="card card_large float_fix\n        \n        shortcut_n ...
##  [4] <div class="card card_large float_fix\n        \n        shortcut_n ...
##  [5] <div class="card card_large float_fix\n        \n        shortcut_n ...
##  [6] <div class="card card_large float_fix\n        \n        shortcut_n ...
##  [7] <div class="card card_large float_fix\n        \n        shortcut_n ...
##  [8] <div class="card card_large float_fix\n        \n        shortcut_n ...
##  [9] <div class="card card_large float_fix\n        \n        shortcut_n ...
## [10] <div class="card card_large float_fix\n        \n        shortcut_n ...
## [11] <div class="card card_large float_fix\n        \n        shortcut_n ...
## [12] <div class="card card_large float_fix\n        \n        shortcut_n ...
## [13] <div class="card card_large float_fix\n        \n        shortcut_n ...
## [14] <div class="card card_large float_fix\n        \n        shortcut_n ...
## [15] <div class="card card_large float_fix\n        \n        shortcut_n ...
## [16] <div class="card card_large float_fix\n        \n        shortcut_n ...
## [17] <div class="card card_large float_fix\n        \n        shortcut_n ...
## [18] <div class="card card_large float_fix\n        \n        shortcut_n ...
## [19] <div class="card card_large float_fix\n        \n        shortcut_n ...
## [20] <div class="card card_large float_fix\n        \n        shortcut_n ...
## ...

Observe que html_nodes nos entrega una lista con varios nodos que cumplieron con tener esa ruta. Estos nodos representan el código que hay en cada resultado de la búsqueda, queremos el que se encuentra en el primer elemento de la lista. Vamos a acceder a él y a convertirlo a texto con la función as.character de R base, observe que ahí dentro se encuentra el subdirectorio del artista, información que queremos obtener.

enlace_artista_char <- as.character(enlace_artista[1]) #accediendo a primer elemento de la lista de nodos y convirtiéndolo a texto
  enlace_artista_char # imprimir
## [1] "<div class=\"card card_large float_fix\n        \n        shortcut_navigable\" data-id=\"a182658\" data-object-id=\"182658\" data-object-type=\"artist\">\n                                                            \n    \n                <a href=\"/artist/182658-Lassi-Nikko\" class=\"thumbnail_link\n            thumbnail_size_large\n            thumbnail_orientation_landscape\n            thumbnail-lazyload\n        \">\n        <span class=\"thumbnail_border\"></span>\n        <span class=\"thumbnail_center\">\n                                <img data-src=\"https://img.discogs.com/oqpsbvuIwt87D9aqvSx7w6SqaU0=/300x300/smart/filters:strip_icc():format(jpeg):mode_rgb():quality(40)/discogs-images/A-182658-1433076899-8016.jpeg.jpg\" src=\"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" alt=\"Lassi Nikko\"></span>\n    </a>\n    \n    <h4><a href=\"/artist/182658-Lassi-Nikko\" class=\"search_result_title\" title=\"Lassi Nikko\" data-followable=\"true\">Lassi Nikko</a></h4>\n            \n    <div class=\"card_actions skittles\">\n                        <span class=\"skittle skittle_collection needs_delegated_tooltip\" data-title=\"0 in Collection\" aria-label=\"0 in Collection\" title=\"0 in Collection\" style=\"display: none;\"><i class=\"icon icon-collection\"></i><span class=\"count\">0</span>\n    </span>                <span class=\"skittle skittle_wantlist needs_delegated_tooltip\" data-title=\"0 in Wantlist\" aria-label=\"0 in Wantlist\" title=\"0 in Wantlist\" style=\"display: none;\"><i class=\"icon icon-wantlist\"></i><span class=\"count\">0</span>\n    </span>                            <a class=\"skittle skittle_inventory needs_delegated_tooltip\" data-title=\"0 in Inventory\" aria-label=\"0 in Inventory\" title=\"0 in Inventory\" style=\"display: none;\" href=\"/sell/release/2662813?seller=Anonymous\"><i class=\"icon icon-store\"></i><span class=\"count\">0</span>\n    </a>\n    </div>\n\n    \n    </div>"

Ahora, de toda esta maraña, extraemos únicamente el subdirectorio del artista. Para extraer exactamente este pedazo de texto utilizaremos la función str_extract de la librería stringr y una expresión regular para obtener uno o más caracteres alfanuméricos, guiones medios y diagonales que existan después del primer ‘href=\»‘. Una vez obtenido el subdirectorio lo pegamos al resto de la url utilizando paste0.

artist_link <- str_extract(enlace_artista_char, "(?<= href=\")[[:alnum:]-/]*") # expresión regular
artist_link <- paste0("https://www.discogs.com", artist_link) # construyendo petición
artist_link # imprimir
## [1] "https://www.discogs.com/artist/182658-Lassi-Nikko"

Resta entrar a esta página y raspar la información de colaboradores. Si entramos a la página del artista e inspeccionamos el bloque de código del nombre de los artistas en los discos, podemos ver que su ruta css es “span.artist_in_title a”

Obtenemos la lista de artistas en el título extrayendo estos nodos y extraemos el texto html de dichos nodos con html_text de rvest. Al final pasamos este texto por la función unique de R base para eliminar elementos repetidos

artist_html_page <- read_html(artis_link)
colaborators <- html_nodes(artist_html_page, "span.artist_in_title a")
# OUTPUT
(lista_colab <- unique(html_text(colaborators)))
##  [1] "MD"               "Seremoniamestari" "brothomStates"   
##  [4] "Brothomstates"    "Origami (9)"      "Blamstrain"      
##  [7] "Lackluster"       "Various"          "Luke Slater"     
## [10] "Surgeon"          "Sundial Aeon"     "Dune (31)"

Basta juntar los bloques de código para construir el script. Recuerda que debemos guardarlo con extensión .R (en la siguiente ejemplificación lo guardamos como ask_discogs.R) y para ejecutarlo basta escribir el siguiente comando dentro de la terminal, desde el directorio en donde hayamos guardado el script

Rscript "ask_discogs.R"

¡Ánimo!