如何将 read_html 的输出保存和读取为 RDS 文件?

How to save and read output of read_html as an RDS file?

可以像这样保存和读取对象

# Save as file
saveRDS(iris, "mydata.RDS")

# Read back in 
readRDS("mydata.RDS")

但这似乎不适用于使用 xml2::read_html()

制作的对象

例子

library(rvest)
someobject <- read_html("https://whosebug.com/")
saveRDS(someobject, "someobject.RDS")

创建了一个文件,但不是预期的那样 即

readRDS("someobject.RDS")
Error in doc_is_html(x$doc) : external pointer is not valid

这是怎么回事?保存 html 对象以便可以用最少的 code/fuss 将其加载回的最简单方法是什么?

在保存前使用toString()xml_documentclass转换为character,像这样

library(rvest)
someobject <- read_html("https://whosebug.com/")

someobject  %>% toString %>% saveRDS(., "someobject.RDS")
newobject <- readRDS("someobject.RDS") %>% read_html

请注意,这些对象并不完全相同(我不确定为什么)。

identical(someobject, newobject)
# [1] FALSE

我们可以使用 xml2 包中的 write_xmlread_html

before <- read_html("https://whosebug.com/")
xml2::write_xml(before, "someobject1.xml")
after <- xml2::read_html("someobject1.xml")

然而,identical returns FALSE

identical(before, after)
#[1] FALSE

但对它们的查询似乎 return 相同的结果

library(rvest)
before %>%  html_nodes("div")
after %>% html_nodes("div")

回答"what's going on":saveRDS 正在尝试序列化正在保存的对象。这里,对象 someobject 是一个包含元素 someobject$docsomeobject$node 的列表。元素的类型是 externalptr(外部指针),这意味着它们引用保存在内存中的 C 数据结构。当外部指针被序列化时,引用就会丢失。因此错误 "external pointer is not valid".

您可以使用 as.character() 序列化 someobject 并将其传递给 saveRDS:

saveRDS(as.character(someobject), "someobject.RDS")

然后使用 readRDSread_html 重新创建对象:

someobject <- read_html(readRDS("someobject.RDS"))

但是像其他人建议的那样使用 write_html() 更容易。

一些讨论in this Github issue thread

据我所知,使用 XMLRDS 文件的方法似乎相差相同的字符数。我做了一个比较,原始版本和加载版本之间的差异似乎在正文节点中。

url <-  "https://whosebug.com/"
html <- read_match(url)
html_node(html, "body")  %>% html_text() %>%  unlist() -> OBT
nchar(OBT)

28879

xml2::write_xml(html, "someobject1.xml")
html_node(html, "body")  %>% html_text() %>%  unlist() -> BT1
nchar(BT1)

28893

html   %>% toString %>% saveRDS(., "someobject.RDS")
after2 <- readRDS("someobject.RDS") %>% read_html
html_node(html, "body")  %>% html_text() %>%  unlist()-> BT2
nchar(BT2)

28893

这说明加载的两个对象的字符数相同。 如果我们从所有文本对象中删除一个“\n”字符,数字应该相同。

BT1 %>% str_remove_all(.,"\n") %>% nchar(.)

27733

BT2 %>% str_remove_all(.,"\n") %>% nchar(.) 

27733

OBT %>% str_remove_all(.,"\n") %>% nchar(.) 

27733