Cosecha de datos de la web

Web Scraping Rvest Worldmeter

Se realiza una introducción al web scraping con el paquete rvest para la cosecha de datos de la web.

Franklin Santos https://franklinsantos.com (Agritech Bolivia)
06-01-2021

1. Introducción

El “web scraping” es un proceso automatizado de recolección de datos e información de los sitios web. R es un lenguaje de programación muy popular para el web scraping. En él se utilizará un popular paquete de R llamado rvest para el raspado de datos web. rvest es un paquete que facilita el scrape (o cosecha) de datos de páginas web html, inspirado en librerías como beautiful soup. Está diseñado para trabajar con magrittr de manera que puedas expresar operaciones complejas como elegantes pipelines compuestos de piezas simples y fáciles de entender (Wickham 2021).

2. Metodología

Para realizar el web scraping (raspado de datos web), las librerías más usuales son rvest y rselenium. En este caso daremos uso al paquete rvest (Wickham 2021). Para la limpieza de datos se usará el paquete tidyverse (Wickham et al. 2019).

2.1. Cargado de paquetes

#paquete para el raspado de datos de la web
library(rvest)
library(tidyverse)

2.2. Procedimiento de raspado de datos web

Los datos a explorar serán de la página web de Worldometer. Esta página contiene diferentes reportes estadídticos; sin embargo, a nosotros nos interesa los reportes diarios del COVID-19 del mundo. Para ello se procederá de la siguiente manera:

#Raspar datos formato tabla
url = "https://www.worldometers.info/coronavirus/"
datacovid = read_html(url) %>% 
  html_table()
head(datacovid)
[[1]]
# A tibble: 238 x 22
     `#` `Country,Other` TotalCases  NewCases   TotalDeaths NewDeaths
   <int> <chr>           <chr>       <chr>      <chr>       <chr>    
 1    NA "North America" 39,918,340  "+12,259"  901,223     "+390"   
 2    NA "Asia"          52,085,649  "+181,453" 703,621     "+4,538" 
 3    NA "South America" 29,331,975  "+11,596"  908,166     "+247"   
 4    NA "Europe"        46,808,469  "+38,336"  1,076,895   "+1,023" 
 5    NA "Africa"        4,929,333   "+5,436"   131,967     "+88"    
 6    NA "Oceania"       69,077      "+49"      1,252       ""       
 7    NA ""              721         ""         15          ""       
 8    NA "World"         173,143,564 "+249,129" 3,723,139   "+6,286" 
 9     1 "USA"           34,178,104  "+3,352"   611,710     "+99"    
10     2 "India"         28,685,804  "+113,445" 343,954     "+3,235" 
# … with 228 more rows, and 16 more variables: TotalRecovered <chr>,
#   NewRecovered <chr>, ActiveCases <chr>, Serious,Critical <chr>,
#   Tot\U00a0Cases/1M pop <chr>, Deaths/1M pop <chr>,
#   TotalTests <chr>, Tests/1M pop <chr>, Population <chr>,
#   Continent <chr>, 1 Caseevery X ppl <chr>,
#   1 Deathevery X ppl <chr>, 1 Testevery X ppl <int>,
#   New Cases/1M pop <dbl>, New Deaths/1M pop <dbl>,
#   Active Cases/1M pop <chr>

[[2]]
# A tibble: 238 x 22
     `#` `Country,Other` TotalCases  NewCases   TotalDeaths NewDeaths
   <int> <chr>           <chr>       <chr>      <chr>       <chr>    
 1    NA "Asia"          51,904,196  "+210,332" 699,083     "+4,107" 
 2    NA "North America" 39,906,081  "+32,047"  900,833     "+1,027" 
 3    NA "South America" 29,320,379  "+167,900" 907,919     "+4,142" 
 4    NA "Europe"        46,770,133  "+50,729"  1,075,872   "+1,208" 
 5    NA "Africa"        4,923,897   "+14,498"  131,879     "+253"   
 6    NA "Oceania"       69,028      "+211"     1,252       ""       
 7    NA ""              721         ""         15          ""       
 8    NA "World"         172,894,435 "+475,717" 3,716,853   "+10,737"
 9     1 "China"         91,170      "+24"      4,636       ""       
