Tutorial #5 Construccion de datasets (BORRADOR)

En este tutorial vamos a explorar distintas maneras de construir un dataset.

  1. Vamos a conectar con una API (Wikipedia) de la manera más básica;
  2. Vamos a conectar con una API (Twitter) usando el paquete rtweet;
  3. Mencionamos las limtaciones de la API de Facebook;
  4. Hacemos scrapping para recuperar contenido de páginas web tipo OJS.

Al final listamos algunos otros recursos donde se pueden obtener datasets.

5.1 APIs

Un API (Application Programming Interface) es una “puerta de entrada” que algunas aplicaciones ofrecen para interatuar con sus datos y sus funciones de forma ordenada. Se puede utilizar una API para recuperar contenido de las bases de datos de la aplicación (e.g., obtener tweets de Twitter) de forma legal y dentro de las condiciones de acceso y uso que la aplicación impone.

Generalmente, las API se utilizan a través del protocolo HTTP, de modo que se puede interactuar con ella a través desde distintos lenguajes y/o programas que nos permita hacer requests (acciones para leer, enviar y manipular datos) por internet, ya sea un navegador como Chrome, un programa específico como Postman, o R.

Cada API tiene su dirección URL, y una forma particular de requerir parámetros por medio de ella. Un ejemplo de un request simple que devuelve la información de una foto de Facebook es la siguiente URL, que se puede consultar desde el navegador: https://graph.facebook.com/facebook/picture?redirect=false.

El response o resultado del request es sólo datos, es decir, contenido de la aplicación sin los elementos de diseño o interfaces. Generalmente los datos se escriben en un formato flexible, como XML o JSON. En ocasiones, hay algunos metadatos en los encabezados de las responses que indican cómo resultó nuestros request por medio de un código (e.g., un status 200 significa que el request se procesó OK, mientras un 404 indica que el recurso que buscamos no está disponible).

Muchas APIs requieren una autenticación para poder procesar un request, esto les permite regular el acceso a la información. Para ello, en algunos casos basta con proveer algunas credenciales que obtenemos al registrarnos como usuarios de la aplicación. En otros casos, como en las APIs de Facebook y Twitter, además es necesario registrarse como “desarrollador” y registrar una “aplicación” para la cual se piden ciertos permisos particulares, como leer o postear contenido. Como resultado de este registro generalmente obtenemos algunas “llaves” que incluimos en nuestros requests como parámetros. Otras APIs, como la de Google, tienen algunos servicios pagos y requieren que registremos una tarjeta de crédito en nuestro perfil, para cobrarnos por consumo.

Por suerte, para algunas aplicaciones contamos con paquetes de R para interactuar con sus APIs. Estos paquetes se encargan de formatear los request de pedido de información, incluyendo las claves de seguridad que hayamos obtenido, y/o leer los datos que nos devuelven en un formato compatible con R, como un dataframe.

5.1.1 API de Wikipedia (Sin paquetes!)

La primera API que vamos a explorar es la de Wikipedia. Nos interesa esta API por 2 razones: (1) no requiere autenticación… por lo menos para el tipo de request que vamos a estar haciendo; y (2) no hay paquete de R para interactuar con ella, asi que vamos a usarla “sin rueditas”…. aunque vale la aclaración, si vamos a usar un paquete para hacer requests por HTTP, llamado httr.

Particularmente:

  1. vamos a hacer un request, componiendo una URL y llamandola por GET (un método del protocolo HTTP, que usás todo el tiempo para navegar);
  2. vamos a leer el response, que está en formato JSON, y vamos a ver cómo lo podemos convertir a un formato más cómodo con R.

Antes que nada, tenemos que saber qué clase de requests se pueden hacer (qué puedo preguntarle a la API). Todas las aplicaciones tienen una API Reference que aclara esto; otras, mas copadas, tienen una API Explorer que permite “componer” con formularios los parámetros de los requests y explorar los resultados. Wikipedia, por suerte es uno de ellos: https://es.wikipedia.org/wiki/Especial:Zona_de_pruebas_de_la_API.

Lo primero que vamos a hacer es buscar entradas que incluyan las palabras “big data.”

