Capítulo 7 Extracción de datos de la web

En apartados anteriores hemos visto cómo importar ficheros para trabajar con los datos. Veremos ahora cómo obtenerlos desde la web.

7.1 Importación de tablas desde Wikipedia

Comenzaremos con el paquete htmltab que nos permite importar tablas disponibles en Wikipedia. Procederemos a extraer los datos de población mundial de una tabla disponible en: https://en.wikipedia.org/wiki/World_population.

Por defecto, si no ofrecemos ningún argumento, la función importa la primera tabla disponible, tal como nos indica el mensaje de respuesta.

library(htmltab)
tabla <- htmltab("https://en.wikipedia.org/wiki/World_population")
## Argument 'which' was left unspecified. Choosing first table.

Si queremos importar otra tabla (por ejemplo, la octava) sólo hay que indicarlo. Lo hacemos y visualizamos las primeras filas.

tabla8 <- htmltab("https://en.wikipedia.org/wiki/World_population", 8)
head(tabla8)
##   Year    Population Yearly growth >> % Yearly growth >> Number Density(pop/km)
## 3 1951 2,584,034,261              1.88%              47,603,112              17
## 4 1952 2,630,861,562              1.81%              46,827,301              18
## 5 1953 2,677,608,960              1.78%              46,747,398              18
## 6 1954 2,724,846,741              1.76%              47,237,781              18
## 7 1955 2,773,019,936              1.77%              48,173,195              19
## 8 1956 2,822,443,282              1.78%              49,423,346              19
##   Urban population >> Number Urban population >> %
## 3                775,067,697                   30%
## 4                799,282,533                   30%
## 5                824,289,989                   31%
## 6                850,179,106                   31%
## 7                877,008,842                   32%
## 8                904,685,164                   32%

Procederemos ahora a obtener la población de los municipios catalanes desde otra tabla también disponible en Wikipedia. En este caso, el mensaje de warning nos indica que, dado que la última columna parece estar vacía, la ha suprimido. No obstante, también nos ofrece información sobre cómo resolver el problema si queremos conservar este campo.

Municipis <- htmltab("https://en.wikipedia.org/wiki/Municipalities_of_Catalonia", 1)
## Warning: Columns [No. ofEMDs] seem to have no data and are removed. Use
## rm_nodata_cols = F to suppress this behavior.

Si visualizamos la estructura de la tabla, vemos que la información está en formato character (chr), por lo que no podremos hacer cálculos (por ejemplo, ¿cuál es la población media de los municipios catalanes?).

str(Municipis)
## 'data.frame':    948 obs. of  6 variables:
##  $ Municipality    : chr  "Abella de la Conca" "Abrera" "Ã\200ger" "Agramunt" ...
##  $ Comarca         : chr  "Pallars JussÃ" "Baix Llobregat" "Noguera" "Urgell" ...
##  $ Province        : chr  "Lleida" "Barcelona" "Lleida" "Lleida" ...
##  $ Population(2014): chr  "170" "12,125" "594" "5,515" ...
##  $ Area(km)        : chr  "78.3" "19.9" "160.6" "79.6" ...
##  $ Density         : chr  "2.2" "609.3" "3.7" "69.3" ...

Vamos a eliminar las comas correspondientes a los miles, convertir los datos a formato numérico y calcular la media de población por municipio.

Municipis$`Population(2014)` <- gsub(",", "", Municipis$`Population(2014)`) #eliminar comas
Municipis$`Population(2014)` <- as.numeric(Municipis$`Population(2014)`) #convertir a numérico
mean(Municipis$`Population(2014)`) #calcular la media
## [1] 15862.66

En realidad, si nos fijamos en la tabla original, veremos que la cifra es errónea, ya que está sumando la última fila con el total a las poblaciones de los municipios (por eso el resultado es aproximadamente el doble).

Veamos como importar de nuevo la tabla, eliminar la última fila y rehacer el cálculo (la eliminación de las comas y la conversión a formato numérico las hemos unido en una única línea).

