7.1 Web scraping

En ocasiones interesa descargar datos directamente de páginas de internet recorriendo, incluso, varias (o muchas) de ellas. A eso, a falta de un nombre de consenso en español (¿rascado?), se lo denomina web scraping.

Para descargar datos de páginas web usaremos el paquete rvest.

library(rvest)

Con él podemos descargar, por ejemplo, las cotizaciones del IBEX 35 en tiempo real:

url.ibex <- "http://www.bolsamadrid.es/esp/aspx/Mercados/Precios.aspx?indice=ESI100000000"
tmp <- read_html(url.ibex)
tmp <- html_nodes(tmp, "table")

La segunda línea descarga y preprocesa una página descargada de internet. El objeto tmp contiene una representación interna de la página. Una página web, en el fondo, no es otra cosa que un árbol del que penden nodos que son párrafos, imágenes, enlaces, tablas, etc. Sobre este árbol se pueden realizar distintos tipos de consultas, i.e., extraer distintos tipos de nodos.

La función html_nodes captura los nodos que tienen determinadas características. En este ejemplo, como ocurre con mucha frecuencia, nos interesan los identificados como tablas (tables). De hecho, las tablas son tan interesantes que el paquete rvest proporciona una función auxiliar para convertir los nodos de tipo table en tablas de R: html_table.

En nuestro ejemplo, la página contiene varias tablas. Como consecuencia, tmp es una lista de nodos:

length(tmp)
## [1] 5
sapply(tmp, class)
## [1] "xml_node" "xml_node" "xml_node" "xml_node" "xml_node"

La página, aunque no lo parezca, tiene varias tablas. Eso se debe a que en HTML las tablas se utilizan en ocasiones, por abuso, no para almacenar datos tabulares sino para dar formato a las páginas. Sin embargo, es fácil detectar estas seudotablas por inspección. Para identificar la tabla de interés, la que contiene las cotizaciones, podemos examinarlas todas ejecutando la función html_table sobre tmp[[1]], tmp[[2]], etc. hasta dar con ella: es la quinta y última.

Alternativamente, para evitar tener que examinar las tablas una a una se puede hacer

sapply(tmp, function(x) dim(html_table(x, fill = TRUE)))
##      [,1] [,2] [,3] [,4] [,5]
## [1,]   46    1    1    1   35
## [2,]  351    1    7    9    9

que nos indica que la quinta tabla tiene 35 filas, un indicio sólido de que es la que va a contener las cotizaciones de las 35 empresas del IBEX.

Podemos entonces transformar este último nodo en una tabla de R:

ibex <- html_table(tmp[[5]])

Inspecciona la tabla recién cargada. Presta atención al tipo de las columnas. ¿Observas algo raro?

La información colgada en internet está pensada para ser consumida por humanos, no máquinas. Como consecuencia, los números están decorados con separadores de miles, unidades, porcentajes, etc.; los nombres de columnas tienen espacios y otros caracteres extraños, etc. Por eso, la información directamente descargada mediante técnicas de rascado raramente se puede utilizar directamente: es necesario someterla aun proceso sencillo pero laborioso de limpieza.

Dales nombres razonables a las columnas de ibex. Nota: usa colnames.

Es habitual tener que cambiar los nombres de columnas: utilizar nombres con caracteres no estándar es garantía de problemas en los análisis subsiguientes. También es habitual tener que manipular los valores de esas columnas; es frecuente que, por culpa de los separadores de miles, el uso de la coma como separador de los decimales o el uso de unidades, R no reconozca las columnas numéricas como tales y que las trate como texto. Entonces no queda otro remedio que tratar esas columnas para darles el formato correcto y convertirlas en numéricas usando as.numeric. De ahí la sección siguiente, que es una introducción a la manipulación básica de texto con R.

Además de las tablas, hay otros tipos de nodos que puede interesar extraer de una página web. La función html_nodes permite hacerlo utilizando XPath. Puedes echarle un vistazo a esta página, donde se muestra un ejemplo de web scraping en el que se descarga información no contenida en tablas de una serie de páginas.

Examina la documentación del paquete rvest y busca aplicaciones a sus funciones.