tryCatch и withTimeout в цикле

#r #rest #error-handling

#r #остальное #обработка ошибок

Вопрос:

Я пытаюсь подсчитать количество слов в текстах исполнителя, взятых из lyrics.ovg. Я написал цикл для перебора песен. Однако, если песня не найдена, я получаю сообщение об ошибке. Я хотел бы обработать ошибку, чтобы продолжить работу со списком песен через короткий промежуток времени. Я пробовал «tryCatch» в сочетании с «withTimeout», но безуспешно. Любая помощь для этого новичка будет оценена по достоинству.

 library(musicbrainz)

song_title <- c("Not A Song", "’39", "(You’re So Square) Baby I Don’t Care")
no_songs <- length(song_title)
root_url <- "https://api.lyrics.ovh/v1/"

## Initialise dataframe
df_songs <- data.frame (title = character(),
                        lyrics = character(),
                        no_words = integer())

while (no_songs > 0) 
{
  print(song_title[no_songs])
  
  my_url <- paste0(root_url,"Queen","/",song_title[no_songs])
  my_raw_result <- httr::GET(my_url)
  my_content <- httr::content(my_raw_result, as = 'text')
  #Convert into a more usable r object
  my_content_from_json <- jsonlite::fromJSON(my_content)
  lyrics <- gsub("[rn]", " ", my_content_from_json$lyrics)
  lyrics <- str_squish(lyrics)
  word_count <- sapply(strsplit(lyrics, " "), length)
  
  df_songs <- rbind(df_songs, data.frame(song_title[no_songs], lyrics, word_count))
  no_songs <- no_songs -1
}
 

Пример работает для первых двух песен, но терпит неудачу в последней («не песня»). Я пытался написать функцию с tryCatch следующим образом, но это не работает.

 SongLyric <- function(x){
  tryCatch(
    expr = {
      withTimeout(my_raw_result <- httr::GET(x),timeout= 3)
      my_raw_result$status_code
      my_content <- httr::content(my_raw_result, as = 'text')
      #Convert into a more usable r object
      my_content_from_json <- jsonlite::fromJSON(my_content)
      lyrics0 <- gsub("[rn]", " ", my_content_from_json$lyrics)
      lyrics0 <- str_squish(lyrics0)
      return(lyrics0)
      
      message("Successfully found song")
    },
    error = function(e){
      message('Song not found')
      lyrics0 = 404
      return(lyrics0)
    } 
  )    
}
 

Ответ №1:

Мы можем использовать withTimeout from R.utils package, который прекращает попытки через несколько секунд и возвращает фрейм данных со NA значениями.

Я также изменил while цикл map_df , который должен быть эффективным.

 library(musicbrainz)
library(R.utils)
library(stringr)

song_title <- c("Not A Song", "’39", "(You’re So Square) Baby I Don’t Care")
root_url <- "https://api.lyrics.ovh/v1/"
all_urls <- paste0(root_url,"Queen/",song_title)

cbind(song_title, purrr::map_df(all_urls, function(my_url) {
  my_content_from_json <- withTimeout(jsonlite::fromJSON(my_url)$lyrics, timeout = 5, onTimeout = "silent")
  if(is.null(my_content_from_json)) return(data.frame(lyrics = NA, word_count = NA))
  lyrics <- gsub("[rn]", " ", my_content_from_json)
  lyrics <- str_squish(lyrics)
  word_count <- str_count(lyrics, '\s ')   1
  data.frame(lyrics, word_count)
})) -> result

result