Municipis <- htmltab("https://en.wikipedia.org/wiki/Municipalities_of_Catalonia", 1)
## Warning: Columns [No. ofEMDs] seem to have no data and are removed. Use
## rm_nodata_cols = F to suppress this behavior.
Municipis2 <- Municipis[-c(948),] #elimina la última fila
Municipis2$`Population(2014)` <- as.numeric(gsub(",", "", Municipis2$`Population(2014)`))
mean(Municipis2$`Population(2014)`)
## [1] 7939.707

Podemos filtrar los municipios de la provincia de Barcelona.

Barcelona <- Municipis2[Municipis2$Province == "Barcelona", 1:4]
head(Barcelona)
##             Municipality          Comarca  Province Population(2014)
## 3                 Abrera   Baix Llobregat Barcelona            12125
## 6     Aguilar de Segarra            Bages Barcelona              251
## 8             Aiguafreda Vallès Oriental Barcelona             2498
## 30                Alella          Maresme Barcelona             9651
## 45                Alpens            Osona Barcelona              300
## 51 L'Ametlla del Vallès Vallès Oriental Barcelona             8283

O los municipios grandes, entendiendo como tales los que tienen una población superior a 50.000 habitantes.

Grans_municipis <- Municipis2[Municipis2$`Population(2014)` > 50000, 1:4]
head(Grans_municipis, 10)
##                  Municipality            Comarca  Province Population(2014)
## 80                   Badalona        Barcelonès Barcelona           217210
## 92                  Barcelona        Barcelonès Barcelona          1602386
## 206             Castelldefels     Baix Llobregat Barcelona            63255
## 228    Cerdanyola del Vallès Vallès Occidental Barcelona            57402
## 252    Cornellà de Llobregat     Baix Llobregat Barcelona            86234
## 331                    Girona           Gironès    Girona            97227
## 342                Granollers   Vallès Oriental Barcelona            59930
## 358 L'Hospitalet de Llobregat        Barcelonès Barcelona           253518
## 387                    Lleida             Segrià   Lleida           139176
## 411                   Manresa              Bages Barcelona            75297

7.2 Importación de páginas web (primer ejemplo)

Vamos a importar los datos de los 50 libros más leídos a España según la web https://www.goodreads.com/book/most_read“. Para ello, utilizaremos el paquete rvest.

library(rvest)

Almacenamos el código HTML de la página de la que obtener los datos

webpage <- read_html("https://www.goodreads.com/book/most_read")

A continuación, extraemos los títulos de los libros. Para conocer las etiquetas, podéis utilizar una extensión del navegador como SelectorGadget (o la opción “Inspecciona” con el botón derecho del ratón).

titulos_html <- html_nodes(webpage,'.bookTitle')

Convertimos los títulos de html a texto y los visualizamos

titulos <- html_text(titulos_html)
head(titulos, 5)
## [1] "\n        From Blood and Ash (Blood and Ash, #1)\n"
## [2] "\n        The Seven Husbands of Evelyn Hugo\n"     
## [3] "\n        Beautiful World, Where Are You\n"        
## [4] "\n        Dune (Dune, #1)\n"                       
## [5] "\n        Normal People\n"

Vamos a eliminar el símbolo “” y los espacios iniciales

titulos2 <- gsub("\n        ","",titulos)
head(titulos2, 5)
## [1] "From Blood and Ash (Blood and Ash, #1)\n"
## [2] "The Seven Husbands of Evelyn Hugo\n"     
## [3] "Beautiful World, Where Are You\n"        
## [4] "Dune (Dune, #1)\n"                       
## [5] "Normal People\n"

También eliminados el símbolo “” al final. Comprobamos que es correcto.

titulos3 <- gsub("\n","",titulos2)
head(titulos3, 5)
## [1] "From Blood and Ash (Blood and Ash, #1)"
## [2] "The Seven Husbands of Evelyn Hugo"     
## [3] "Beautiful World, Where Are You"        
## [4] "Dune (Dune, #1)"                       
## [5] "Normal People"

Hacemos lo mismo con los autores

autores_html <- html_nodes(webpage,'a.authorName')
autores <- html_text(autores_html)
head(autores)
## [1] "Jennifer L. Armentrout" "Taylor Jenkins Reid"    "Sally Rooney"          
## [4] "Frank Herbert"          "Sally Rooney"           "Madeline Miller"

