#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