Время шить
(я имею в виду, вы использовали слово нить , так что я не упускаю возможность провести каламбур или три).
Глубоко на странице справки для ?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
На самом деле я больше ничего не могу сделать, чтобы прояснить это, чем описанная выше болтовня в многословном режиме. Надеюсь, это расширение также поможет другим понять, что происходит.