Y lo mismo con los ratings

rating_html <- html_nodes(webpage,'.minirating')
rating <- html_text(rating_html)
rating
##  [1] " 4.30 avg rating — 181,924 ratings"  
##  [2] " 4.45 avg rating — 583,661 ratings"  
##  [3] " 3.85 avg rating — 52,699 ratings"   
##  [4] " 4.24 avg rating — 944,941 ratings"  
##  [5] " 3.85 avg rating — 640,017 ratings"  
##  [6] " 4.41 avg rating — 530,595 ratings"  
##  [7] " 4.09 avg rating — 636,881 ratings"  
##  [8] " 4.53 avg rating — 1,534 ratings"    
##  [9] " 4.21 avg rating — 74,366 ratings"   
## [10] " 4.32 avg rating — 14,815 ratings"   
## [11] " 4.08 avg rating — 878 ratings"      
## [12] " 4.47 avg rating — 71,394 ratings"   
## [13] " 4.09 avg rating — 438,766 ratings"  
## [14] " 4.25 avg rating — 398,129 ratings"  
## [15] " 3.97 avg rating — 609,068 ratings"  
## [16] " 4.08 avg rating — 22,066 ratings"   
## [17] " 4.18 avg rating — 766,421 ratings"  
## [18] " 4.12 avg rating — 10,574 ratings"   
## [19] " 4.41 avg rating — 429,846 ratings"  
## [20] " 3.88 avg rating — 387,517 ratings"  
## [21] " 3.78 avg rating — 38,904 ratings"   
## [22] " 4.47 avg rating — 551,096 ratings"  
## [23] " 4.58 avg rating — 544,359 ratings"  
## [24] " 4.53 avg rating — 142,958 ratings"  
## [25] " 4.35 avg rating — 235,045 ratings"  
## [26] " 4.23 avg rating — 378,577 ratings"  
## [27] " 3.83 avg rating — 34,221 ratings"   
## [28] " 4.33 avg rating — 278,292 ratings"  
## [29] " 3.70 avg rating — 185,641 ratings"  
## [30] " 4.03 avg rating — 145,002 ratings"  
## [31] " 3.97 avg rating — 213,459 ratings"  
## [32] " 4.27 avg rating — 3,386,128 ratings"
## [33] " 4.30 avg rating — 12,091 ratings"   
## [34] " 4.17 avg rating — 14,112 ratings"   
## [35] " 4.11 avg rating — 1,126,044 ratings"
## [36] " 4.36 avg rating — 355,598 ratings"  
## [37] " 4.10 avg rating — 21,572 ratings"   
## [38] " 4.44 avg rating — 379,941 ratings"  
## [39] " 4.59 avg rating — 1,733 ratings"    
## [40] " 4.22 avg rating — 27,813 ratings"   
## [41] " 3.82 avg rating — 266,147 ratings"  
## [42] " 4.42 avg rating — 474,355 ratings"  
## [43] " 4.61 avg rating — 101,257 ratings"  
## [44] " 4.22 avg rating — 436,395 ratings"  
## [45] " 4.46 avg rating — 12,808 ratings"   
## [46] " 4.59 avg rating — 365,169 ratings"  
## [47] " 3.87 avg rating — 14,896 ratings"   
## [48] " 4.19 avg rating — 3,586,642 ratings"
## [49] " 4.47 avg rating — 7,979,054 ratings"
## [50] " 4.69 avg rating — 41,576 ratings"

Combinamos en un dataframe títulos, autores y ratings. El resultado lo exportamos a un fichero txt.

libros <- data.frame(Titulo = titulos3, Autor = autores, Rating = rating)
write.table(libros, file = "Libros", sep = "\t")

7.3 Importación de páginas web (segundo ejemplo)

pelis <- read_html("https://m.imdb.com/chart/bestpicture/")