Para hacer este request es necesario conocer como componer la URL. En el caso de Wikipedia esto se compone por una URL base (https://es.wikipedia.org/w/api.php?) seguido de la acción que nos interesa realizar: “query” porque vamos a buscar páginas (&action=query). Esta acción requiere un criterio (&srsearch=big%20data) y que especifiquemos qué devolver y en qué formato (&list=search&format=json): https://es.wikipedia.org/w/api.php?action=query&list=search&srsearch=big%20data&format=json

library(tidyverse)

criterio <- "big%20data" # como es una URL hay que codificar algunos caracteres, como el espacio (%20)

library(httr) # vamos usar este paquete para usar el protocolo HTTP
llamada <- httr::GET(paste0("https://es.wikipedia.org/w/api.php?action=query&list=search&format=json&srsearch=", criterio)) # hacemos el request via HTTP/GET

llamada$status_code # si es 200, todo OK
## [1] 200

Veamos un pedazo de la respuesta, que se encuentra en formato JSON:

{
  batchcomplete: "",
  continue: {
    sroffset: 10,
    continue: "-||"
  },
  query: {
    searchinfo: {
      totalhits: 6783
    },
    search: [
      {
        ns: 0,
        title: "Macrodatos",
        pageid: 5242736,
        size: 115077,
        wordcount: 13491,
        snippet: "también llamados datos masivos, inteligencia de datos, datos a gran escala o <span class="searchmatch">big</span> <span class="searchmatch">data</span> (terminología en idioma inglés utilizada comúnmente) es un término que",
        timestamp: "2021-06-03T16:23:33Z"
        },
      {
        ns: 0,
        title: "Big Bang",
        pageid: 6822,
        size: 71060,
        wordcount: 9270,
        snippet: "En cosmología, se entiende por <span class="searchmatch">Big</span> Bang,[1]​[2]​ también llamada la Gran Explosión (término proveniente del astrofísico Fred Hoyle a modo de burla de",
        timestamp: "2021-06-12T03:48:05Z"
        },
...

Este contenido se puede acceder a través del content del objeto generado por httr::GET. Esta función convierte el JSON es una lista, es decir, una colección de objetos en R.

Esta lista tiene 4 elementos: batchcomplete y continue se usan para paginar los resultados; query que a su vez tiene 2 elementos: primero informa cuantos resultados hubo; y despues en search incluye los resultados. Arriba mostramos 2 resultados, que incluyen un título, un pageid, y otra info.

Veamos como podemos acceder a este JSON:

respuesta <- httr::content(x = llamada) # httr nos lee el content del objeto que generamos

class(respuesta) # chequeemos que es una lista
## [1] "list"
str(respuesta, max=3) # veamos su estructura ... lo que nos interesa está en query > search
## List of 3
##  $ batchcomplete: chr ""
##  $ continue     :List of 2
##   ..$ sroffset: int 10
##   ..$ continue: chr "-||"
##  $ query        :List of 2
##   ..$ searchinfo:List of 1
##   .. ..$ totalhits: int 6831
##   ..$ search    :List of 10
##   .. ..$ :List of 7
##   .. ..$ :List of 7
##   .. ..$ :List of 7
##   .. ..$ :List of 7
##   .. ..$ :List of 7
##   .. ..$ :List of 7
##   .. ..$ :List of 7
##   .. ..$ :List of 7
##   .. ..$ :List of 7
##   .. ..$ :List of 7
resultados <- respuesta[["query"]][["search"]] # tomamos del objeto respuesta su objeto query, y luego su objeto "search"

La lista tiene dentro 1 lista por cada elemento (resultado). Como estas listas son iguales, podemos convertirlas a un formato tabla.

library(dplyr) # para transformar la lista vamos a usar dplyr
resultados_df <- dplyr::bind_rows(resultados) # vamos a separar las listas y unirlas en formato dataframe
glimpse(resultados_df) # veamos la tabla armada
## Rows: 10
## Columns: 7
## $ ns        <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
## $ title     <chr> "Macrodatos", "Big Bang", "Tidy data", "Big Rapids", "Río Big Sioux", "Big Be~
## $ pageid    <int> 5242736, 6822, 8754307, 4702847, 2648540, 4102878, 4115045, 4103098, 480752, ~
## $ size      <int> 115077, 71060, 2054, 3911, 8016, 11851, 2731, 3073, 3007, 129354
## $ wordcount <int> 13491, 9270, 247, 319, 575, 1121, 208, 241, 494, 13781
## $ snippet   <chr> "también llamados datos masivos, inteligencia de datos, datos a gran escala o~
## $ timestamp <chr> "2021-06-03T16:23:33Z", "2021-06-12T03:48:05Z", "2020-08-26T21:03:30Z", "2019~

Ahora vamos a usar estos valores para recuperar los contenidos de las páginas.

Para esta acción la URL base es la misma que veniamos usando (https://es.wikipedia.org/w/api.php?) seguido de la acción que nos interesa realizar: “parse” porque le pedimos que imprima una pagina (&action=parse). Esta acción requiere un criterio (&page=) con el título que querramos recuperar, y finalmente, que especifiquemos qué en qué formatos (&list=search&format=json).

Veamos los títulos que podemos recuperar y compongamos la URL con los primeros.

resultados_df$title # veamos cuales son los primeros titulos
##  [1] "Macrodatos"          "Big Bang"            "Tidy data"           "Big Rapids"         
##  [5] "Río Big Sioux"       "Big Bear Lake"       "Big Pine"            "Big Bear City"      
##  [9] "Big Water"           "The Big Bang Theory"
criterio2 <- str_replace(string = resultados_df$title[1], pattern = " ", replacement = "_")  # llamemos al primer elemento, reemplazando espacios por _ .... esto hay que mejorarlo para encodear otros elementos, como acentos

library(httr) # vamos usar este paquete para usar el protocolo HTTP
llamada2 <- httr::GET(paste0("https://es.wikipedia.org/w/api.php?action=parse&prop=text&formatversion=2&format=json&page=", criterio2)) # hacemos el request via HTTP/GET

llamada2$status_code # veamos si devuelve 200 == OK
## [1] 200
respuesta2 <- httr::content(x = llamada2) # httr nos lee el content del objeto que generamos

class(respuesta2) # chequeemos que es una lista
## [1] "list"
str(respuesta2, max=3) # veamos su estructura ... lo que nos interesa está en parse > text
## List of 1
##  $ parse:List of 3
##   ..$ title : chr "Macrodatos"
##   ..$ pageid: int 5242736
##   ..$ text  : chr "<div class=\"mw-parser-output\"><div class=\"thumb tright\"><div class=\"thumbinner\" style=\"width:252px;\"><a"| __truncated__
respuesta2[["parse"]][["text"]] %>% str_sub(start = 1, end = 500) # tomemos un fragmento
## [1] "<div class=\"mw-parser-output\"><div class=\"thumb tright\"><div class=\"thumbinner\" style=\"width:252px;\"><a href=\"/wiki/Archivo:Viegas-UserActivityonWikipedia.gif\" class=\"image\"><img alt=\"\" src=\"//upload.wikimedia.org/wikipedia/commons/thumb/6/69/Viegas-UserActivityonWikipedia.gif/250px-Viegas-UserActivityonWikipedia.gif\" decoding=\"async\" width=\"250\" height=\"188\" class=\"thumbimage\" srcset=\"//upload.wikimedia.org/wikipedia/commons/thumb/6/69/Viegas-UserActivityonWikipedia.gif/375px-Viegas-UserActivit"

Lo que vemos es el código fuente (HTML) de esta página: https://es.wikipedia.org/wiki/Macrodatos

5.1.2 API de Twitter (con rtweet)

Twitter requiere generar una aplicacion y registrarla en una cuenta de desarrollador. Para es necesario:

  1. Registrarse en Twitter developers y aplicar al acceso: https://developer.twitter.com/en/apply-for-access
  2. Crear un proyecto: https://developer.twitter.com/en/portal/projects/new

Para esto te va a pedir que definas: tipo de proyecto (academico), descripcion (objetivos, posta), crear/vincularla a una app (va a pedir solo el nombre). Twitter aclara: Apps are where you get your access keys and tokens and set permissions.

Luego, vamos a ir a la pagina de app que acabamos de crear, a buscar las credenciales para acceder:

(Estamos usando el metodo de generar un token… pero hay otros: https://cran.r-project.org/web/packages/rtweet/vignettes/auth.html)

  1. Vamos a configurar las app permissions, que habilitan a distintas acciones, como buscar y leer tweets, o postear.
  2. Vasmo a configurar credenciales, yendo a “Keys and tokens”

Aquí vamos a buscar “API Key and Secret” y “Access Token and Secret.” Ambos tienen 2 claves: key y secret key.

# estos datos son de ejemplo...

api_key <- "afYS4vbIlPAj096E60c4W1fiK"
api_secret_key <- "bI91kqnqFoNCrZFbsjAWHD4gJ91LQAhdCJXCj3yscfuULtNkuu"
access_token <- "9551451262-wK2EmA942kxZYIwa5LMKZoQA4Xc2uyIiEwu2YXL"
access_token_secret <- "9vpiSGKg1fIPQtxc5d5ESiFlZQpfbknEN1f1m2xe5byw7"

Con estas 4 claves mas el nombre de la app, ya podemos ir al codigo, a trabajar con el paquete rtweet. Vamos a registrar un token que queda en memoria y que el pack va a usar para autenticar cada pedido a twitter.

library(rtweet)

token <- create_token(
  app = registered_app,
  consumer_key = api_key,
  consumer_secret = api_secret_key,
  access_token = access_token,
  access_secret = access_token_secret) # creamos token de autenticacion en memoria

Ahora ya estamos en condiciones de buscar tweets.

library(tidyverse)
tweets <- search_tweets(q = "#bigdata",n = 20,lang = "es", include_rts = FALSE)

names(tweets)
##  [1] "user_id"                 "status_id"               "created_at"             
##  [4] "screen_name"             "text"                    "source"                 
##  [7] "display_text_width"      "reply_to_status_id"      "reply_to_user_id"       
## [10] "reply_to_screen_name"    "is_quote"                "is_retweet"             
## [13] "favorite_count"          "retweet_count"           "quote_count"            
## [16] "reply_count"             "hashtags"                "symbols"                
## [19] "urls_url"                "urls_t.co"               "urls_expanded_url"      
## [22] "media_url"               "media_t.co"              "media_expanded_url"     
## [25] "media_type"              "ext_media_url"           "ext_media_t.co"         
## [28] "ext_media_expanded_url"  "ext_media_type"          "mentions_user_id"       
## [31] "mentions_screen_name"    "lang"                    "quoted_status_id"       
## [34] "quoted_text"             "quoted_created_at"       "quoted_source"          
## [37] "quoted_favorite_count"   "quoted_retweet_count"    "quoted_user_id"         
## [40] "quoted_screen_name"      "quoted_name"             "quoted_followers_count" 
## [43] "quoted_friends_count"    "quoted_statuses_count"   "quoted_location"        
## [46] "quoted_description"      "quoted_verified"         "retweet_status_id"      
## [49] "retweet_text"            "retweet_created_at"      "retweet_source"         
## [52] "retweet_favorite_count"  "retweet_retweet_count"   "retweet_user_id"        
## [55] "retweet_screen_name"     "retweet_name"            "retweet_followers_count"
## [58] "retweet_friends_count"   "retweet_statuses_count"  "retweet_location"       
## [61] "retweet_description"     "retweet_verified"        "place_url"              
## [64] "place_name"              "place_full_name"         "place_type"             
## [67] "country"                 "country_code"            "geo_coords"             
## [70] "coords_coords"           "bbox_coords"             "status_url"             
## [73] "name"                    "location"                "description"            
## [76] "url"                     "protected"               "followers_count"        
## [79] "friends_count"           "listed_count"            "statuses_count"         
## [82] "favourites_count"        "account_created_at"      "verified"               
## [85] "profile_url"             "profile_expanded_url"    "account_lang"           
## [88] "profile_banner_url"      "profile_background_url"  "profile_image_url"
glimpse(tweets)
## Rows: 20
## Columns: 90
## $ user_id                 <chr> "872131876168900608", "994948727566753792", "122716346017531084~
## $ status_id               <chr> "1404493839579107328", "1404493587845427202", "1404490451143606~
## $ created_at              <dttm> 2021-06-14 17:40:00, 2021-06-14 17:39:00, 2021-06-14 17:26:32,~
## $ screen_name             <chr> "ECON_Automation", "citicven", "MariaGuzur", "GrupoAIS", "con10~
## $ text                    <chr> "Smart Management System (SMS), otro de nuestros productos #Sma~
## $ source                  <chr> "Twitter Web App", "TweetDeck", "Twitter Web App", "Twitter Web~
## $ display_text_width      <dbl> 254, 171, 277, 268, 272, 254, 262, 278, 262, 275, 267, 209, 276~
## $ reply_to_status_id      <chr> NA, NA, NA, NA, "1404470968593141766", NA, NA, NA, NA, NA, NA, ~
## $ reply_to_user_id        <chr> NA, NA, NA, NA, "1668435678", NA, NA, NA, NA, NA, NA, NA, NA, N~
## $ reply_to_screen_name    <chr> NA, NA, NA, NA, "MedicoFernandez", NA, NA, NA, NA, NA, NA, NA, ~
## $ is_quote                <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, ~
## $ is_retweet              <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, ~
## $ favorite_count          <int> 0, 0, 1, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0
## $ retweet_count           <int> 0, 2, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0
## $ quote_count             <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,~
## $ reply_count             <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,~
## $ hashtags                <list> <"SmartFactory", "Automation", "Manufacturing", "BigData", "IoT~
## $ symbols                 <list> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,~
## $ urls_url                <list> "econ-tech.com/wp-content/upl…", "citicven.la", "gzmaria97.wix~
## $ urls_t.co               <list> "https://t.co/uD4UE15vJw", "https://t.co/b6pJ7XUD2L", "https:/~
## $ urls_expanded_url       <list> "https://econ-tech.com/wp-content/uploads/2020/06/Caso-de-Exit~
## $ media_url               <list> "http://pbs.twimg.com/media/E3vLN9pWQAEX5MQ.png", "http://pbs.~
## $ media_t.co              <list> "https://t.co/Nz52uQM5wE", "https://t.co/URmJQuK8h4", NA, "htt~
## $ media_expanded_url      <list> "https://twitter.com/ECON_Automation/status/140449383957910732~
## $ media_type              <list> "photo", "photo", NA, "photo", NA, "photo", "photo", NA, "phot~
## $ ext_media_url           <list> "http://pbs.twimg.com/media/E3vLN9pWQAEX5MQ.png", "http://pbs.~
## $ ext_media_t.co          <list> "https://t.co/Nz52uQM5wE", "https://t.co/URmJQuK8h4", NA, "htt~
## $ ext_media_expanded_url  <list> "https://twitter.com/ECON_Automation/status/140449383957910732~
## $ ext_media_type          <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA~
## $ mentions_user_id        <list> NA, <"3017145715", "994948727566753792">, NA, <"171962485", "1~
## $ mentions_screen_name    <list> NA, <"QualitasGlobal", "citicven">, NA, <"Leandrosoft", "Felaba~
## $ lang                    <chr> "es", "es", "es", "es", "es", "es", "es", "es", "es", "es", "e~
## $ quoted_status_id        <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, "1~
## $ quoted_text             <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, "¡A~
## $ quoted_created_at       <dttm> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 202~
## $ quoted_source           <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, "Tw~
## $ quoted_favorite_count   <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 2,~
## $ quoted_retweet_count    <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 1, ~
## $ quoted_user_id          <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, "19~
## $ quoted_screen_name      <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, "mi~
## $ quoted_name             <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, "Mi~
## $ quoted_followers_count  <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 124~
## $ quoted_friends_count    <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 309~
## $ quoted_statuses_count   <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 691~
## $ quoted_location         <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, "Sp~
## $ quoted_description      <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, "Mi~
## $ quoted_verified         <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, FAL~
## $ retweet_status_id       <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,~
## $ retweet_text            <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,~
## $ retweet_created_at      <dttm> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,~
## $ retweet_source          <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,~
## $ retweet_favorite_count  <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA~
## $ retweet_retweet_count   <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,~
## $ retweet_user_id         <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,~
## $ retweet_screen_name     <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,~
## $ retweet_name            <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,~
## $ retweet_followers_count <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,~
## $ retweet_friends_count   <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,~
## $ retweet_statuses_count  <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,~
## $ retweet_location        <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,~
## $ retweet_description     <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,~
## $ retweet_verified        <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,~
## $ place_url               <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,~
## $ place_name              <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,~
## $ place_full_name         <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,~
## $ place_type              <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,~
## $ country                 <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,~
## $ country_code            <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,~
## $ geo_coords              <list> <NA, NA>, <NA, NA>, <NA, NA>, <NA, NA>, <NA, NA>, <NA, NA>, <NA~
## $ coords_coords           <list> <NA, NA>, <NA, NA>, <NA, NA>, <NA, NA>, <NA, NA>, <NA, NA>, <NA~
## $ bbox_coords             <list> <NA, NA, NA, NA, NA, NA, NA, NA>, <NA, NA, NA, NA, NA, NA, NA,~
## $ status_url              <chr> "https://twitter.com/ECON_Automation/status/140449383957910732~
## $ name                    <chr> "ECON Tech", "CiTICven", "María Gutiérrez", "AIS", "Opino en d~
## $ location                <chr> "Puebla, México", "Caracas, Venezuela", "VIT/VLL/SEV", "Barcelo~
## $ description             <chr> "Somos expertos en tecnología avanzada para control de procesos~
## $ url                     <chr> "https://t.co/rESTisWvVv", "https://t.co/txqe2OZ3J8", "https://~
## $ protected               <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, ~
## $ followers_count         <int> 454, 251, 30, 373, 140, 3686, 155, 1099, 370, 2647, 386, 649, 6~
## $ friends_count           <int> 318, 196, 95, 124, 752, 1292, 664, 3373, 479, 567, 955, 490, 77~
## $ listed_count            <int> 11, 4, 0, 41, 1, 120, 1, 150, 7, 69, 0, 65, 0, 682, 1105, 391, ~
## $ statuses_count          <int> 7569, 1766, 72, 2102, 2757, 5583, 163, 2824, 1899, 8468, 2299, ~
## $ favourites_count        <int> 131, 127, 6, 804, 2010, 3606, 249, 567, 184, 1199, 323, 3408, 6~
## $ account_created_at      <dttm> 2017-06-06 16:43:37, 2018-05-11 14:33:56, 2020-02-11 09:32:34, ~
## $ verified                <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, ~
## $ profile_url             <chr> "https://t.co/rESTisWvVv", "https://t.co/txqe2OZ3J8", "https:/~
## $ profile_expanded_url    <chr> "http://www.econ-tech.com", "http://citicven.la", "https://gzma~
## $ account_lang            <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,~
## $ profile_banner_url      <chr> "https://pbs.twimg.com/profile_banners/872131876168900608/16221~
## $ profile_background_url  <chr> "http://abs.twimg.com/images/themes/theme1/bg.png", NA, NA, "ht~
## $ profile_image_url       <chr> "http://pbs.twimg.com/profile_images/1322742634335657984/TsQBYG~

Además de buscar tweets hay otras funciones:

?rtweet::search_users() # busca usuarios
?rtweet::get_trends()  # busca tendencias en algún lugar (por latitud y longitud)
?rtweet::get_timeline() # busca los tweets de 1 o mas usuarios

Sobre los límites del API de Twitter: https://developer.twitter.com/en/docs/twitter-api/v1/rate-limits

5.1.3 Una nota sobre la API de Facebook

Facebook impuso serias limitaciones a la información que es posible consultar via API.

Al igual que Twitter, Facebook requiere registrar una app. Pero, los tipos de apps que se sugieren y los permisos a otorgar, son indicativos del poco acceso que se puede obtener a los contenidos de la red social para investigación, dejando la API mayormente para interactuar y controlar campañas y negocios: https://developers.facebook.com/docs/development/create-an-app/app-dashboard/app-types.

Lo más cercano que hay al feed de contenido social publicado por los usuarios, se limita a las páginas de las empresas, para lo que hay que tener una cuenta de negocios asociada a la página, y además someterse a una revisión de la app. https://developers.facebook.com/docs/permissions/reference/pages_read_user_content/

En contrapartida, Facebook ofrece algunos datasets: https://research.fb.com/data/

Mas info: https://theconversation.com/facebooks-data-lockdown-is-a-disaster-for-academic-researchers-94533

Como resultado de estos cambios, uno de los paquetes más usados para interactuar con el API de Facebook para R (https://github.com/pablobarbera/Rfacebook) ya no se mantiene más…

5.2 Web Scraping

El proceso de recuperar contenido de manera automática a partir de páginas web se conoce como Web Scraping.

Para poder recuperar contenido de páginas web hay 2 requisitos: tenemos que averiguar:

  1. las URLs de las páginas que queremos leer: en muchos casos pueden ser URLs dinámicas, con parámetros y valores, que podemos manipular.
  2. la estructura HTML de la página donde está el contenido: como probablemente no nos interese todo el contenido de la página (logos,textos,botones) sino sólo algunos textos, tenemos que poder identificar en qué parte del template de la página se insertan los contenidos que nos interesa, para así poder mapearlos.

Ambas informaciones varían de sitio en sitio y de sistema en sistema. Para lo primero conviene entender lo básico de la sintáxis de las URLs: https://en.wikipedia.org/wiki/URL#Syntax; para lo segundo, comprender lo básico de HTML markup https://en.wikipedia.org/wiki/HTML#Markup, incluyendo como tomar los identificadores de un pedazo de sitio https://towardsdatascience.com/web-scraping-in-r-using-rvest-and-selectorgadget-5fc5124547e, algo para lo que el Dev.Tools del Chrome (F12 navegando la página) puede ser muy util.

5.2.1 Webscraping resultados de búqsqueda de OJS

Open Journal Systems (OJS) es un sistema de manejo editorial para revistas académicas. Es usado mayormente por universidades y por revistas que no tienen acuerdos editoriales con grandes empresas.

En este tutorial vamos a intentar recuperar los links a los artículos sobre “big data” que estén publicados en un listado de revistas que utilizan el sistema OJS:

sitios_ojs = c(
  "http://ojs.sociologia-alas.org/index.php/CyC/",
  "http://ediciones.ucsh.cl/ojs/index.php/TSUCSH/",
  #"https://revistachasqui.org/index.php/chasqui/", # tiene muchos resultados
  "http://revistamexicanadesociologia.unam.mx/index.php/rms/"
  )

Nuestro primer requisito es averiguar las URLs de las páginas que queremos leer.

Recordemos que queremos encontrar sólo artículos que contengan “big data,” de modo que necesitamos utilizar la función de búsqueda de cada OJS. Luego de realizar algunas búsqueda “a mano” y obsevar cómo el OJS construye sus URLs (https://docs.pkp.sfu.ca/dev/documentation/en/architecture-routes), podemos armar las de los resultados de búsqueda:

criterio <- "%22big+data%22"
url_scrapear <- paste( sitios_ojs , # vamos a contatenar pedazos de textos
                       "search/search?query=",
                       criterio,
                       sep = "")
url_scrapear
## [1] "http://ojs.sociologia-alas.org/index.php/CyC/search/search?query=%22big+data%22"            
## [2] "http://ediciones.ucsh.cl/ojs/index.php/TSUCSH/search/search?query=%22big+data%22"           
## [3] "http://revistamexicanadesociologia.unam.mx/index.php/rms/search/search?query=%22big+data%22"

Ya tenemos listo el primer requisito: las URLs de las páginas a leer!

Nuestro segundo requisito es encontrar algún patrón para identificar el pedazo de contenido que nos interesa. En nuestro caso, se trata de los links a los artículos, de modo que, otra vez, tenemos que ver cómo se componen las URLs del OJS. Según su convención, los artículos en OJS tienen una URL con esta estructura: url_sitio + “/article/view/” + id_artículo. Con XPath, podemos escribir una expresión que filtre este tipo de link: //a[contains(@href, "/article/view/")].

Ya tenemos listo el primer requisito: el patrón para identificar el contenido!

Ahora vamos a podemos comenzar con el scraping. Aquí vamos a ayudarnos con el paquete rvest.

library(rvest)

tomar_links_resultados <- function( url ) {
  url_con <- url(url, "rb") # abrimos una conexion para leer la pagina
  webpage <- xml2::read_html(url_con) # leemos la pagina
  close(url_con) # cerramos la conexion
  xpath <- '//a[contains(@href, "/article/view/")]' # XPath para distinguir links a artículos
  links <- rvest::html_nodes(webpage, xpath = xpath) %>% # leemos los pedazos de codigo de links
    html_attr('href') %>% # nos quedamos solo con la URL de los links
    return()
}

links_articulos <- character()

for (i in 1:length(url_scrapear)) { # vamos a ejecutar un bucle, donde i toma el valor del indice del array de URLs a visitar
  message("scrapeando ",url_scrapear[i]) # que nos muestre en pantalla por donde anda
  links_articulos <- c(links_articulos, tomar_links_resultados(url_scrapear[i]))
}
## scrapeando http://ojs.sociologia-alas.org/index.php/CyC/search/search?query=%22big+data%22
## scrapeando http://ediciones.ucsh.cl/ojs/index.php/TSUCSH/search/search?query=%22big+data%22
## scrapeando http://revistamexicanadesociologia.unam.mx/index.php/rms/search/search?query=%22big+data%22
links_articulos
## [1] "http://ojs.sociologia-alas.org/index.php/CyC/article/view/245"                    
## [2] "http://ojs.sociologia-alas.org/index.php/CyC/article/view/219"                    
## [3] "http://ojs.sociologia-alas.org/index.php/CyC/article/view/213"                    
## [4] "http://ojs.sociologia-alas.org/index.php/CyC/article/view/151"                    
## [5] "http://ojs.sociologia-alas.org/index.php/CyC/article/view/131"                    
## [6] "http://ediciones.ucsh.cl/ojs/index.php/TSUCSH/article/view/268"                   
## [7] "http://revistamexicanadesociologia.unam.mx/index.php/rms/article/view/57723"      
## [8] "http://revistamexicanadesociologia.unam.mx/index.php/rms/article/view/57723/51185"

Listo, tenemos los links de los artículos donde dice “big data” en varias revistas distintas. Ahora, seguro nos interese hacer un nuevo proceso de scraping para recuperar algunos metadatos de esos artículos. Pero para eso, mejor usemos el paquete ojsr.

library(ojsr)

metadata <- ojsr::get_html_meta_from_article(input_url = links_articulos, verbose = TRUE)
## trying url 1/8 http://ojs.sociologia-alas.org/index.php/CyC/article/view/245
## scrapped http://ojs.soci ... found 53 elements using criteria .//meta
## trying url 2/8 http://ojs.sociologia-alas.org/index.php/CyC/article/view/219
## scrapped http://ojs.soci ... found 61 elements using criteria .//meta
## trying url 3/8 http://ojs.sociologia-alas.org/index.php/CyC/article/view/213
## scrapped http://ojs.soci ... found 54 elements using criteria .//meta
## trying url 4/8 http://ojs.sociologia-alas.org/index.php/CyC/article/view/151
## scrapped http://ojs.soci ... found 52 elements using criteria .//meta
## trying url 5/8 http://ojs.sociologia-alas.org/index.php/CyC/article/view/131
## scrapped http://ojs.soci ... found 53 elements using criteria .//meta
## trying url 6/8 http://ediciones.ucsh.cl/ojs/index.php/TSUCSH/article/view/268
## scrapped http://edicione ... found 51 elements using criteria .//meta
## trying url 7/8 http://revistamexicanadesociologia.unam.mx/index.php/rms/article/view/57723
## scrapped http://revistam ... found 51 elements using criteria .//meta
## trying url 8/8 http://revistamexicanadesociologia.unam.mx/index.php/rms/article/view/57723
## scrapped http://revistam ... found 51 elements using criteria .//meta
glimpse(metadata)
## Rows: 426
## Columns: 5
## $ input_url         <chr> "http://ojs.sociologia-alas.org/index.php/CyC/article/view/245", "htt~
## $ meta_data_name    <chr> NA, "viewport", "generator", "DC.Creator.PersonalName", "DC.Creator.P~
## $ meta_data_content <chr> NA, "width=device-width, initial-scale=1.0", "Open Journal Systems 3.~
## $ meta_data_scheme  <chr> NA, NA, NA, NA, NA, "ISO8601", "ISO8601", "ISO8601", "ISO8601", NA, N~
## $ meta_data_xmllang <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, "en", "es", "pt", NA, NA, NA, NA,~
metadata %>% 
  filter(
      meta_data_name=="citation_keywords", 
      trimws(meta_data_content)!=""
    ) %>% # filtering keywords
  mutate(keyword = trimws(tolower(meta_data_content))) %>%
  count(keyword, sort = TRUE) 
##                                                                     keyword n
## 1  big data, epistemología, metodología, ciencias sociales, redes sociales. 2
## 2    big data, epistemology, methodology, social sciences, social networks. 2
## 3                                                       relaciones públicas 2
## 4                                                            automatización 1
## 5                                                                  big data 1
## 6                                                civilización transcultural 1
## 7                                                              colonialidad 1
## 8                                                competencias profesionales 1
## 9                                                              comunicación 1
## 10                                                 comunicación estratégica 1
## 11                                                                 covid-19 1
## 12                                                            crisis raigal 1
## 13                                                      cultura del trabajo 1
## 14                                                          cyberdemocracia 1
## 15                                                               empirismo. 1
## 16                                                        huellas digitales 1
## 17                                                         industria láctea 1
## 18                                                     investigación social 1
## 19                                                  métodos deinvestigación 1
## 20                                                               modernidad 1
## 21                                                         perfil de egreso 1
## 22                                                       políticas públicas 1
## 23                                                             sindicalismo 1
## 24                                            tecnologías de la información 1
## 25                                                    tecnologías digitales 1
## 26                                                                     vida 1

Además de recuperar metadatos del HTML de los OJS, el paquete permite:

?ojsr::get_issues_from_archive() # busca numeros de la revista
?ojsr::get_articles_from_issue() # busca artículos de un número de la revista
?ojsr::get_galleys_from_article() # toma las galeradas (los pdf, xml, epub, y demas)

5.3 Recursos

5.3.2 Repositorios de datasets:

5.3.3 Herrameintas de Scraping: