【问题标题】:How to save and read output of read_html as an RDS file?如何将 read_html 的输出保存和读取为 RDS 文件?
【发布时间】:2020-01-05 22:36:31
【问题描述】:

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

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

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

但这似乎不适用于使用xml2::read_html() 制作的对象

示例

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

这会创建一个文件,但不像预期的那样 即

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

发生了什么以及保存 html 对象以便可以用最少的代码/大惊小怪重新加载它的最简单方法是什么?

【问题讨论】:

    标签: r rvest xml2


    【解决方案1】:

    在保存之前使用toString()xml_document类转换为character,就像这样

    library(rvest)
    someobject <- read_html("https://stackoverflow.com/")
    
    someobject  %>% toString %>% saveRDS(., "someobject.RDS")
    newobject <- readRDS("someobject.RDS") %>% read_html
    

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

    identical(someobject, newobject)
    # [1] FALSE
    

    【讨论】:

      【解决方案2】:

      我们可以使用xml2包中的write_xmlread_html

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

      但是,identical 返回 FALSE

      identical(before, after)
      #[1] FALSE
      

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

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

      【讨论】:

        【解决方案3】:

        回答“发生了什么”:saveRDS 正在尝试序列化正在保存的对象。这里,对象someobject 是一个包含元素someobject$docsomeobject$node 的列表。元素的类型是externalptr(外部指针),这意味着它们引用了一个保存在内存中的C 数据结构。当外部指针被序列化时,引用会丢失。因此错误“外部指针无效”。

        您可以使用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

        【讨论】:

          【解决方案4】:

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

          url <-  "https://stackoverflow.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

          【讨论】:

          • 很好的调查,可以提供底部代码的结果吗?
          • 我添加到结果中,我添加了 url,因为结果取决于 URL。
          • 有趣。看起来write_xml() 可能会添加这些换行符。您也许可以执行this 来查看插入\n 字符的位置是否存在模式?
          • 我快速浏览了一下,似乎与布局有关。例如,页面底部的表格有四列:堆栈溢出、产品、公司和堆栈交换网络。如果单击第四列(其他)的底部,然后单击技术。每个列标题(除了最左边的)都会添加一个“\n”。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-10-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多