titles_html <- html_nodes(pelis,'.best-picture-item-title')
titles <- html_text(titles_html)
head(titles, 10)
##  [1] " Nomadland\n(2020)\n"                                      
##  [2] " Gisaengchung\n(2019)\n"                                   
##  [3] " Green Book\n(2018)\n"                                     
##  [4] " The Shape of Water\n(2017)\n"                             
##  [5] " Moonlight\n(2016)\n"                                      
##  [6] " Spotlight\n(2015)\n"                                      
##  [7] " Birdman or (The Unexpected Virtue of Ignorance)\n(2014)\n"
##  [8] " 12 Years a Slave\n(2013)\n"                               
##  [9] " Argo\n(2012)\n"                                           
## [10] " The Artist\n(2011)\n"
titles2 <- gsub("\n","",titles)
head(titles2)
## [1] " Nomadland(2020)"          " Gisaengchung(2019)"      
## [3] " Green Book(2018)"         " The Shape of Water(2017)"
## [5] " Moonlight(2016)"          " Spotlight(2015)"

Vamos ahora con los años.

year_html <- html_nodes(pelis,'.titlemeter')
year <- html_text(year_html)
head(year)
## [1] "\n2020 winner\n" "\n2019 winner\n" "\n2018 winner\n" "\n2017 winner\n"
## [5] "\n2016 winner\n" "\n2015 winner\n"
year2 <- gsub("\n","",year)
year3 <- gsub(" winner","",year2)
head(year3,50)
##  [1] "2020" "2019" "2018" "2017" "2016" "2015" "2014" "2013" "2012" "2011"
## [11] "2010" "2009" "2008" "2007" "2006" "2005" "2004" "2003" "2002" "2001"
## [21] "2000" "1999" "1998" "1997" "1996" "1995" "1994" "1993" "1992" "1991"
## [31] "1990" "1989" "1988" "1987" "1986" "1985" "1984" "1983" "1982" "1981"
## [41] "1980" "1979" "1978" "1977" "1976" "1975" "1974" "1973" "1972" "1971"

Y ahora con la puntuación.

puntos_html <- html_nodes(pelis,'.spacing-right-small')
puntos <- html_text(puntos_html)
puntos
##  [1] "7.4" "8.6" "8.2" "7.3" "7.4" "8.1" "7.7" "8.1" "7.7" "7.9" "8.0" "7.5"
## [13] "8.0" "8.1" "8.5" "7.7" "8.1" "8.9" "7.2" "8.2" "8.5" "8.3" "7.1" "7.8"
## [25] "7.4" "8.3" "8.8" "8.9" "8.2" "8.6" "8.0" "7.4" "8.0" "7.7" "8.1" "7.2"
## [37] "8.3" "7.4" "8.0" "7.2" "7.7" "7.8" "8.1" "8.0" "8.1" "8.7" "9.0" "8.3"
## [49] "9.2" "7.7" "7.9" "7.8" "7.4" "7.9" "7.7" "8.0" "7.8" "6.5" "8.3" "7.5"
## [61] "8.3" "8.1" "6.7" "8.1" "6.8" "7.7" "8.1" "7.6" "6.6" "7.2" "8.2" "7.5"
## [73] "7.6" "7.3" "8.1" "7.9" "7.0" "8.5" "7.6" "7.7" "8.1" "8.1" "7.9" "7.2"
## [85] "6.7" "7.7" "8.1" "5.9" "7.4" "5.9" "8.1" "5.7" "7.5" "8.1"

Por último, combinamos todos los campos (títulos, años y puntuaciones) en un dataframe y lo exportamos.

peliculas <- data.frame(Titulo = titles2, Year = year3, Puntuacion = puntos)

write.table(peliculas, file = "Pelis", sep = "\t")

7.4 Ejercicio 7. Extracción de datos de la web

La siguiente página web recoge una selección de los 100 mejores discos de la historia según una revista musical. Intenta extraer la información relativa a los títulos de los discos, los grupos y la descripción de cada álbum.

The 500 Greatest Albums of All Time: 100-1

https://www.nme.com/photos/the-500-greatest-albums-of-all-time-100-1-1426116

De manera opcional, puedes intentar hacer lo mismo con la siguiente página que incluye una lista de 100 ciudades del mundo. Si lo prefieres, puedes escoger cualquier otra página web que te interese o puedes probar a importar a R alguna tabla disponible en Wikipedia.

World’s best cities

https://www.bestcities.org/rankings/worlds-best-cities/