Ускорьте вызовы API в R

Я запрашиваю Freebase, чтобы получить информацию о жанрах для примерно 10000 фильмов.

После прочтения Как оптимизировать парсинг с помощью getURL() в R , я пробовал выполнять запросы параллельно. Однако у меня не получилось — см. ниже. Помимо распараллеливания, я также читал, что httr может быть лучшей альтернативой RCurl.

Мои вопросы: возможно ли ускорить вызовы API, используя а) параллельную версию цикла ниже (с использованием машины WINDOWS)? б) альтернативы getURL, такие как GET в пакете httr?

library(RCurl)
library(jsonlite)
library(foreach)
library(doSNOW)

df <- data.frame(film=c("Terminator", "Die Hard", "Philadelphia", "A Perfect World", "The Parade", "ParaNorman", "Passengers", "Pink Cadillac", "Pleasantville", "Police Academy", "The Polar Express", "Platoon"), genre=NA)

f_query_freebase <- function(film.title){

  request <- paste0("https://www.googleapis.com/freebase/v1/search?",
                    "filter=", paste0("(all alias{full}:", "\"", film.title, "\"", " type:\"/film/film\")"),
                    "&indent=TRUE",
                    "&limit=1",
                    "&output=(/film/film/genre)")

  temp <- getURL(URLencode(request), ssl.verifypeer = FALSE)
  data <- fromJSON(temp, simplifyVector=FALSE)
  genre <- paste(sapply(data$result[[1]]$output$`/film/film/genre`[[1]], function(x){as.character(x$name)}), collapse=" | ")
  return(genre)
}


# Non-parallel version
# ----------------------------------

for (i in df$film){
  df$genre[which(df$film==i)] <- f_query_freebase(i)      
}


# Parallel version - Does not work
# ----------------------------------

# Set up parallel computing
cl<-makeCluster(2) 
registerDoSNOW(cl)

foreach(i=df$film) %dopar% {
  df$genre[which(df$film==i)] <- f_query_freebase(i)     
}

stopCluster(cl)

# --> I get the following error:  "Error in { : task 1 failed", further saying that it cannot find the function "getURL". 

person majom    schedule 10.04.2014    source источник
comment
Многоядерность вряд ли ускорит веб-запросы. Прочитайте stackoverflow.com/questions/22940150/ fast-url-query-with-r/ для использования конвейерной обработки соединений. Но знайте, что вы долбите чужой сервер, так что будьте вежливы.   -  person hadley    schedule 10.04.2014
comment
Чтобы заставить работать версию foreach, вам нужно добавить параметр .packages=c("RCurl", "jsonlite") в foreach, чтобы эти пакеты загружались рабочими процессами.   -  person Steve Weston    schedule 11.04.2014


Ответы (1)


Это не обеспечивает параллельных запросов внутри одного сеанса R, однако я использовал это для достижения ›1 одновременных запросов (например, параллельно) между несколькими сеансами R. , так что это может быть полезно.

На высоком уровне

Вы захотите разбить процесс на несколько частей:

  1. Получите список вызовов URL/API, которые вам нужно сделать, и сохраните их в виде CSV/текстового файла.
  2. Используйте приведенный ниже код в качестве шаблона для запуска нескольких процессов R и распределения работы между ними.

Примечание: это случилось с Windows, поэтому я использовал powershell. На Mac это можно было бы написать на bash.

Powershell/bash-скрипт

Используйте один скрипт powershell для запуска нескольких экземпляров процессов R (здесь мы разделяем работу между 3 процессами):

например сохраните обычный текстовый файл с расширением .ps1, вы можете дважды щелкнуть по нему, чтобы запустить его, или запланировать его с помощью планировщика задач/cron:

start powershell { cd C:\Users\Administrator\Desktop; Rscript extract.R 1; TIMEOUT 20000 }
start powershell { cd C:\Users\Administrator\Desktop; Rscript extract.R 2; TIMEOUT 20000 }
start powershell { cd C:\Users\Administrator\Desktop; Rscript extract.R 3; TIMEOUT 20000 }

Что он делает? Так и будет:

  • Перейдите на рабочий стол, запустите найденный скрипт с именем extract.R и укажите аргумент для сценария R (1, 2 и 3).

R-процессы

Каждый процесс R может выглядеть так

# Get command line argument 
arguments <- commandArgs(trailingOnly = TRUE)
process_number <- as.numeric(arguments[1])

api_calls <- read.csv("api_calls.csv")

# work out which API calls each R script should make (e.g. 
indicies <- seq(process_number, nrow(api_calls), 3)

api_calls_for_this_process_only <- api_calls[indicies, ] # this subsets for 1/3 of the API calls
# (the other two processes will take care of the remaining calls)

# Now, make API calls as usual using rvest/jsonlite or whatever you use for that
person stevec    schedule 19.02.2021