HTML-страницы не сохраняются в списке при использовании mclapply - PullRequest
0 голосов
/ 08 сентября 2018

При использовании просто lapply read_html страница результатов сохраняется.

library(xml2)  

lapply(c("https://www.analyticsvidhya.com/blog/2018/06/datahack-radio-1-machine-learning-competitions-with-kaggle-ceo-anthony-goldbloom/","https://www.analyticsvidhya.com/blog/2018/09/datahack-radio-lyft-dr-alok-gupta/"), function(x){read_html(x)})
#> [[1]]
#> {xml_document}
#> <html>
#> [1] <head lang="en-US" prefix="og: http://ogp.me/ns#">\n<meta http-equiv ...
#> [2] <body class="post-template-default single single-post postid-45087 s ...
#> 
#> [[2]]
#> {xml_document}
#> <html>
#> [1] <head lang="en-US" prefix="og: http://ogp.me/ns#">\n<meta http-equiv ...
#> [2] <body class="post-template-default single single-post postid-46725 s ...

При использовании Parallel mclapply:

library(xml2)
library(parallel)  

mclapply(c("https://www.analyticsvidhya.com/blog/2018/06/datahack-radio-1-machine-learning-competitions-with-kaggle-ceo-anthony-goldbloom/","https://www.analyticsvidhya.com/blog/2018/09/datahack-radio-lyft-dr-alok-gupta/"), function(x){read_html(x)}, mc.cores = 2)
#> [[1]]
#> {xml_document}
#> 
#> [[2]]
#> {xml_document}

Я не могу понять, почему это происходит, даже с foreach я не могу получить желаемые результаты как обычно. Помогите!

1 Ответ

0 голосов
/ 09 сентября 2018

Время шить

(я имею в виду, вы использовали слово нить , так что я не упускаю возможность провести каламбур или три).

Глубоко на странице справки для ?parallel::mclapply вы в конечном итоге увидите, что она работает:

  • процессы разветвления
  • результаты сериализации
  • в конечном итоге собирает эти сериализованные результаты и объединяет их в один объект

Вы можете прочитать ?serialize, чтобы увидеть используемый метод.

Почему мы не можем сериализовать xml_document / html_document объектов?

Во-первых, давайте сделаем один:

library(xml2)

(doc <- read_html("<p>hi there!</p>"))
## {xml_document}
## <html>
## [1] <body><p>hi there!</p></body>

и посмотрите на str лекцию:

str(doc)
## List of 2
##  $ node:<externalptr> 
##  $ doc :<externalptr> 
##  - attr(*, "class")= chr [1:2] "xml_document" "xml_node"

doc$node
## <pointer: 0x7ff45ab17ce0>

Хмм. Это <externalptr> объекты. Что ?"externalptr-class" (в конце концов) говорит о них?

…
"externalptr" # raw external pointers for use in C code

Поскольку это не встроенный объект, а данные скрыты и доступны только через интерфейс пакета, R не может самостоятельно сериализовать его, и нужна помощь . (Эта шестнадцатеричная строка & mdash; 0x7ff45ab17ce0 & mdash; является указателем памяти, где скрыты эти непрозрачные данные).

«Ты не можешь быть серьезным…»

Всего утра.

В случае, если вы из Миссури (состояние «Покажи мне»), мы можем увидеть, что происходит без сложности параллельных операций и необработанных обработок сериализации объектов соединения, просто попытавшись сохранить документ выше в файл RDS и прочитайте это назад:

tf <- tempfile(fileext = ".rds")
saveRDS(doc, tf)

(doc2 <- readRDS(tf))
## List of 2
##  $ node:<externalptr> 
##  $ doc :<externalptr> 
##  - attr(*, "class")= chr [1:2] "xml_document" "xml_node"

Теперь вы можете быть похожи на "Ага! Видите, это работает!" А-а-а ... вы бы ошиблись :

doc2$node
## <pointer: 0x0>

0x0 означает, что он ни на что не указывает. Вы потеряли все эти данные. Это ушло Навсегда. (Но это был хороший пробег, поэтому мы не должны расстраиваться из-за этого). Это обсуждалось xml2 devs и & mdash; а не облегчать нам жизнь & mdash; они наказали и сделали ?xml_serialize.

Подождите ... есть xml_serialize но это не так уж и полезно?

Да. И это становится даже лучше хуже.

Надеюсь, ваше любопытство было достаточно возбуждено, чтобы вы пошли дальше и узнали, что делает эта функция с серьезным названием xml_serialize(). Если нет, то это R, поэтому для определения просто введите его имя без (), чтобы получить:

function (object, connection, ...) 
{
    if (is.character(connection)) {
        connection <- file(connection, "w", raw = TRUE)
        on.exit(close(connection))
    }
    serialize(structure(as.character(object, ...), class = "xml_serialized_document"), 
        connection)
}

Помимо подключения некоторых битов соединения, комплексное волшебство , стоящее за этой xml_serialize функцией, ну, всего as.character(). (На самом деле, это своего рода разочарование.)

Поскольку параллельные операции выполняют (идиоматически) эквивалент saveRDS() => readRDS(), когда вы возвращаете xml_document, html_document (или их _node[s] братьев и сестер) в параллельном применении, вы в конечном итоге получаете целое куча ничего.

Что может сделать невинный скребок Content Thief , чтобы преодолеть это разрушительное ограничение?

У вас есть (как минимум) четыре варианта:

  • ? усложнить параллельное применение вашей функции для обработки документа XML / HTML в фрейм данных, вектор или список объектов, которые все могут быть автоматически сериализованы с помощью R, чтобы их можно было объединить для вас
  • быть крутым ? и иметь одно параллельное применение, которое сохраняет HTML в файлы (в любом случае, операции HTTP, скорее всего, медленный бит), а затем непараллельную операцию, которая последовательно читает их и обрабатывает их & mdash; что, похоже, ты все равно собираешься делать. Обратите внимание, что вы в некотором роде пиявка и очень плохой пользователь, если вы все равно не выполняете кеширование HTML в файл, поскольку показывает, что вам действительно не нужны пропускная способность и затраты на ЦП для содержимого, которое вы Губка Соскоб.
  • не будьте крутыми, выполнив ^^ ?, и вместо этого используйте as.character((read_html(…)), чтобы вернуть необработанный, сериализуемый символьный HTML непосредственно из вашего параллельного приложения, а затем повторно xml2 вернуть их в оставшуюся часть вашей программы
  • ? разветвитьxml2 ?, наложите корректный сериализационный хак и не беспокойтесь о PR'е, так как, скорее всего, вы потратите немало времени, пытаясь убедить их в том, что оно того стоит, и все равно не получится, так как эта "externalptr serialization` хитрое дело, чреватое опасностью, и вы, вероятно, пропустили некоторые крайние случаи (то есть Хэдли / Джим / и т. д. знают, что они делают, и если их наказали, это, вероятно, что-то не стоит делать).

На самом деле вместо того, чтобы использовать xml2::read_html() для захвата контента, я бы использовал httr::GET() + httr::content(…, as="text") вместо этого (если вы круты и кешируете страницы, а не тратите впустую ресурсы других людей), так как read_html() использует libxml2 под прикрытием и преобразует документ (даже если иногда просто немного), и лучше преобразовывать необработанные, кэшированные исходные данные по сравнению с чем-то, искаженным программным обеспечением, которое считает его умнее, чем мы.

FIN

На самом деле я больше ничего не могу сделать, чтобы прояснить это, чем описанная выше болтовня в многословном режиме. Надеюсь, это расширение также поможет другим понять, что происходит.

...