Explora tu actividad en YouTube con R: Análisis y visualización de datos de tu historial

Descubre cómo y cuánto consumes de YouTube, usando una copia de tus datos personales

Screenshot — Plot de gustos basados en categorías y comportamiento con el tiempo

Aceptémoslo, amigo lector, uno de los sitios web que visitamos frecuentemente y se volvió parte de nuestra vida, ya sea por entretenimiento o buscando respuestas, y en el que podríamos pasar horas enteras, es YouTube. Del mismo modo que con cualquier otra cosa a la que dedicamos tanto tiempo, me parece curioso ver exactamente cómo han cambiado nuestros hábitos de consumo. Con Google Takeout y la API de YouTube, analizar tu historial personal de YouTube es sencillo.

¿Cómo puedo obtener una copia de mis datos?

Tu historial de YouTube está disponible a través de Google Takeout, herramienta que brinda Google para consultar prácticamente los historiales y datos almacenados de cualquiera de sus productos que hayas hecho uso. Tienes que ingresar a la url https://takeout.google.com/settings/takeout y loguearte con tu cuenta personal.

Screenshot: Sitio web Google Takeout

Ahí encontrarás la posibilidad de solicitar una copia completa, o por producto, o incluso seleccionar sólo algunas características de un producto. A nosotros sólo nos interesa por ahora YouTube. Incluso si tienes varias cuentas vinculadas, puedes alternar entre ellas, dando clic en la imagen de perfil de la parte superior derecha. Si has subido videos te recomiendo que te asegures de desmarcar los videos del archivo que generará, ya que esto aumentará considerablemente su peso y el tiempo de entrega.

Screenshot: Generando copia de datos de cuenta YouTube

Una vez que el archivo es generado, estarás recibiendo un correo de notificación, desde donde podrás proceder a descargarlo. Por seguridad, el archivo tiene una caducidad, así que tienes que hacerlo antes de la fecha señalada en el correo.

Screenshot: Correo de notificación de descarga de copia de datos desde Google Takeout

Al descargar el archivo obtendrás un .zip que al ser descomprimido creará una estructura de carpetas y archivos, dependiendo lo que en un principio hayas solicitado a Google Takeout.

Screenshot: Estructura de carpetas del .zip descomprimido

Actualmente el archivo generado que corresponde a tu historial de búsquedas y reproducciones en YouTube, solo es exportado como HTML. Puedes usar el package “rvest” para analizar los HTML generados. Si, no me juzguen, “el canaca” y “dios eolo” siempre serán clásicos (memes virales en México).

Screenshot: Historial de búsquedas en YouTube

Trabajando los datos exportados

Ahora puedes proceder a crear un script en R. Primero hay que incluir todos los packages necesarios, y establecer la lectura del historial de búsqueda, contenido en el archivo “search-history.html” que se encuentra dentro de la carpeta “history”.

# PACKAGES REQUERIDOS
library(stringr)
library(rvest)
library(tidyverse)
library(jsonlite)
library(tidytext)
library(lubridate)
library(wordcloud)
library(httr)
library(ggplot2)
library(wordcloud2)
library(RCurl)
library(curl)
library(pbapply)
library(ggthemes)
# LECTURA DEL HISTORIAL DE BÚSQUEDAS
youtubeSearchHistory <- read_html("Takeout/YouTube and YouTube Music/history/search-history.html")

Para analizar texto en HTML usando “rvest” puedes especificar clases CSS que correspondan a una sección en particular. Por ejemplo, la línea .header-cell + .content-cell> a busca el hipervínculo en el contenido, correspondiente a las búsquedas que realizaste.

# SCRAPING DE BÚSQUEDAS
youtubeSearch <- youtubeSearchHistory %>%
html_nodes(".header-cell + .content-cell > a") %>%
html_text()
Screenshot: Inspector de elementos del HTML del historial de búsquedas en YouTube

Y claro, puedes obtener la información que necesites, por ejemplo el timestamp del mismo modo.

# SCRAPING DE TIMESTAMP
youtubeSearchContent <- youtubeSearchHistory %>%
html_nodes(".header-cell + .content-cell")
youtubeSearchTimeStr <- str_match(youtubeSearchContent, "<br>(.*?)</div>")[,2]
youtubeSearchTime <- mdy_hms(youtubeSearchTimeStr)

Ahora que tienes los datos de búsqueda y timestamp, puedes crear un Data Frame con ellos.

