En este tutorial quiero mostrar cómo usar las herramientas de Google Chrome para obtener la información necesaria para extraer los datos de stats.nba.com usando el lenguaje R.
1 Algunas definiciones
2 Requisitos para conseguir los datos de la página
3 Extraer los datos y guardarlos
El método que explico funciona para stats.nba.com y funcionaría para muchas otras páginas, pero no funcionará para todas. Solo funcionará para las páginas basadas en javascript que llenan sus datos dinámicamente desde APIS externas.
Las siglas API corresponden al inglés application programming interface, que se traduce como interfaz de programación de aplicaciones. Esta fórmula se refiere al conjunto de estructuras que permiten que los componentes de un software se comuniquen con otros.
Fuente: https://keepcoding.io/blog/que-es-una-api-y-para-que-sirve/)
Una petición HTTP que retorna JSON. Dicho de otra forma, un endpoint es un extremo de un canal de comunicación. Cuando una API interactúa con otro sistema, los puntos de contacto de esta comunicación se consideran endpoints. Para las API, un punto final puede incluir una URL de un servidor o servicio. Cada endpoint es la ubicación desde la cual las API pueden acceder a los recursos que necesitan para llevar a cabo su función.
Las API funcionan mediante “solicitudes” y “respuestas”. Cuando una API solicita información de una aplicación web o un servidor web, recibirá una respuesta. El lugar donde las API envían solicitudes y donde vive el recurso se denomina endpoint.
Fuente: https://smartbear.com/learn/performance-monitoring/api-endpoints/
Este tutorial utiliza la última versión de Google Chrome para encontrar la información del endpoint. El código de este tutorial se escribió en R 4.1.3 usando las siguientes Librerias:
tidyverse
httr
jsonlite
En este tutorial descargaré las estadísticas de aclarados por partido de cada jugador de esta temporada en playoffs, que se puede encontrar en este enlace https://www.nba.com/stats/players/isolation/
El primer paso es abrir Google Chrome e ir a la página de la que queremos recopilar los datos.
Después botón derecho del ratón y clikamos inspeccionar
Se debería abrir un panel en el lado derecho de la pantalla o debajo, según esté configurado, como en el pantallazo. A continuación, hacemos clic en la pestaña de red para ver todas las llamadas API realizadas por la página.
En la pestaña Red, seleccionamos XHR y cargamos de nuevo la página.
Una vez que la página se haya cargado, deberemos buscar entre las diferentes llamadas para encontrar la que queremos. Normalmente se llamará algo similar al nombre de la página en la que se encuentra y estará seguido por un montón de parámetros de consulta.
Una vez que hayamos encontrado la API correcta (o si no estamos seguros), podemos hacer clic con el botón derecho en la llamada y copiar la respuesta.
Luego pegamos la respuesta en un visor json como http://jsonviewer.stack.hu/ para verla mejor.
Aquí no funciona el botón derecho, así que le damos paste o a ctrl+ v, después damos a la pestaña Viewer y vamos desplegando para ver el contenido
Yo uso este lector para saber lo que tengo que desplegar para sacar los datos, así que si ya estamos seguros de que tenemos la llamada de la API correcta, podemos copiar el link.
Creamos el código
Para eso lo primero que hay que hacer es cargar las librerías necesarias
library(tidyverse)
library(httr)
library(jsonlite)
library(janitor)
library(glue)
La librería httr es una Herramienta útil para trabajar con HTTP organizado por verbos HTTP (GET(), POST(), etc.), jsonlite es para reestructurar los datos del Json extraido, janitor para hacer los nombres de las columnas mas amigables y glue para ahorrarnos trabajo a la hora de escribir el csv
Ahora necesitamos crear un dataFrame con los encabezados de solicitud, pinchamos en Encabezados y los copiamos. Casi todo lo que vemos en el navegador se transmite a la computadora a través del protocolo HTTP . Por ejemplo, cuando abrimos la página de estadísticas de la NBA, el navegador envió muchas solicitudes HTTP ( Solicitud ) y recibió muchas respuestas ( Respuesta ). Para recibir la tabla tenemos que poner los encabezados con los que solicitar las estadísticas para que el servidor nos de una respuesta adecuada con la información que hemos pedido.
Fuente: https://appmaster.io/es/help/how-to/encabezados-de-solicitud-respuesta-en-appmaster
Los dos puntos los cambiamos por iguales y en la linea número 10 usamos comillas simples dentro de las comillas dobles
headers <- c( #les llamo headers porque es mas corto ;)
"Accept" = "application/json, text/plain, */*",
"Accept-Encoding" = "gzip, deflate, br",
"Accept-Language" = "es-ES,es;q=0.9,en;q=0.8",
"Connection" = "keep-alive",
"Host" = "stats.nba.com",
"If-Modified-Since"= "Tue, 28 Jun 2022 16:52:53 GMT",
"Origin" = "https://www.nba.com",
"Referer" = "https://www.nba.com/",
"sec-ch-ua" = "Not A;Brand';v='99', 'Chromium';v='103', 'Google Chrome';v='103'",
"sec-ch-ua-mobile" = "?0",
"sec-ch-ua-platform" = "macOS",
"Sec-Fetch-Dest" = "empty",
"Sec-Fetch-Mode" = "cors",
"Sec-Fetch-Site" = "same-site",
"User-Agent" = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36",
"x-nba-stats-origin"= "stats",
"x-nba-stats-token"= "true"
)
Seguimos. Pegamos el link y aplicamos las funciones httr:: o jsonlite:: si hemos cargado las librerías previamente no haría falta, pero lo pongo para que se sepa de donde vienen las funciones
link <- "https://stats.nba.com/stats/synergyplaytypes?LeagueID=00&PerMode=PerGame&PlayType=Isolation&PlayerOrTeam=P&SeasonType=Playoffs&SeasonYear=2021-22&TypeGrouping=offensive"
res <- httr::GET(url = link, add_headers(.headers = headers))
json_res <- jsonlite::fromJSON(content(res, "text"))
Si ponemos el símbolo de $ después de la variable json_res, veríamos lo siguiente
Elegimos el dataframe resultSets
Y aquí elegimos rowSets y hacemos un dataframe con los rows
df <- data.frame(json_res$resultSets$rowSet)
Obtenemos esto
Y ahora vamos con los encabezados
colnames(df) <- json_res$resultSets$headers[[1]]
ahora tendríamos la tabla completa pero vamos a pasar el clean_names
df <- janitor::clean_names(df)
Y este sería el código completo
library(tidyverse)
library(httr)
library(jsonlite)
library(janitor)
library(glue)
headers <- c( # les llamo headers porque es mas corto ;)
"Accept" = "application/json, text/plain, */*",
"Accept-Encoding" = "gzip, deflate, br",
"Accept-Language" = "es-ES,es;q=0.9,en;q=0.8",
"Connection" = "keep-alive",
"Host" = "stats.nba.com",
"If-Modified-Since" = "Tue, 28 Jun 2022 16:52:53 GMT",
"Origin" = "https://www.nba.com",
"Referer" = "https://www.nba.com/",
"sec-ch-ua" = "Not A;Brand';v='99', 'Chromium';v='103', 'Google Chrome';v='103'",
"sec-ch-ua-mobile" = "?0",
"sec-ch-ua-platform" = "macOS",
"Sec-Fetch-Dest" = "empty",
"Sec-Fetch-Mode" = "cors",
"Sec-Fetch-Site" = "same-site",
"User-Agent" = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36",
"x-nba-stats-origin" = "stats",
"x-nba-stats-token" = "true"
)
season <- "2021-22"
link <- paste0("https://stats.nba.com/stats/synergyplaytypes?LeagueID=00&PerMode=PerGame&PlayType=Isolation&PlayerOrTeam=P&SeasonType=Playoffs&SeasonYear=", season, "&TypeGrouping=offensive")
res <- httr::GET(url = link, add_headers(.headers = headers))
json_res <- jsonlite::fromJSON(content(res, "text"))
df <- data.frame(json_res$resultSets$rowSet)
colnames(df) <- json_res$resultSets$headers[[1]]
df <- janitor::clean_names(df)
write.csv(df, glue::glue("{season}.csv"))
Esto es un año solamente, y estas estadísticas están disponibles desde el 2015 pero se puede hacer una función que nos den estos 7 años y también podemos cambiar la palabra playoffs por Regular+Season y nos daría la temporada regular, pero bueno la función seria tal que así
library(tidyverse)
library(httr)
library(jsonlite)
library(janitor)
library(glue)
season <- c("2015-16",
"2016-17",
"2017-18",
"2018-19",
"2019-20",
"2020-21",
"2021-22")
dfseason <- function(season) {
headers <- c( # les llamo headers porque es mas corto ;)
"Accept" = "application/json, text/plain, */*",
"Accept-Encoding" = "gzip, deflate, br",
"Accept-Language" = "es-ES,es;q=0.9,en;q=0.8",
"Connection" = "keep-alive",
"Host" = "stats.nba.com",
"If-Modified-Since" = "Tue, 28 Jun 2022 16:52:53 GMT",
"Origin" = "https://www.nba.com",
"Referer" = "https://www.nba.com/",
"sec-ch-ua" = "Not A;Brand';v='99', 'Chromium';v='103', 'Google Chrome';v='103'",
"sec-ch-ua-mobile" = "?0",
"sec-ch-ua-platform" = "macOS",
"Sec-Fetch-Dest" = "empty",
"Sec-Fetch-Mode" = "cors",
"Sec-Fetch-Site" = "same-site",
"User-Agent" = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36",
"x-nba-stats-origin" = "stats",
"x-nba-stats-token" = "true"
)
link <- paste0("https://stats.nba.com/stats/synergyplaytypes?LeagueID=00&PerMode=PerGame&PlayType=Isolation&PlayerOrTeam=P&SeasonType=Playoffs&SeasonYear=", season, "&TypeGrouping=offensive") # LINK PARA PLAYOFFS
res <- httr::GET(url = link, add_headers(.headers = headers))
json_res <- jsonlite::fromJSON(content(res, "text"))
link1 <- paste0("https://stats.nba.com/stats/synergyplaytypes?LeagueID=00&PerMode=PerGame&PlayType=Isolation&PlayerOrTeam=P&SeasonType=Regular+Season&SeasonYear=", season, "&TypeGrouping=offensive") # LINK PARA REGULAR SEASON
res1 <- httr::GET(url = link1, add_headers(.headers = headers))
json_res1 <- jsonlite::fromJSON(content(res1, "text"))
# TABLA PLAYOFF
df <- data.frame(json_res$resultSets$rowSet)
colnames(df) <- json_res$resultSets$headers[[1]]
df <- janitor::clean_names(df)
df$year <- season
df$type <- "playoffs"
# TABLA REGULAR SEASON
df1 <- data.frame(json_res1$resultSets$rowSet)
colnames(df1) <- json_res1$resultSets$headers[[1]]
df1 <- janitor::clean_names(df1)
df1$year <- season
df1$type <- "regularSeason"
df2 <- rbind(df1, df) #juntamos las tablas
return(df2)
}
df_isolation <- map_df(season, dfseason) #mapeamos la funcion
write.csv(df_isolation, "isolation.csv")
Y este es el resultado
And that’s it si se quiere descargar directamente el csv aquí dejo el link
https://raw.githubusercontent.com/IvoVillanueva/isolation/main/isolation.csv