10     2 "USA"           34,174,752  "+17,821"  611,611     "+574"   
# … with 228 more rows, and 16 more variables: TotalRecovered <chr>,
#   NewRecovered <chr>, ActiveCases <chr>, Serious,Critical <chr>,
#   Tot\U00a0Cases/1M pop <chr>, Deaths/1M pop <chr>,
#   TotalTests <chr>, Tests/1M pop <chr>, Population <chr>,
#   Continent <chr>, 1 Caseevery X ppl <chr>,
#   1 Deathevery X ppl <chr>, 1 Testevery X ppl <int>,
#   New Cases/1M pop <chr>, New Deaths/1M pop <dbl>,
#   Active Cases/1M pop <chr>

[[3]]
# A tibble: 238 x 22
     `#` `Country,Other` TotalCases  NewCases   TotalDeaths NewDeaths
   <int> <chr>           <chr>       <chr>      <chr>       <chr>    
 1    NA "Asia"          51,693,864  "+211,296" 694,976     "+4,237" 
 2    NA "North America" 39,874,034  "+34,275"  899,806     "+713"   
 3    NA "South America" 29,152,479  "+179,599" 903,777     "+4,321" 
 4    NA "Europe"        46,719,404  "+52,612"  1,074,664   "+1,404" 
 5    NA "Africa"        4,909,399   "+13,439"  131,626     "+307"   
 6    NA "Oceania"       68,817      "+135"     1,252       ""       
 7    NA ""              721         ""         15          ""       
 8    NA "World"         172,418,718 "+491,356" 3,706,116   "+10,982"
 9     1 "China"         91,146      "+24"      4,636       ""       
10     2 "USA"           34,156,931  "+16,881"  611,037     "+512"   
# … with 228 more rows, and 16 more variables: TotalRecovered <chr>,
#   NewRecovered <chr>, ActiveCases <chr>, Serious,Critical <chr>,
#   Tot\U00a0Cases/1M pop <chr>, Deaths/1M pop <chr>,
#   TotalTests <chr>, Tests/1M pop <chr>, Population <chr>,
#   Continent <chr>, 1 Caseevery X ppl <chr>,
#   1 Deathevery X ppl <chr>, 1 Testevery X ppl <int>,
#   New Cases/1M pop <chr>, New Deaths/1M pop <dbl>,
#   Active Cases/1M pop <chr>

En este caso se pudo obtener tres tablas con su respectiva información. La primera tabla se refiere a la información COVID de hoy. La segunda tabla es la información COVID de ayer y la tercera es información de hace dos días.

2.3. Limpieza de datos

Para este propósito realizaremos la limpieza de datos de la primera tabla.

# Asignar a un objeto la primera tabla 
table1 = datacovid[[1]]
head(table1)
# A tibble: 6 x 22
    `#` `Country,Other` TotalCases NewCases TotalDeaths NewDeaths
  <int> <chr>           <chr>      <chr>    <chr>       <chr>    
1    NA North America   39,918,340 +12,259  901,223     "+390"   
2    NA Asia            52,085,649 +181,453 703,621     "+4,538" 
3    NA South America   29,331,975 +11,596  908,166     "+247"   
4    NA Europe          46,808,469 +38,336  1,076,895   "+1,023" 
5    NA Africa          4,929,333  +5,436   131,967     "+88"    
6    NA Oceania         69,077     +49      1,252       ""       
# … with 16 more variables: TotalRecovered <chr>, NewRecovered <chr>,
#   ActiveCases <chr>, Serious,Critical <chr>,
#   Tot\U00a0Cases/1M pop <chr>, Deaths/1M pop <chr>,
#   TotalTests <chr>, Tests/1M pop <chr>, Population <chr>,
#   Continent <chr>, 1 Caseevery X ppl <chr>,
#   1 Deathevery X ppl <chr>, 1 Testevery X ppl <int>,
#   New Cases/1M pop <dbl>, New Deaths/1M pop <dbl>,
#   Active Cases/1M pop <chr>

Los datos no estan limpios para operar algun análisis exploratorio, para ello necesitamos eliminar las primeras filas de continentes y las últimas filas de totales. El propósito es solamente quedarse con los países.

Para este proceso, necesito filtrar a traves de la columna #, ya que solamente anumera a los países.

El objeto aux es para diferencias entre los números perdidos (NA) y los dátos existentes en la columna #. La función is.na identificará datos faltantes. Si identifica NA nos dirá TRUE y se encuentra información será FALSE. Esto nos ayuda a filtrar de forma más sencilla y quedarnos con los datos que requerimos.

#Filtrado de datos
aux = is.na(table1$'#')

