使用 rlang::duplicate 克隆的 rvest object 未正确克隆
rvest object cloned with rlang::duplicate is not properly cloned
rvest
似乎没有提供任何方法来仅从 parent object 中提取文本(忽略 children)。一个 使用 xml_remove()
,它改变了原始的 object - 给定 R 的默认惰性评估一直沿内存链向上。
我查看 rlang::duplicate()
,它应该用于 “修改副本使原始 object 完好无损”,但克隆似乎没有真正独立。例如:
require(rvest)
h = '<ul>
<li id="target">
text to extract
<ul><li>text to ignore</li><li>this too</li></ul>
</li>
</ul>'
doc = xml2::read_html(h)
x = html_node(doc, '#target')
html_text(x)
#> [1] "\ntext to extract\ntext to ignorethis too"
现在克隆 x,删除它的 children,并提取文本:
x2 = rlang::duplicate(x, shallow = FALSE)
children = html_children(x2)
xml2::xml_remove(children)
html_text(x2)
#> [1] "\ntext to extract\n"
按预期工作,但是 x 也发生了变化:
html_text(x)
#> [1] "\ntext to extract\n"
关于为什么以及如何解决这个问题的任何建议?我不想开始 re-attaching children..
另一种可能的解决方案(也许是更通用的方法)是使用html_children()
函数获取所有子节点的文本,然后从全文中删除。
require(rvest)
h = '<ul>
<li id="target">
text to extract
<ul><li>text to ignore</li><li>this too</li></ul>
</li>
</ul>'
doc = xml2::read_html(h)
x = html_node(doc, '#target')
fulltext <- html_text(x)
# [1] "\ntext to extract\ntext to ignorethis too"
#find the text in the children nodes
childtext <- html_children(x) %>% html_text()
# "text to ignorethis too"
#replace the child node text with a numm
gsub(childtext, "", fulltext) %>% trimws()
#"text to extract"
#alternative using the text from the first child node
firstchild <- xml_child(x, search=1) %>% xml_text()
gsub(paste0(firstchild, ".*"), "", fulltext)
当然,如果有额外的换行符“\n”或格式化字符,gsub()
可能会中断。
首先让我说一下,我认为yoo 可以在不复制数据的情况下解决问题。我不是 xpath 方面的专家,但我认为您可以使用它来仅 select 直接文本后代,而忽略嵌套在其他 xml 节点中的文本。 IE。以下似乎没有任何副本就可以解决问题(x
定义为您的问题):
html_text(html_elements(x, xpath = "text()"))
# [1] "\ntext to extract\n"
话虽这么说,我也有一个关于如何制作深拷贝的问题的答案:
问题是rlang::duplicate()
只能复制R数据结构。然而,rvest
建立在 xml2
之上,而 xml2
建立在 C 库 libxml2
.
之上
当您在 R 中创建 xml_node
对象时,会在 libxml2
中创建相应的数据结构。在 R 端,基本上只有一个指向 libxml2
对象的指针。所以 rlang::duplicate()
只会创建该指针的副本,而不是基础数据的副本。它不能这样做,因为它无法访问它,因为它位于不同的库中(rlang
不知道)。
创建基础数据副本的最简单方法似乎是序列化和反序列化 xml。我怀疑这不是很有效。
示例:
读入原始数据:
require(rvest)
h <- '<ul>
<li id="target">
text to extract
<ul><li>text to ignore</li><li>this too</li></ul>
</li>
</ul>'
doc <- xml2::read_html(h)
x <- html_node(doc, '#target')
创建两个副本 - 一个 rlang:duplicate()
和一个 xml2::xml_unserialize()
:
x1 <- rlang::duplicate(x, shallow = FALSE)
x2 <- xml2::xml_unserialize(xml2::xml_serialize(x, NULL))
检查 x
和 x1
实际上是相同的,而 x2
是一个真实的副本(你得到的内存位置当然会与这里显示的不同):
x$doc
# <pointer: 0x0000023911334ea0>
x1$doc
# <pointer: 0x0000023911334ea0>
# --> same as x
x2$doc
# <pointer: 0x00000239113377d0>
# --> different to x
测试一切是否按预期工作:
children <- html_children(x2)
xml2::xml_remove(children)
html_text(x2)
# [1] "\n text to extract\n "
html_text(x)
# [1] "\n text to extract\n text to ignorethis too"
rvest
似乎没有提供任何方法来仅从 parent object 中提取文本(忽略 children)。一个 xml_remove()
,它改变了原始的 object - 给定 R 的默认惰性评估一直沿内存链向上。
我查看 rlang::duplicate()
,它应该用于 “修改副本使原始 object 完好无损”,但克隆似乎没有真正独立。例如:
require(rvest)
h = '<ul>
<li id="target">
text to extract
<ul><li>text to ignore</li><li>this too</li></ul>
</li>
</ul>'
doc = xml2::read_html(h)
x = html_node(doc, '#target')
html_text(x)
#> [1] "\ntext to extract\ntext to ignorethis too"
现在克隆 x,删除它的 children,并提取文本:
x2 = rlang::duplicate(x, shallow = FALSE)
children = html_children(x2)
xml2::xml_remove(children)
html_text(x2)
#> [1] "\ntext to extract\n"
按预期工作,但是 x 也发生了变化:
html_text(x)
#> [1] "\ntext to extract\n"
关于为什么以及如何解决这个问题的任何建议?我不想开始 re-attaching children..
另一种可能的解决方案(也许是更通用的方法)是使用html_children()
函数获取所有子节点的文本,然后从全文中删除。
require(rvest)
h = '<ul>
<li id="target">
text to extract
<ul><li>text to ignore</li><li>this too</li></ul>
</li>
</ul>'
doc = xml2::read_html(h)
x = html_node(doc, '#target')
fulltext <- html_text(x)
# [1] "\ntext to extract\ntext to ignorethis too"
#find the text in the children nodes
childtext <- html_children(x) %>% html_text()
# "text to ignorethis too"
#replace the child node text with a numm
gsub(childtext, "", fulltext) %>% trimws()
#"text to extract"
#alternative using the text from the first child node
firstchild <- xml_child(x, search=1) %>% xml_text()
gsub(paste0(firstchild, ".*"), "", fulltext)
当然,如果有额外的换行符“\n”或格式化字符,gsub()
可能会中断。
首先让我说一下,我认为yoo 可以在不复制数据的情况下解决问题。我不是 xpath 方面的专家,但我认为您可以使用它来仅 select 直接文本后代,而忽略嵌套在其他 xml 节点中的文本。 IE。以下似乎没有任何副本就可以解决问题(x
定义为您的问题):
html_text(html_elements(x, xpath = "text()"))
# [1] "\ntext to extract\n"
话虽这么说,我也有一个关于如何制作深拷贝的问题的答案:
问题是rlang::duplicate()
只能复制R数据结构。然而,rvest
建立在 xml2
之上,而 xml2
建立在 C 库 libxml2
.
当您在 R 中创建 xml_node
对象时,会在 libxml2
中创建相应的数据结构。在 R 端,基本上只有一个指向 libxml2
对象的指针。所以 rlang::duplicate()
只会创建该指针的副本,而不是基础数据的副本。它不能这样做,因为它无法访问它,因为它位于不同的库中(rlang
不知道)。
创建基础数据副本的最简单方法似乎是序列化和反序列化 xml。我怀疑这不是很有效。
示例:
读入原始数据:
require(rvest)
h <- '<ul>
<li id="target">
text to extract
<ul><li>text to ignore</li><li>this too</li></ul>
</li>
</ul>'
doc <- xml2::read_html(h)
x <- html_node(doc, '#target')
创建两个副本 - 一个 rlang:duplicate()
和一个 xml2::xml_unserialize()
:
x1 <- rlang::duplicate(x, shallow = FALSE)
x2 <- xml2::xml_unserialize(xml2::xml_serialize(x, NULL))
检查 x
和 x1
实际上是相同的,而 x2
是一个真实的副本(你得到的内存位置当然会与这里显示的不同):
x$doc
# <pointer: 0x0000023911334ea0>
x1$doc
# <pointer: 0x0000023911334ea0>
# --> same as x
x2$doc
# <pointer: 0x00000239113377d0>
# --> different to x
测试一切是否按预期工作:
children <- html_children(x2)
xml2::xml_remove(children)
html_text(x2)
# [1] "\n text to extract\n "
html_text(x)
# [1] "\n text to extract\n text to ignorethis too"