Hemos visto antes cómo capturar datos de Twitter a traves de su REST API. Otra opción es capturar datos en tiempo real con su Streaming API. Capturar datos en streaming requiere un instrumental especial para poder mantener una conexión abierta y capturar datos a medida que llegan a la plataforma. La libraría streamR simplifica esta tarea.

library(ROAuth)
library(streamR)

El sistemde autorización es un poco diferente al que vimos en el caso de la REST API. En primer lugar, definimos los valores que deberemos pasar a la función de autorización del paquete ROauth:

creds <- readLines("./credentials-twitter.txt")
requestURL <- "https://api.twitter.com/oauth/request_token"
accessURL <- "https://api.twitter.com/oauth/access_token"
authURL <- "https://api.twitter.com/oauth/authorize"
consumerKey <- creds[1]
consumerSecret <- creds[2]

y ahora creamos un objeto que contenga las credenciales

auth <- OAuthFactory$new(consumerKey=consumerKey,
                         consumerSecret=consumerSecret,
                         requestURL=requestURL,
                         accessURL=accessURL,
                         authURL=authURL)
auth$handshake(cainfo=system.file("CurlSSL",
                                  "cacert.pem",
                                  package="RCurl"))

Con esto, podemos hacer llamadas al stream de datos. Es importante recordar que lo que estamos haciendo es mantener una conexión abierta. La función filterStream nos da varias opciones para definir cuándo cerrar la conexión.

Podemos decidir cuánto tiempo estaremos conectados:

filterStream(file="tweets_trump.json",
             track="trump",
             timeout=15,
             oauth=auth)

La función guarda un archivo a disco y ahora podemos leerlo de vuelta:

tw <- readLines("tweets_trump.json")
tw[[1]]

Como vemos, se trata de objetos JSON iguales que los he hemos usado cuando aprendíamos a interaccionar con una REST API. Podemos ahora convertir estas cadenas a una list igual que antes:

fromJSON(tw[[1]])

y podríamos convertir todos los datos que hemos recogido

jsontw <- lapply(tw, fromJSON)

Sin embargo, el paquete provee de una función para simplificar esta tarea

head(parseTweets("tweets_trump.json"))

También podemos seleccionar cuántos tweets queremos en total:

filterStream(file.name="tweets_trump.json",
             track="trump",
             tweets=10,
             oauth=auth)

o incluso definir un área geográfica para tuits geolocalizados

filterStream(file.name="",
             language="es",
             locations=c(-74,40,-73,41),
             timeout=60,
             oauth=auth)

Otra opción que nos ofrece streamR es una conexión a una muestra aleatoria de todos los tweets que se producen.

