HTML 使用 mclapply 时页面未保留在列表中
HTML pages not retained in list while using mclapply
虽然简单地使用 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 ...
使用并行 mc 时lapply:
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,我也无法正常获得所需的结果 lapply。求助!
该缝纫了
(我的意思是,你用了 thread 这个词,所以我不会错过一三个双关语的机会)。
在 ?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 无法自行序列化它 needs help。 (那个十六进制字符串 — 0x7ff45ab17ce0
— 是指向隐藏此不透明数据的位置的内存指针)。
"You can't be serious…"
完全是。
如果您来自密苏里州("Show Me" 州),我们可以通过尝试将上面的文档保存到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"
现在,你们可能都像 "AHA! See, it works!" Aaaaand…你们会 错了:
doc2$node
## <pointer: 0x0>
0x0
表示它没有指向任何东西。你已经丢失了所有这些数据。它消失了。永远。 (但是,它有一个很好的 运行 所以我们不应该太难过)。
这 has been discussed by the xml2
devs 并且——他们并没有让我们的生活更轻松——而是踢球并取得了 ?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()
。 (实际上有点让人失望。)
因为当你return一个xml_document
、html_document
(或他们的_node[s]
siblings) in a parallel apply 你最终得到的是一无所有。
内容窃贼无辜的抓取者可以做什么来克服这个毁灭性的限制?
您(至少)有四个选择:
- 在并行应用中扩展函数的复杂性,将 XML/HTML 文档处理成数据框、向量或对象列表,这些都可以由 R 自动序列化,以便为您组合
- 保持冷静,并进行一个并行应用,将 HTML 保存到文件中(HTTP 操作可能很慢),然后是一个非并行操作,顺序读取它们并处理它们——这看起来你无论如何都要去做。请注意,如果您不对文件进行 HTML 缓存,那么您就是一个水蛭和 rly 坏网民,因为您表明您真的不关心带宽和 CPU您正在
窃取 抓取的内容的费用。
- 不要因为做 ^^ 而变得很酷,而是直接从并行应用中使用
as.character((read_html(…))
到 return 原始的、可序列化的字符 HTML,然后重新 xml2
他们回到你程序的其余部分
- 分叉
xml2
,在适当的序列化 hack 中分层,不要打扰它,因为你可能会花费大量时间试图说服他们这是值得的,但最终还是失败了,因为这个“externalptr
序列化` 是一项棘手的工作,充满危险,你可能会错过一些边缘情况(即 Hadley/Jim/etc 知道他们在做什么,如果他们下注,这很可能是不值得做的事情)。
In reality, rather than use xml2::read_html()
to grab the content, I'd use httr::GET()
+ httr::content(…, as="text")
instead (if you're being cool and caching the pages vs callously wasting other folks' resources) since read_html()
uses libxml2
under the covers and transforms the document (even if sometimes just a little) and it's better to have untransformed raw, cached source data vs something mangled by software that thinks its smarter than we are.
芬兰
除了上面的冗长模式,我真的没有什么可以澄清的了。希望这个扩展也能帮助其他人理解正在发生的事情。
虽然简单地使用 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 ...
使用并行 mc 时lapply:
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,我也无法正常获得所需的结果 lapply。求助!
该缝纫了
(我的意思是,你用了 thread 这个词,所以我不会错过一三个双关语的机会)。
在 ?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 无法自行序列化它 needs help。 (那个十六进制字符串 — 0x7ff45ab17ce0
— 是指向隐藏此不透明数据的位置的内存指针)。
"You can't be serious…"
完全是。
如果您来自密苏里州("Show Me" 州),我们可以通过尝试将上面的文档保存到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"
现在,你们可能都像 "AHA! See, it works!" Aaaaand…你们会 错了:
doc2$node
## <pointer: 0x0>
0x0
表示它没有指向任何东西。你已经丢失了所有这些数据。它消失了。永远。 (但是,它有一个很好的 运行 所以我们不应该太难过)。
这 has been discussed by the xml2
devs 并且——他们并没有让我们的生活更轻松——而是踢球并取得了 ?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()
。 (实际上有点让人失望。)
因为当你return一个xml_document
、html_document
(或他们的_node[s]
siblings) in a parallel apply 你最终得到的是一无所有。
内容窃贼无辜的抓取者可以做什么来克服这个毁灭性的限制?
您(至少)有四个选择:
- 在并行应用中扩展函数的复杂性,将 XML/HTML 文档处理成数据框、向量或对象列表,这些都可以由 R 自动序列化,以便为您组合
- 保持冷静,并进行一个并行应用,将 HTML 保存到文件中(HTTP 操作可能很慢),然后是一个非并行操作,顺序读取它们并处理它们——这看起来你无论如何都要去做。请注意,如果您不对文件进行 HTML 缓存,那么您就是一个水蛭和 rly 坏网民,因为您表明您真的不关心带宽和 CPU您正在
窃取抓取的内容的费用。 - 不要因为做 ^^ 而变得很酷,而是直接从并行应用中使用
as.character((read_html(…))
到 return 原始的、可序列化的字符 HTML,然后重新xml2
他们回到你程序的其余部分 - 分叉
xml2
,在适当的序列化 hack 中分层,不要打扰它,因为你可能会花费大量时间试图说服他们这是值得的,但最终还是失败了,因为这个“externalptr
序列化` 是一项棘手的工作,充满危险,你可能会错过一些边缘情况(即 Hadley/Jim/etc 知道他们在做什么,如果他们下注,这很可能是不值得做的事情)。
In reality, rather than use
xml2::read_html()
to grab the content, I'd usehttr::GET()
+httr::content(…, as="text")
instead (if you're being cool and caching the pages vs callously wasting other folks' resources) sinceread_html()
useslibxml2
under the covers and transforms the document (even if sometimes just a little) and it's better to have untransformed raw, cached source data vs something mangled by software that thinks its smarter than we are.
芬兰
除了上面的冗长模式,我真的没有什么可以澄清的了。希望这个扩展也能帮助其他人理解正在发生的事情。