Как печатать результаты с перерывами при применении функции к большому набору данных в R?

#r #function #web-scraping #rvest #purrr

#r #функция #очистка веб-страниц #rvest #мурлыканье

Вопрос:

Я пытаюсь извлечь несколько разных деталей из большого количества URL-адресов (35000). Я определил несколько функций, используя рабочий процесс rvest, и я использую map для применения каждой функции к каждому URL, создавая tibble непосредственно из функций. Моя проблема в том, что из-за большого количества URL-адресов выполнение всего процесса занимает очень много времени, и я не могу придумать способ сохранения результатов по ходу работы (если только я не сделаю это циклом, что, на мой взгляд, еще медленнее).

Единственный способ, который я мог придумать, чтобы обойти это, — сопоставить фрагменты URL-адресов и соответствующим образом заполнить tibble. Но этот код действительно неэффективен и требует от меня ручного ввода большого количества чисел снова и снова.

 library(rvest); library(tidyverse)

#define function to scrape webdata
##i actually have multiple functions for each css tag i want, and create a tibble column for each one

get_web_info <- function(url) {
  read_html(url) %>%
  html_nodes("h3:nth-of-type(1)") %>%
  html_text()
}

#create tibble scraping the first 500 urls 
##only scrape the first 500 because otherwise there's no output until all 35000 urls are done, which takes more than a day

scraped <- tibble(
  web_info = map(url_vector[1:500], possibly(get_web_info, otherwise = NULL)),
  original_url = url_vector[1:500]
)

#fill in the next 500 rows of the tibble by scraping the next 500 urls
##i would have to copy and paste the code below, manually changing which rows i'm filling in and mapping 

scraped$web_info[500:1000] <- map(url_vector[500:1000], possibly(get_web_info, otherwise = NULL))
  

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

Сохранение результатов из сопоставления функций по большому набору данных должно быть распространенной проблемой, но я просто не могу найти ничего о том, как это решается.

Комментарии:

1. Дикое предположение состояло бы в том, что узким местом здесь является rvest (т. Е. http-вызов), а не R / your функция.

2. rvest, вероятно, замедляет работу, вы правы, но это не обязательно проблема, если бы я мог сохранять свои результаты по ходу дела. Я полагал, что, поскольку приведенный выше код работает с 500 URL-адресами, он должен работать на 35000, но я не могу оставить его запущенным на 3 дня без вывода.

3. Да, но тогда в чем вред экономии? Периодическое сохранение не замедлит ваш процесс (условно говоря). Если вы хотите, чтобы она работала быстрее, вам нужно открыть больше подключений.

4. Кстати: ты соскребаешь url[500] дважды. 🙂

5. Было некоторое обсуждение добавления индикаторов выполнения в map , но я не думаю, что это реализовано. В этом посте показан рабочий пример ее добавления с использованием progress пакета: github.com/tidyverse/purrr/issues/149#issuecomment-365270639

Ответ №1:

Было некоторое обсуждение добавления индикаторов выполнения в map, но я не думаю, что это реализовано. Однако в ветке проблем jtrecenti опубликовал некоторый код, который использует progress пакет для добавления индикатора выполнения в map . Приведенный ниже пример работает, но я не уверен, что он будет работать с вашим кодом:

 progressively <- function(.f, .n, ...) {
  pb <- progress::progress_bar$new(total = .n, ...)
  function(...) {
    pb$tick()
    .f(...)
  }
}

input <- 1:5
fun <- function(x) {
  Sys.sleep(.2)
  sample(x)
}
progress_fun <- progressively(fun, length(input))
purrr::map(input, progress_fun)
  

По мере выполнения отображается индикатор выполнения, а затем возвращается:

 [[1]]                                                                                               
[1] 1

[[2]]
[1] 1 2

[[3]]
[1] 3 1 2

[[4]]
[1] 2 1 3 4

[[5]]
[1] 5 1 4 3 2