tablecountry = table1[!aux,]
tablecountry
# A tibble: 222 x 22
     `#` `Country,Other` TotalCases NewCases   TotalDeaths NewDeaths
   <int> <chr>           <chr>      <chr>      <chr>       <chr>    
 1     1 USA             34,178,104 "+3,352"   611,710     "+99"    
 2     2 India           28,685,804 "+113,445" 343,954     "+3,235" 
 3     3 Brazil          16,803,472 ""         469,784     ""       
 4     4 France          5,694,076  ""         109,857     ""       
 5     5 Turkey          5,276,468  "+6,169"   47,976      "+94"    
 6     6 Russia          5,108,129  "+8,947"   123,037     "+377"   
 7     7 UK              4,506,016  "+6,238"   127,823     "+11"    
 8     8 Italy           4,227,719  "+2,557"   126,415     "+73"    
 9     9 Argentina       3,884,447  ""         79,873      ""       
10    10 Germany         3,702,866  "+1,176"   89,637      "+32"    
# … with 212 more rows, and 16 more variables: TotalRecovered <chr>,
#   NewRecovered <chr>, ActiveCases <chr>, Serious,Critical <chr>,
#   Tot\U00a0Cases/1M pop <chr>, Deaths/1M pop <chr>,
#   TotalTests <chr>, Tests/1M pop <chr>, Population <chr>,
#   Continent <chr>, 1 Caseevery X ppl <chr>,
#   1 Deathevery X ppl <chr>, 1 Testevery X ppl <int>,
#   New Cases/1M pop <dbl>, New Deaths/1M pop <dbl>,
#   Active Cases/1M pop <chr>

La base de datos aún presenta signos + y , en las columnas. El siguiente procedimiento es quitar esos signos y convertirlo a datos numéricos.

#Eliminando los signos
tableclean = tablecountry %>% 
  mutate_at(3:15, parse_number)

head(tableclean)
# A tibble: 6 x 22
    `#` `Country,Other` TotalCases NewCases TotalDeaths NewDeaths
  <int> <chr>                <dbl>    <dbl>       <dbl>     <dbl>
1     1 USA               34178104     3352      611710        99
2     2 India             28685804   113445      343954      3235
3     3 Brazil            16803472       NA      469784        NA
4     4 France             5694076       NA      109857        NA
5     5 Turkey             5276468     6169       47976        94
6     6 Russia             5108129     8947      123037       377
# … with 16 more variables: TotalRecovered <dbl>, NewRecovered <dbl>,
#   ActiveCases <dbl>, Serious,Critical <dbl>,
#   Tot\U00a0Cases/1M pop <dbl>, Deaths/1M pop <dbl>,
#   TotalTests <dbl>, Tests/1M pop <dbl>, Population <dbl>,
#   Continent <chr>, 1 Caseevery X ppl <chr>,
#   1 Deathevery X ppl <chr>, 1 Testevery X ppl <int>,
#   New Cases/1M pop <dbl>, New Deaths/1M pop <dbl>,
#   Active Cases/1M pop <chr>

Ahora nuestra base de datos desde la colunma 3 al 15 ya estan en formato numérica. La cual posibilita realizar operaciones estadíticas.

3. Conclusión

rvest es muy manejable para raspar datos de la web. En esta opurtunidad solamente vimos una opción de cosecha de datos disponibles en formato tabla. Para datos que no esten en formato tabla se requiere utilizar una herramienta llamada selector gadget. Esta herramienta ayuda a extraer nodos de html y a traves de ellos capturamos información.

Wickham, Hadley. 2021. Rvest: Easily Harvest (Scrape) Web Pages. https://CRAN.R-project.org/package=rvest.
Wickham, Hadley, Mara Averick, Jennifer Bryan, Winston Chang, Lucy D’Agostino McGowan, Romain François, Garrett Grolemund, et al. 2019. “Welcome to the tidyverse.” Journal of Open Source Software 4 (43): 1686. https://doi.org/10.21105/joss.01686.

References

Citation

For attribution, please cite this work as

Santos (2021, June 1). Franklin Santos: Cosecha de datos de la web. Retrieved from https://franklinsantos.com/posts/2021-05-31-webscraping/

BibTeX citation

@misc{santos2021cosecha,
  author = {Santos, Franklin},
  title = {Franklin Santos: Cosecha de datos de la web},
  url = {https://franklinsantos.com/posts/2021-05-31-webscraping/},
  year = {2021}
}