# CREANDO DATA FRAME BÚSQUEDAS + TIMESTAMP
youtubeSearchDataFrame <- data.frame(search = youtubeSearch,
time = youtubeSearchTime,
stringsAsFactors = FALSE)

Historial de reproducciones

El historial de reproducciones lo encontrarás en otro HTML con el nombre de “watch-history.html” también contenido dentro de la carpeta “history”. Primero debes realizar la lectura del archivo y obtener del mismo modo que anteriormente por web scraping, cada entrada. Tendrás que analizar información con expresiones regulares.

# LECTURA DEL HISTORIAL DE REPRODUCCIONES 
watchHistory <- read_html("Takeout/YouTube and YouTube Music/history/watch-history.html")
watchedVideoContent <- watchHistory %>%
html_nodes(".header-cell + .content-cell")
# POSIBLES TIME CHARACTERS
watchVideoTimes <- str_match(watchedVideoContent, "<br>([A-Z].*)</div>")[,2]
# POSIBLES ID VALUES
watchedVideoIDs <- str_match(watchedVideoContent, "watch\\?v=([a-zA-Z0-9-_]*)")[,2]
# TÍTULO VIDEO
watchedVideoTitles <- str_match(watchedVideoContent, "watch\\?v=[a-zA-Z0-9-_]*\">(.*?)</a>")[,2]

Ahora puedes unir todo nuevamente en un Data Frame.

# DATA FRAME HISTORIAL DE REPRODUCCIONES
watchedVideosDataFrame <- data.frame(id = watchedVideoIDs,
scrapedTitle = watchedVideoTitles,
scrapedTime = watchVideoTimes,
stringsAsFactors = FALSE)
watchedVideosDataFrame$time <- mdy_hms(watchedVideosDataFrame$scrapedTime)

Obtener más Datos sobre los videos con el API de YouTube

Estoy casi seguro que dirás hasta este punto… Bueno Saúl, no es la gran cosa, ¡qué más!. Esto se pone más interesante cuando se integra con la API de YouTube para obtener más datos. En lugar de ver sólo datos básicos como títulos y el timestamps, puedes obtener mayor información. De esta manera, podrás ver la popularidad, las descripciones, las categorías y más acerca de tus reproducciones de video.

Si es tu primera vez obteniendo credenciales para hacer uso del API de Youtube, sigue los sencillos pasos que la documentación oficial de YouTube pone a tu disposición: https://developers.google.com/youtube/v3/getting-started.

Una vez que establezcas un proyecto de Google, deberás generar un API Key y habilitar YouTube Data API v3, para crear tus credenciales en el menú de la barra lateral.

Screenshot: Google API Console, estableciendo el API de YouTube
Screenshot: Google API Console, generando un nuevo API Key

Hecho esto, puedes hacer ahora uso de tu API Key asignándola a una nueva variable. También debes asignar a una nueva variable la conexión con la API de youtube.

# ESTABLECER API KEY Y CONEXIÓN
youtubeAPIKey <- "AQUI_INGRESA_TU_API_KEY"
connectionURL <- 'https://www.googleapis.com/youtube/v3/videos'

Puedes hacer una prueba, por ejemplo, tomaré un video de mi canal en YouTube con ID “SG2pDkdu5kE”, para darte una visión general.

# PROBANDO QUERY RESPONSE
videoID <- "SG2pDkdu5kE"
queryParams <- list()
queryResponse <- GET(connectionURL,
query = list(
key = youtubeAPIKey,
id = videoID,
fields = "items(id,snippet(channelId,title,categoryId))",
part = "snippet"
))
parsedData <- content(queryResponse, "parsed")
str(parsedData)

Encontrás que dos importantes parámetros son “fields” y “parts”. Debes tener cuidado en tus consultas, porque podrías exceder la cuota de solicitudes o la respuesta a la solicitud puede volverse muy lenta. Puedes encontrar más información sobre estos parámetros en la documentación oficial: https://developers.google.com/youtube/v3/docs/videos

Screenshot: Probando Query Response

Obtén la categoría de video: Preparando solicitudes

Para obtener más metadatos como la categoría de video, puedes usar los IDs de video que obtuviste de tu archivo HTML y realizar solicitudes a YouTube para obtener más información sobre cada video. Estás a punto de realizar varios miles de solicitudes a la API de YouTube, así que tienes que hacer una preparación adicional de las solicitudes.

