Instalando as bibliotecas necessárias.
library(spotifyr) # Allows for extracting metadata for songs
library(dplyr) # DF manipulation
library(readr) # Fil reading
library(stringr) # For string manpulation
library(ggplot2) # Plotting
library(ggdark) # Plotting themes
Declarando a chave do API do Spotify como variável do ambiente. Acesse a documentação para mais detalhes.
Sys.setenv(SPOTIFY_CLIENT_ID = 'YOUR KEY')
Sys.setenv(SPOTIFY_CLIENT_SECRET = 'YOUR CLIENT SECRET')
Boa parte desta seção foi baseada no código de Nathaniel Lao.
Declarando as datas desejadas, neste caso, o ano de 2019; e o país desejado, Brasil.
# Get start and end date
start_date <- as.Date("2019/01/01")
end_date <- as.Date("2019/12/31")
# Target URL prefix and suffix
url_prefix <- 'https://spotifycharts.com/regional/br/daily/'
url_suffix <- '/download'
# List of Column Names that should exist in the dataframe
col_names = c("Position","`Track Name`","Artist","Streams","URL")
Cria a função para extrair dados de Spotify Charts e formar em um dataframe.
# This pulls data from a certain date
pull_top200 <- function(date) {
# Create the URL target
url <- str_c(url_prefix,format.Date(date),url_suffix)
# Pull data from Spotify
top200_df <- read_csv(url, skip=1)
# There are sometimes file that do not exist, this handles the exception
#ifelse(all(colnames(top200_df) %in% col_names),
top200_df <- top200_df %>% mutate(Date = date) # Create a date attribute
#top200_df <- data.frame()) # Return an empty data frame if invalid
top200_df
}
Loop que chama a função acima com as datas definidas como argumentos e forma o dataframe.
date <- start_date # set data counter
top200_df <- data.frame() # initialize empty df
# Iterate every date from start to end
# And get top 200 songs
while (date <= end_date) {
print(str_c("Processing ", date))
# Concat the current data frame with new data
top200_df <- top200_df %>%
rbind(pull_top200(date))
date <- date + 1
}
# Rename columns for consistency
top200_df <- rename(top200_df, position = Position,
track_name = `Track Name`,
artist_name = Artist,
streams = Streams,
url = URL,
date = Date)
Cria uma coluna contendo o uri (variável usada para conseguir dados sobre as músicas) a partir da URL.
# Extracts uri from URL
top200_df$uri <- lapply(top200_df[,'url'], function(x) str_sub(as.character(x), -22, -1))
top200_df = data.frame(lapply(top200_df, as.character), stringsAsFactors=FALSE)
# Checkpoint
write.csv(top200_df, 'top200_brazil.csv', row.names=FALSE)
Cria um dataframe com as músicas únicas que estiveram no Top 200 em qualquer dia de 2019. Este passo é necessário pois o dataframe original possui 73.000 linhas, o que excederia o limite do API e bloquearia a conexão.
# Creates a "simple" df with trackname and URI for scraping
simple <- top200_df[,c('track_name','uri')]
# Convert from factor to character
simple$track_name <- as.character(simple$track_name)
simple$uri <- as.character(simple$uri)
# Gets unique rows for later scraping
simple_unique <- unique(simple)
# Test the function and scrapes the first row
# Besides testing the API, this is also important to store the labels as "names"
test <- get_track_audio_features(simple_unique[1, 2])
values <- unlist(test, use.names=FALSE)
# Store the labels
names <- colnames(test)
names
Finalmente, usa o API do Spotify para conseguir informações das músicas e depois cruza os dados com o dataframe original.
for (i in 1:1095){
simple_unique[i,names] <- get_track_audio_features(simple_unique[i,2])
print(i)
}
# Checkpoint for songs with features scraped
write.csv(simple_unique, 'tracks_analysis.csv', row.names=FALSE)
# Final df with all tracks, days and features
df <- merge(x=top200_df, y=tracks_analysis, by.x = 'uri', by.y='id')
df$track_name.y <- NULL
Realiza uma rápida engenharia de recursos para converter o tom e o modo das músicas de número para nome. Ex: 0 - > C (dó). 0 -> minor. Salva o dataframe final.
# Creating a key_name column
index <- c(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)
values <- c("C", "C#", "D", "D#", "E", "F", 'F#', 'G', 'G#', 'A', 'A#', 'B')
df$key_name<- values[match(df$key, index)]
# Creating a mode_name column
index_mode <- c(0, 1)
values_mode <- c("minor", "major")
df$mode_name <- values_mode[match(df$mode, index_mode)]
# Creating a key + mode column
df$key_mode <- paste(df$key_name, df$mode_name)
# Final Checkpoint
write.csv(df, 'df_final.csv', row.names=FALSE)