sampleStream("tweetsSample.json", timeout=60, oauth=auth, verbose=FALSE)
tweets.df <- parseTweets("tweetsSample.json", verbose=FALSE)
head(tweets.df$friends_count)
LS0tIAp0aXRsZTogIkNhcHR1cmFyIGRhdG9zIGVuIHN0cmVhbWluZyIKZGF0ZTogImByIGZvcm1hdChTeXMudGltZSgpLCAnJUIgJWQsICVZJylgIgotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFLCBjYWNoZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGV2YWwgPSBGQUxTRSkgCmBgYAoKSGVtb3MgdmlzdG8gYW50ZXMgY8OzbW8gY2FwdHVyYXIgZGF0b3MgZGUgVHdpdHRlciBhIHRyYXZlcyBkZSBzdSBSRVNUIEFQSS4gT3RyYQpvcGNpw7NuIGVzIGNhcHR1cmFyIGRhdG9zIF9lbiB0aWVtcG8gcmVhbF8gY29uIHN1IFN0cmVhbWluZyBBUEkuIENhcHR1cmFyIGRhdG9zCmVuIHN0cmVhbWluZyByZXF1aWVyZSB1biBpbnN0cnVtZW50YWwgZXNwZWNpYWwgcGFyYSBwb2RlciBtYW50ZW5lciB1bmEgY29uZXhpw7NuCmFiaWVydGEgeSBjYXB0dXJhciBkYXRvcyBhIG1lZGlkYSBxdWUgbGxlZ2FuIGEgbGEgcGxhdGFmb3JtYS4gTGEgbGlicmFyw61hCmBzdHJlYW1SYCBzaW1wbGlmaWNhIGVzdGEgdGFyZWEuIAoKYGBge3J9CmxpYnJhcnkoUk9BdXRoKQpsaWJyYXJ5KHN0cmVhbVIpCmBgYAoKRWwgc2lzdGVtZGUgYXV0b3JpemFjacOzbiBlcyB1biBwb2NvIGRpZmVyZW50ZSBhbCBxdWUgdmltb3MgZW4gZWwgY2FzbyBkZSBsYSBSRVNUCkFQSS4gRW4gcHJpbWVyIGx1Z2FyLCBkZWZpbmltb3MgbG9zIHZhbG9yZXMgcXVlIGRlYmVyZW1vcyBwYXNhciBhIGxhIGZ1bmNpw7NuIGRlCmF1dG9yaXphY2nDs24gZGVsIHBhcXVldGUgYFJPYXV0aGA6CgpgYGB7ciBldmFsPUZBTFNFfQpjcmVkcyA8LSByZWFkTGluZXMoIi4vY3JlZGVudGlhbHMtdHdpdHRlci50eHQiKQpyZXF1ZXN0VVJMIDwtICJodHRwczovL2FwaS50d2l0dGVyLmNvbS9vYXV0aC9yZXF1ZXN0X3Rva2VuIgphY2Nlc3NVUkwgPC0gImh0dHBzOi8vYXBpLnR3aXR0ZXIuY29tL29hdXRoL2FjY2Vzc190b2tlbiIKYXV0aFVSTCA8LSAiaHR0cHM6Ly9hcGkudHdpdHRlci5jb20vb2F1dGgvYXV0aG9yaXplIgpjb25zdW1lcktleSA8LSBjcmVkc1sxXQpjb25zdW1lclNlY3JldCA8LSBjcmVkc1syXQpgYGAKCnkgYWhvcmEgY3JlYW1vcyB1biBvYmpldG8gcXVlIGNvbnRlbmdhIGxhcyBjcmVkZW5jaWFsZXMKCmBgYHtyIGV2YWw9RkFMU0V9CmF1dGggPC0gT0F1dGhGYWN0b3J5JG5ldyhjb25zdW1lcktleT1jb25zdW1lcktleSwKICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN1bWVyU2VjcmV0PWNvbnN1bWVyU2VjcmV0LAogICAgICAgICAgICAgICAgICAgICAgICAgcmVxdWVzdFVSTD1yZXF1ZXN0VVJMLAogICAgICAgICAgICAgICAgICAgICAgICAgYWNjZXNzVVJMPWFjY2Vzc1VSTCwKICAgICAgICAgICAgICAgICAgICAgICAgIGF1dGhVUkw9YXV0aFVSTCkKYXV0aCRoYW5kc2hha2UoY2FpbmZvPXN5c3RlbS5maWxlKCJDdXJsU1NMIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJjYWNlcnQucGVtIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhY2thZ2U9IlJDdXJsIikpCmBgYAoKQ29uIGVzdG8sIHBvZGVtb3MgaGFjZXIgbGxhbWFkYXMgYWwgc3RyZWFtIGRlIGRhdG9zLiBFcyBpbXBvcnRhbnRlIHJlY29yZGFyIHF1ZQpsbyBxdWUgZXN0YW1vcyBoYWNpZW5kbyBlcyBtYW50ZW5lciB1bmEgY29uZXhpw7NuIGFiaWVydGEuIExhIGZ1bmNpw7NuCmBmaWx0ZXJTdHJlYW1gIG5vcyBkYSB2YXJpYXMgb3BjaW9uZXMgcGFyYSBkZWZpbmlyIGN1w6FuZG8gY2VycmFyIGxhIGNvbmV4acOzbi4gCgpQb2RlbW9zIGRlY2lkaXIgY3XDoW50byB0aWVtcG8gZXN0YXJlbW9zIGNvbmVjdGFkb3M6CgpgYGB7ciBldmFsPUZBTFNFfQpmaWx0ZXJTdHJlYW0oZmlsZT0idHdlZXRzX3RydW1wLmpzb24iLAogICAgICAgICAgICAgdHJhY2s9InRydW1wIiwKICAgICAgICAgICAgIHRpbWVvdXQ9MTUsCiAgICAgICAgICAgICBvYXV0aD1hdXRoKQpgYGAKCkxhIGZ1bmNpw7NuIGd1YXJkYSB1biBhcmNoaXZvIGEgZGlzY28geSBhaG9yYSBwb2RlbW9zIGxlZXJsbyBkZSB2dWVsdGE6CgpgYGB7ciBldmFsPUZBTFNFfQp0dyA8LSByZWFkTGluZXMoInR3ZWV0c190cnVtcC5qc29uIikKdHdbWzFdXQpgYGAKCkNvbW8gdmVtb3MsIHNlIHRyYXRhIGRlIG9iamV0b3MgSlNPTiBpZ3VhbGVzIHF1ZSBsb3MgaGUgaGVtb3MgdXNhZG8gY3VhbmRvCmFwcmVuZMOtYW1vcyBhIGludGVyYWNjaW9uYXIgY29uIHVuYSBSRVNUIEFQSS4gUG9kZW1vcyBhaG9yYSBjb252ZXJ0aXIgZXN0YXMKY2FkZW5hcyBhIHVuYSBgbGlzdGAgaWd1YWwgcXVlIGFudGVzOgoKYGBge3IgZXZhbD1GQUxTRX0KZnJvbUpTT04odHdbWzFdXSkKYGBgCgp5IHBvZHLDrWFtb3MgY29udmVydGlyIHRvZG9zIGxvcyBkYXRvcyBxdWUgaGVtb3MgcmVjb2dpZG8KCmBgYHtyIGV2YWw9RkFMU0V9Cmpzb250dyA8LSBsYXBwbHkodHcsIGZyb21KU09OKQpgYGAKClNpbiBlbWJhcmdvLCBlbCBwYXF1ZXRlIHByb3ZlZSBkZSB1bmEgZnVuY2nDs24gcGFyYSBzaW1wbGlmaWNhciBlc3RhIHRhcmVhCgpgYGB7ciBldmFsPUZBTFNFfQpoZWFkKHBhcnNlVHdlZXRzKCJ0d2VldHNfdHJ1bXAuanNvbiIpKQpgYGAKClRhbWJpw6luIHBvZGVtb3Mgc2VsZWNjaW9uYXIgY3XDoW50b3MgdHdlZXRzIHF1ZXJlbW9zIGVuIHRvdGFsOgpgYGB7ciBldmFsPUZBTFNFfQpmaWx0ZXJTdHJlYW0oZmlsZS5uYW1lPSJ0d2VldHNfdHJ1bXAuanNvbiIsCiAgICAgICAgICAgICB0cmFjaz0idHJ1bXAiLAogICAgICAgICAgICAgdHdlZXRzPTEwLAogICAgICAgICAgICAgb2F1dGg9YXV0aCkKYGBgCgpvIGluY2x1c28gZGVmaW5pciB1biDDoXJlYSBnZW9ncsOhZmljYSBwYXJhIHR1aXRzIGdlb2xvY2FsaXphZG9zCgpgYGB7ciBldmFsPUZBTFNFfSAKZmlsdGVyU3RyZWFtKGZpbGUubmFtZT0iIiwKICAgICAgICAgICAgIGxhbmd1YWdlPSJlcyIsCiAgICAgICAgICAgICBsb2NhdGlvbnM9YygtNzQsNDAsLTczLDQxKSwKICAgICAgICAgICAgIHRpbWVvdXQ9NjAsCiAgICAgICAgICAgICBvYXV0aD1hdXRoKQpgYGAKCk90cmEgb3BjacOzbiBxdWUgbm9zIG9mcmVjZSBgc3RyZWFtUmAgZXMgdW5hIGNvbmV4acOzbiBhIHVuYSBtdWVzdHJhIGFsZWF0b3JpYSBkZQp0b2RvcyBsb3MgdHdlZXRzIHF1ZSBzZSBwcm9kdWNlbi4gCgpgYGB7ciBldmFsPUZBTFNFfQpzYW1wbGVTdHJlYW0oInR3ZWV0c1NhbXBsZS5qc29uIiwgdGltZW91dD02MCwgb2F1dGg9YXV0aCwgdmVyYm9zZT1GQUxTRSkKdHdlZXRzLmRmIDwtIHBhcnNlVHdlZXRzKCJ0d2VldHNTYW1wbGUuanNvbiIsIHZlcmJvc2U9RkFMU0UpCmhlYWQodHdlZXRzLmRmJGZyaWVuZHNfY291bnQpCmBgYAo=