#(Antes de empezar, aquí voy a dejar el estilo para RMarkdown) #NOTA: dejé los párrafos con # para que tampoco sea tan feo leerlo en el scipt.
#Mapas interactivos con Leaflet y Ploty
Web scaping, paso a paso
## ── Attaching packages ───────────────────────────── tidyverse 1.3.0 ──
## ✓ ggplot2 3.3.2 ✓ purrr 0.3.4
## ✓ tibble 3.0.3 ✓ dplyr 1.0.1
## ✓ tidyr 1.1.1 ✓ stringr 1.4.0
## ✓ readr 1.3.1 ✓ forcats 0.4.0
## Warning: package 'ggplot2' was built under R version 3.6.2
## Warning: package 'tibble' was built under R version 3.6.2
## Warning: package 'tidyr' was built under R version 3.6.2
## Warning: package 'purrr' was built under R version 3.6.2
## Warning: package 'dplyr' was built under R version 3.6.2
## ── Conflicts ──────────────────────────────── tidyverse_conflicts() ──
## x dplyr::filter() masks stats::filter()
## x dplyr::lag() masks stats::lag()
## Warning: package 'sf' was built under R version 3.6.2
## Linking to GEOS 3.7.2, GDAL 2.4.2, PROJ 5.2.0
## Warning: package 'plotly' was built under R version 3.6.2
##
## Attaching package: 'plotly'
## The following object is masked from 'package:ggplot2':
##
## last_plot
## The following object is masked from 'package:stats':
##
## filter
## The following object is masked from 'package:graphics':
##
## layout
## Parsed with column specification:
## cols(
## Entidad = col_character(),
## sol_2012 = col_double(),
## sol_2013 = col_double(),
## sol_2014 = col_double(),
## sol_2015 = col_double(),
## sol_2016 = col_double(),
## sol_2017 = col_double(),
## sol_2018 = col_double(),
## sol_2019 = col_double(),
## sol_2020 = col_double(),
## sol_Total = col_double()
## )
## Reading layer `DivisionEstatal' from data source `https://raw.githubusercontent.com/JuveCampos/Shapes_Resiliencia_CDMX_CIDE/master/geojsons/Division%20Politica/DivisionEstatal.geojson' using driver `GeoJSON'
## Simple feature collection with 32 features and 7 fields
## geometry type: POLYGON
## dimension: XY
## bbox: xmin: -117.1264 ymin: 14.53401 xmax: -86.74038 ymax: 32.71877
## CRS: 4326
Web scaping, paso a paso
map <- merge(x = geom, y = bd,
by.y = "Entidad", by.x = "ENTIDAD")
glimpse(map)
## Rows: 32
## Columns: 18
## $ ENTIDAD <fct> Aguascalientes, Baja California, Baja California Sur, …
## $ AREA <dbl> 0.4919529, 6.7179224, 6.3738283, 4.7775949, 6.2140708,…
## $ PERIMETER <dbl> 3.784629, 18.921158, 27.474006, 16.883864, 17.552457, …
## $ COV_ <dbl> 257, 2, 54, 299, 371, 10, 308, 16, 312, 86, 264, 325, …
## $ COV_ID <dbl> 270, 1, 53, 305, 389, 10, 324, 20, 329, 99, 285, 377, …
## $ CAPITAL <fct> Aguascalientes, Mexicali, La Paz, Campeche, Tuxtla Gut…
## $ CVE_EDO <fct> 01, 02, 03, 04, 07, 08, 09, 05, 06, 10, 11, 12, 13, 14…
## $ sol_2012 <dbl> 0, 1, 0, 0, 0, 3, 1, 3, 0, 0, 0, 3, 0, 2, 1, 1, 0, 0, …
## $ sol_2013 <dbl> 1, 2, 0, 0, 6, 6, 26, 3, 0, 1, 1, 3, 3, 4, 9, 3, 1, 0,…
## $ sol_2014 <dbl> 0, 3, 2, 1, 1, 2, 11, 1, 1, 0, 1, 7, 1, 1, 2, 4, 1, 0,…
## $ sol_2015 <dbl> 0, 2, 0, 0, 5, 3, 19, 2, 1, 0, 0, 10, 0, 4, 4, 2, 2, 1…
## $ sol_2016 <dbl> 0, 2, 1, 3, 6, 1, 15, 5, 0, 0, 0, 11, 0, 1, 7, 3, 3, 0…
## $ sol_2017 <dbl> 2, 2, 5, 0, 3, 4, 21, 6, 1, 0, 4, 8, 0, 1, 6, 5, 10, 2…
## $ sol_2018 <dbl> 0, 7, 2, 1, 6, 3, 13, 5, 0, 0, 4, 13, 3, 1, 2, 2, 3, 1…
## $ sol_2019 <dbl> 0, 4, 2, 0, 10, 2, 29, 1, 0, 0, 4, 21, 5, 6, 14, 7, 6,…
## $ sol_2020 <dbl> 0, 2, 0, 0, 2, 2, 8, 0, 0, 0, 1, 4, 1, 3, 1, 4, 1, 2, …
## $ sol_Total <dbl> 3, 25, 12, 5, 39, 26, 143, 26, 3, 1, 15, 80, 13, 23, 4…
## $ geometry <POLYGON [°]> POLYGON ((-102.0004 22.2332..., POLYGON ((-114…
RColorBrewer::display.brewer.all()
map %>%
ggplot(aes(x= (fct_reorder(ENTIDAD, sol_Total)),
y= sol_Total, fill = sol_Total))+
geom_col()+
coord_flip()+
theme_minimal() +
theme(axis.text.y = element_text(color = "black"),
axis.title = element_blank(),
plot.title = element_text(size = 14,
color = "black", face = "bold"),
legend.title = element_text(size = 11,
color = "black", face = "bold"),
legend.key.size = unit(1.5, "line"),
legend.text = element_text(size = 9,
color = "black", face = "plain"),
plot.caption = element_text(size = 10, hjust = 1,
color = "black", face = "italic"),
plot.subtitle = element_text(size = 11, color = "black", face = "plain")) +
labs(title = "Empresas que manejan sustancias tóxicas",
fill = "Solicitudes Admitidas para periodistas y defensores\n de Derechos Humanos para acceder al Mecanismo\n por entidad",
caption = "Fuente cortesía de Juve:https://raw.githubusercontent.com/JuveCampos/miniProyectos/master/datos%20Examenes/proteccion.csv") +
scale_fill_distiller(palette = "Blues", direction = 1)+
geom_text(aes(label= map$sol_Total), position=position_dodge(width =0.7),
hjust =-0.2,
color = "blue", size = 2)
## Warning: Use of `map$sol_Total` is discouraged. Use `sol_Total` instead.
ggplotly()
## Warning: Use of `map$sol_Total` is discouraged. Use `sol_Total` instead.
Web scaping, paso a paso
st_crs(map)
## Coordinate Reference System:
## User input: 4326
## wkt:
## GEOGCS["WGS 84",
## DATUM["WGS_1984",
## SPHEROID["WGS 84",6378137,298.257223563,
## AUTHORITY["EPSG","7030"]],
## AUTHORITY["EPSG","6326"]],
## PRIMEM["Greenwich",0,
## AUTHORITY["EPSG","8901"]],
## UNIT["degree",0.0174532925199433,
## AUTHORITY["EPSG","9122"]],
## AUTHORITY["EPSG","4326"]]
min.sol <- min(map$sol_Total, na.rm = T)
max.sol <- max(map$sol_Total, na.rm = T)
pal.sol <- colorNumeric(palette = "Blues",
rev = T,
domain =c(min.sol,
max.sol))
leaflet(map, options = leafletOptions(zoomControl = TRUE)) %>%
addProviderTiles("CartoDB.Positron") %>%
addPolygons(color = ~pal.sol(sol_Total),
fillColor = ~pal.sol(sol_Total),
label = map$ENTIDAD,
popup = paste0(map$sol_Total)) %>%
addLegend(position = c("topright"), pal = pal.sol, values = ~sol_Total, title = "Solicitudes admitidas", opacity = 1)
Web scaping, paso a paso
Lo primero que tenemos que hacer es cargar las librerías que vamos a utilizar
library(rvest)
## Loading required package: xml2
## Warning: package 'xml2' was built under R version 3.6.2
##
## Attaching package: 'rvest'
## The following object is masked from 'package:purrr':
##
## pluck
## The following object is masked from 'package:readr':
##
## guess_encoding
library(tidyverse)
url <- "https://en.wikipedia.org/wiki/Legality_of_cannabis"
code <- read_html(url)
code
## {html_document}
## <html class="client-nojs" lang="en" dir="ltr">
## [1] <head>\n<meta http-equiv="Content-Type" content="text/html; charset= ...
## [2] <body class="mediawiki ltr sitedir-ltr mw-hide-empty-elt ns-0 ns-sub ...
# Nodos de interés... (enlaces al articulo de cada pais en particular)
#mw-content-text > div.mw-parser-output > table > tbody > tr:nth-child(1) > td:nth-child(4) > div > a
#mw-content-text > div.mw-parser-output > table > tbody > tr:nth-child(2) > td:nth-child(4) > div > a
#mw-content-text > div.mw-parser-output > table > tbody > tr:nth-child(3) > td:nth-child(4) > div > a
#mw-content-text > div.mw-parser-output > table > tbody > tr:nth-child(4) > td:nth-child(4) > div > a
# ...
#mw-content-text > div.mw-parser-output > table > tbody > tr:nth-child(206) > td:nth-child(4) > div > a
Web scaping, paso a paso
Primero, bajamos la página 2 para ver cuál es la variable y cuál es la constante. Con la función html_table, obtenemos el nodo de las tablas enteras. En esta función usamos el objeto “code” que se creó anteriormente, con la URL de la página completa. Después, con as_tibble podemos hacer un tibble para poder manipularlo adecuadamente. Para eso usamos el objeto anterior y la bd nombre tabla_con_todo. El paso siguiente es usar html_node, para extraer el contenido del nodo que necesitamos y su información. Es importante usar htlm_text para traducir el lenguaje de html y que lo podamos usar en R. A continuación, tenemos que hacer lo mismo que en el paso anterior usando html_attr, para extraer datos con atributos; el entrecomillado indica que el dato es href. Finalmente, para esta primera parte,generamos un data.frame que esté vacío y lo denominamos articulos_x_pais
node <- "#mw-content-text > div.mw-parser-output > table > tbody > tr:nth-child(2) > td:nth-child(4) > div > a"
tablas <- html_table(code, fill = TRUE)
tabla_con_todo <- tablas[[1]] %>% as_tibble()
html_node(code, node) %>% html_text()
html_node(code, node) %>% html_attr("href")
articulos_x_pais <- data.frame()
Creas un tibble que guardamos en el objeto texto, que contiene los datos de nuestra rama del árbol sin atributos, usando la información que guardamos en el objeto texto y en enlace (que es la rama con atributos). Hay que usar la función rbind.data.frame para que se combinen las ramas con las que estamos trabajando (la información de los nodos) y lo metemos en el objeto articulos x país, que está vacío.
Necesitamos que se vaya imprimiendo con cada descarga en la consola (del universo 2:206) y que integre la frase de “Dato extraído no.”. Usamos la función de paste0 para que no haya espacios. La información se va desplegar cada vez que acabe el ciclo del loop (o bucle).
for (i in 2:206){
node <- paste0("#mw-content-text > div.mw-parser-output > table > tbody > tr:nth-child(", i, ") > td:nth-child(4) > div > a")
texto <- html_node(code, node) %>% html_text()
enlace <- html_node(code, node) %>% html_attr("href")
enlace <- paste0("https://en.wikipedia.org/", enlace)
articulos_x_pais <- rbind.data.frame(articulos_x_pais,
tibble(texto = texto,
enlace = enlace))
print(paste0("Dato extraído no. ", i, "/206"))
}
Aquí terminó el primer loop. Lo que sigue es imprimir la base de datos articulos_x_pais. Lo hacemos para checar el contenido. Después, creo que se está guardando la base de datos para que a la hora de hacer el loop se tomen en cuenta las modificaciones que le vamos a hacer a la bd. Primero se está filtrando del nodo de “texto” (sin atributos) los valores NA. Después, con mutate, se crea una nueva columna con los valores de NA con el nombre de contenido_paginas. La condición lógica de este loop es definir el universo de la secuencia para que vayan del 1 al total de número de renglones de la bd. Se usa el contador [i], que está vacío para que se vaya llenando en cada secuencia.
Web scaping, paso a paso
Aquí se usa tryCatch para que sepamos dónde está ocurriendo un error pero que no deje de hacer el loop. Extraemos de la bd articulos_x_pais la variable de “enlace” y la guardamos en el objeto de url2; ponemos la [i] para cumplir con la función de los ciclos de los loops. A continuación, guardamos este objeto en otro nuevo llamado code2, y usamos la función read_html que baja el código html. Vamos a crear un nuevo objeto llamado parrafos_de_cada_articulo donde pedimos que nos extraiga la rama de información que nos interesa, con el objeto que creamos de codes2, que corresponde a la información de la variable “enlace” que tiene los atributos. Hacemos esto para que R pueda traducir el lenguaje de programación de html con la función de html_text.
Web scaping, paso a paso
Indicamos <br>, creo, para que quede un espacio entre las observaciones. Finalmente, metemos el objeto parrafos_de_cada_articulo en el renglón que corresponda el contador [i] de la variable de contenido_páginas, que es la columna con los valores NA que hicimos anteriormente. Después pedimos que imprima con paste0 para evitar los espacios de la parte variable, llamado a la variable “texto” de la bd de articulos_x_pais, y usando el contador de [i]. Lo siguiente es resolver la parte estética de los errores. En el primer caso, queremos que los errores que aparezcan de “texto” en la bd articulos_x_pais se sustituyan con el siguiente texto: “Error!, no hay datos en esta página:”, respondiendo a la función de tryCatch. Y queremos que imprima “No hay artículos” para los renglones en los que hay valores NA que están en la columna que creamos con mutate: contenido_paginas.
articulos_x_pais
articulos_x_pais <- articulos_x_pais %>%
filter(!is.na(texto)) %>%
mutate(contenido_paginas = NA)
for(i in 1:nrow(articulos_x_pais)){
tryCatch({
url2 <- articulos_x_pais$enlace[i]
code2 <- read_html(url2)
parrafos_de_cada_articulo <- html_nodes(code2, "p") %>%
html_text() %>%
paste0(collapse = "<br>")
articulos_x_pais$contenido_paginas[i] <- parrafos_de_cada_articulo
print(paste0("Descargados párrafos del articulo de: ", articulos_x_pais$texto[i]))
},
error = function(e){
print(paste0("Error!, no hay datos en esta pagina: ", articulos_x_pais$texto[i]))
articulos_x_pais$contenido_paginas[i] <- "No hay artículo"
})
}
Con estas bases de datos podemos ver el país del artículo, el estatuto legal de la mariguana (cannabis), el estatus médico y las notas. En la segunda base, vemos el texto del artículo, el enlace y el contenido de la página.
# Bases finales
tabla_con_todo
articulos_x_pais
Web scaping, paso a paso
Podemos obtener una radiografía del estatus legal del cannabis en el mundo. Además podemos hacer gráficas para hacer un análisis más profundo y contrastar entre países de forma más clara. Desde la cancha ciudadana, sería información relevante para entender el panorama global y poder hacer argumentos fundamentados a favor de los intereses particulares de cada sujeto. Desde la cancha de política pública podría cumplir con la misma función, pero desde otro terreno.