La librería más popular para realizar solicitudes web es “httr” (solo admite una solicitud a la vez). También existen “RCurl” y “curl”. Para asegurarte de obtener los datos lo más rápido posible, prueba los tres, la velocidad puede diferir según las consultas establecidas.

# REQUESTS OPTIONS
testConnection <- "https://www.google.com"
testCount <- 100
# HTTR TEST
systemTime(for(i in 1:testCount){
result <- GET(testConnection)
})
# RCURL TEST
uris = rep(testConnection, testCount)
systemTime(txt <- getURIAsynchronous(uris))
# CURL TEST
pool <- new_pool()
for(i in 1:testCount){curl_fetch_multi(testConnection)}
systemTime(out <- multi_run(pool = pool))

Por lo menos en mi caso, fue mucho más rápida la velocidad con “curl”, a diferencia de las otras dos opciones.

Obtén la categoría de video: Dando formato a las solicitudes

Necesitarás crear un string de conexión para cada solicitud y eliminar los duplicados para reducir el número de solicitudes hechas. También necesitarás una función para analizar los datos de la respuesta a la solicitud.

# CREAR SOLICITUDES Y ELIMINAR DUPLICADOS
createRequest <- function(id){
paste0(connectionURL,
"?key=",youtubeAPIKey,
"&id=",id,
"&fields=","items(id,snippet(channelId,title,description,categoryId))",
"&part=","snippet")
}
uniqueWatchedVideoIDs <- unique(watchedVideosDataFrame$id)
requests <- pblapply(uniqueWatchedVideoIDs, createRequest )
# RESPUESTA DE SOLICITUD
getMetadataDataFrame <- function(response){
rawchar <- rawToChar(response$content)
parsedData <- fromJSON(rawchar)
data.frame <- cbind(id = parsedData$items$id, parsedData$items$snippet)
return(data.frame)
}

Puedes configurar qué hacer en caso de que la solicitud sea exitosa o en caso de que haya fallado.

videoMetadataDataFrame <- data.frame(id = c(), 
channelId = c(),
title = c(),
description = c(),
categoryId = c()
)
# SOLICITUD EXITOSA
addToMetadataDataFrame <- function(response){
.GlobalEnv$videoMetadataDataFrame <- rbind(.GlobalEnv$videoMetadataDataFrame,getMetadataDataFrame(response))
}
# SOLICITUD FALLIDA
failFunction <- function(request){
print("algo falló")
}

Un método, aunque más lento pero más confiable para obtener tus datos, es tomarlos de la memoria y configurar nuestra solicitud múltiple, para extraer posteriormente de la memoria.

# CAPTURA DE SOLICITUDES Y USO DE MEMORIA
fetchMetadataFromMemory <- function(request){
return(getMetadataDataFrame(curl_fetch_memory(request)))
}
system.time(out <- multi_run(pool = pool))
saveRDS(videoMetadataDataFrame, file = "videoMetadataDataframeAsync.rds")
length(requests)
nrow(videoMetadataDataFrame)
listMetadata <- pblapply(requests, fetchMetadataFromMemory)

Una vez hecho esto, dependiendo la velocidad de tu conexión a internet, y el tamaño de tu Data Frame, puedes probablemente preparar un café, un té o abrir una cerveza, en lo que el indicador llegue al 100% .

Screenshot: Avance en consola de listMetadata

Usarás también la función “bind_rows” para combinar la lista en un Data Frame ordenado.

# COMBINAR LISTA EN UN DATA FRAME
videoMetadataDataFrame <- bind_rows(listMetadata)
saveRDS(videoMetadataDataFrame, file = "video_metadata_dataframe_memory1.rds")

Obtén la categoría de video: Dando formato a las categorías

Cada categoría posee un ID único. Necesitas realizar otra solicitud para obtenerlos y añadir los datos a una nueva columna.

# CATEGORY ID REQUEST
categoryListURL <- "https://www.googleapis.com/youtube/v3/videoCategories"
categoryResponse <- GET(url = categoryListURL,
query = list(
key = youtubeAPIKey,
regionCode = "us",
part = "snippet"
))
parsedCategoryResponse <- content(categoryResponse, "parsed")
categoryDataFrame <- data.frame(categoryId=c(), category=c())
for(item in parsedCategoryResponse$items){
categoryDataFrame <<-rbind(categoryDataFrame,
data.frame(categoryId = item$id, category=item$snippet$title))
}
categoryDataFrame
videoMetadata <- merge(x = videoMetadataDataFrame, y = categoryDataFrame, by = "categoryId")
head(videoMetadata)

Puedes combinar tu nuevo Data Frame con tu historial de reproducciones para obtener los metadatos de video junto con el momento en que fue reproducido.

watchedVideos <- merge(watchedVideosDataFrame , videoMetadata, by="id")
str(watchedVideos)

¿Han cambiado tus gustos de lo que miras en YouTube con el tiempo?

Con tu historial de búsqueda, tu historial de reproducciones y los metadatos obtenidos sobre las categorías, ahora puedes responder a esta pregunta. Puedes visualizar qué categorías de videos son las que más reproduces y cómo esto ha venido cambiando con el tiempo.

# VISUALIZAR CATEGORÍAS VISTAS
watchedVideos %>%
group_by(category) %>%
summarise(count = n()) %>%
arrange(desc(count))
watchedVideos %>%
ggplot(aes(x = time, fill = category)) +
labs(x= "Año", y= "Count") +
ggtitle("¿Han cambiado tus gustos con el tiempo?", "Visualización de categorías mayormente reproducidas")+
geom_area(stat = "bin") +
theme_economist_white()

Por ejemplo en mi caso, algo que seguramente al igual que muchos de ustedes es muy presente, son videos en categoría de música. Y claro, tiene sentido la presencia de la categoría de Film & Animation, sigo muchos canales para enterarme de próximas películas, trailers y reseñas.

Análisis y visualización de datos de tu historial en YouTube — Plot de gustos basados en categorías y su comportamiento con el tiempo

¿En qué hora del día gastas más tiempo mirando videos en YouTube?

Sería interesante visualizar los horarios en que tienes mayor actividad regalándole tu valioso tiempo a YouTube. Puedes mirar esto creando una función personalizada que te permita visualizarlo a manera de reloj.

# PLOT DE RELOJ PARA VISUALIZAR REPRODUCCIONES POR HORA DEL DÍA
clockPlot <- function (x, col = heat.colors(n), ...) {
if( min(x)<0 ) x <- x - min(x)
if( max(x)>1 ) x <- x/max(x)
n <- length(x)
if(is.null(names(x))) names(x) <- 0:(n-1)
m <- 1.05
plot(0, type = 'n', xlim = c(-m,m), ylim = c(-m,m),
axes = F, xlab = '', ylab = '', ...)
fig <- pi/2 - 2*pi/200*0:200
polygon( cos(fig), sin(fig) )
f2 <- .02
fig <- pi/2 - 2*pi/n*0:n
segments( (1+f2)*cos(fig), (1+f2)*sin(fig), (1-f2)*cos(fig), (1-f2)*sin(fig) )
segments( cos(fig), sin(fig),0, 0, col = 'light grey', lty = 3)
f1 <- -2*pi/n*(0:50)/50
for (i in 1:n) {
fig <- pi/2 - 2*pi/n*(i-1)
b <- pi/2 - 2*pi/n*i
polygon( c(0, x[i]*cos(fig+f1), 0), c(0, x[i]*sin(fig+f1), 0), col=col[i] )
f2 <- .1
text((1+f2)*cos(fig), (1+f2)*sin(fig), names(x)[i])
}
}
clockDataFrame <- watchedVideos %>%
mutate(hour = hour(time)) %>%
group_by(hour) %>%
summarise(count = n()) %>%
arrange(hour)
clockPlot(clockDataFrame$count, main = "What hours do you spend the most time watching YouTube?")

Levanten la mano cuántos más aquí, al igual que yo, para poder vencer el insomnio pasamos horas de madrugada viendo YouTube? En mi caso, es muy notorio que mayormente de 2 am a 3 am le regalo a la plataforma mi valioso tiempo.

Análisis y visualización de datos de tu historial en YouTube — Plot de horario con mayor actividad en YouTube

¿Cuáles son los vídeos que más has reproducido una y otra vez?

Otro interesante dato que puedes obtener es el top 10 de los videos que más has reproducido una y otra vez. Puedes visualizar esto con una sencilla tabla, con el package “kableExtra” y ordenarla en forma descendente respecto al número de reproducciones.

# TABLA DE VIDEOS MÁS REPRODUCIDOS TOP 10
w1 <- watchedVideos %>%
mutate(year = year(time)) %>%
group_by(year, title) %>%
summarise(count = n()) %>%
arrange(year, desc(count)) %>%
top_n(5)
mostReWatched <- knitr::kable(x = head(arrange(w1, desc(count)) %>%
select(year, title, ,count), 10),
col.names = c('Year', 'Video Title', 'Count'))
kable_styling(mostReWatched, "striped", position = "left", font_size = 12)

Obtendrás una tabla como la siguiente. En mi caso, me parece curioso ver que de 2016 a 2019 no aparece algún video en mi top 10. Supongo mucho tiene que ver la llegada de Spotify a mi vida, y que antes de esto usaba mayormente YouTube para reproducir música. Por cierto, si, soy fan de “Vicentico” y “Los Fabulosos Cadillacs” (ex vocalista de, y una banda de las bandas de rock/ska en español más icónicas de todos los tiempos).

Análisis y visualización de datos de tu historial en YouTube — Top 10 de videos más vistos una y otra vez

¿Cuáles han sido los términos que más has buscado en YouTube?

Puedes visualizar esto de una manera más atractiva con lel package “wordcloud”. Depende qué tan extenso sea tu historial, claro. Puedes establecer por ejemplo palabras con un mínimo de 25 veces repitiéndose.

# WORDCLOUD PALABRAS MÁS BUSCADAS
myWords <- youtubeSearchDataFrame %>%
unnest_tokens(word, search) %>%
anti_join(stop_words) %>%
count(word, sort = TRUE)
myWordcloud <- myWords %>%
group_by(word) %>%
summarize(count = sum(n)) %>%
anti_join(stop_words)
wordcloud(words = myWordcloud$word, freq = myWordcloud$count, min.freq = 25,
max.words = 100, random.order =FALSE, rot.per =.35,
colors=brewer.pal(9, "Set1"))

Obtendrás como resultado un plot como el siguiente, donde podrás descubrir la congruencia con tus gustos. Si te lo estás preguntando, la respuesta es sí, creo en el fenómeno OVNI.

Análisis y visualización de datos de tu historial en YouTube — Wordcloud palabras más buscadas

¿Cuáles han sido los términos más frecuentes en las descripciones de los videos que has visto?

Para responder a eta pregunta, puedes hacer lo mismo que en el ejemplo anterior, pero esta vez con las descripciones de los videos, aprovechando que poseemos esta información en nuestro Data Frame. Es muy común que por hacer publicidad muchos videos en sus descripciones incluyen URL’s, vamos a filtrar esto (o lo más que se pueda), además de palabras que no nos dan información relevante como artículos, pronombres, etc. Esta vez lo haremos con “wordcloud2” y palabras que se repitan por lo menos más de 250 veces.

# WORDCLOUD PALABRAS MÁS FRECUENTES EN DESCRIPCIONES DE VIDEO 
descriptionsWordcloud <- watchedVideos %>%
unnest_tokens(word, description) %>%
anti_join(stop_words) %>%
count(word, sort = TRUE) %>%
filter(! word %in% c("www.instagram.com", "gmail.com", "www.twitter.com", "youtu.be", "como", "instagram", "instagram.com", "tú", "watch", "aquí", "pero", "su", "http", "al","se","si","goo.gl","smarturl.it","facebook","video","más", "twitter", "te","lo","este","tu", "para", "por", "con", "es", "del", "las", "una", "mi", "de", "en", "la", "el", "los", "https", "bit.ly" , "â", "www.youtube.com")) %>%
filter(n > 250)
wordcloud2(descriptionsWordcloud)

Como podrás notar, muchas de las palabras hacen alusión a suscribirse a sus canales o seguir sus redes sociales, como era de esperarse.

Análisis y visualización de datos de tu historial en YouTube — Wordcloud palabras más presentes en las descripciones de video vistos

Gracias por tu amable lectura. Por aquí puedes encontrar el código completo: https://github.com/cosmoduende/r-youtube-personal-history-analysis

¡Te agradezco haber llegado hasta acá, te deseo que tengas felices análisis, que lo puedas poner en práctica y te sorprendas y diviertas tanto como yo con los resultados!

Otros artículos que he escrito

Si te ha gustado este post, agradeceré mucho tus claps. Además te invito a visitar también otros artículos que he escrito:

Aprendiz constante, amante de la tecnología. #G3ekArmy, Web Developer & Data Enthusiast. Coordinador académico & Instructor en KMMX. Organizador de #KotlinCDMX.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store