【问题标题】:Saving xml nodes in R [duplicate]在R中保存xml节点[重复]
【发布时间】:2018-04-22 02:06:48
【问题描述】:

我对使用 R 和 xml 进行抓取非常陌生,我有一个关于保存和加载数据集的问题。

我使用如下代码抓取了一个相当大的数据集

data<-list()
for(i in page[1:10]){
  pages<-read_html(paste0("http://www.gbig.org/buildings/", i))
  nodes<-html_nodes(pages, '.badge-info .cert-badge , .event , 
.date , .media-heading a , .truncated , .location , .buildings-type')
 data[[i]]  <-nodes
}

我想我可以保存数据并再次加载以备将来使用

save(data, file="trials.RData")

当我加载它并尝试再次使用它时,我收到一条错误消息。我做错了什么?以及保存和加载 xml 节点的最佳方式是什么?

{xml_nodeset (10)}
Error in node_write_character(x$node, options = options, encoding = encoding) : 
  external pointer is not valid

编辑

我尝试的加载命令是:

load("trials.RData")

谢谢

【问题讨论】:

  • 是的。这是技术上重复的,但请参阅为替代方法和使用重复答案中提到的序列化的另一种方法提供的答案。

标签: r xml rvest


【解决方案1】:

它不起作用的原因是节点是“xptr”或“外部指针”,当它们被保存到 R 数据文件时它们没有被序列化。 xml2 包存储库和 R 文档中的各种其他地方确实对此有警示性指导,但不再有 RTFM 了。 #叹息

解决您的问题的一种方法阻止自己将来再次对站点进行 DoSing 是从节点中提取数据与尝试保存原始节点 保留源页面的副本,以便您可以抓取该页面,而不是返回该站点并(再次)浪费他们的带宽。

我们需要一些包:

library(rvest)
library(httr)
library(tidyverse)

您应该始终首先查看网站robots.txt 和服务条款/条款和条件。这个网站有robots.txt,但没有 ToS/T&C,所以我们会看看他们是否允许你尝试做的事情:

robotstxt::get_robotstxt(urltools::domain("http://www.gbig.org/buildings/")) %>%
  cat()
## # See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file
## #
## # To ban all spiders from the entire site uncomment the next two lines:
## # User-Agent: *
## # Disallow: /
## User-Agent: *
## Crawl-delay: 10
## Disallow: /beta_invites
## Disallow: /admin
## Disallow: /search
## Disallow: /green_schools
## Disallow: /api
## Disallow: /places/8194/activities
## Disallow: /places/935/activities

因此,我们需要在页面请求之间使用 10 秒的抓取延迟,并且您最好希望您没有通过使用 /search/api 路径来获取该页面列表而违反技术控制。

此外,我们稍后将需要它,因为我们将采用另一种方法来获取您想要的节点:

c(
  ".badge-info .cert-badge", ".event", ".date" , ".media-heading a",
  ".truncated", ".location" , ".buildings-type"
) -> target_nodes

而且,我们稍后也需要清理^^

clean_node_names <- function(x) {
  x <- tolower(x)
  x <- gsub("[[:punct:][:space:]]+", "_", x)
  x <- gsub("_+", "_", x)
  x <- gsub("(^_|_$)", "", x)
  x <- make.unique(x, sep = "_")
  x
}

对于这个示例 --- 因为您没有提供任何数据 --- 我们需要一些 URL,因此我们将从该页面中获取前 12 个:

pg <- read_html("http://www.gbig.org/buildings/")

html_nodes(pg, "a.cell") %>%
  html_attr("href") %>%
  sprintf("http://www.gbig.org%s", .) -> building_urls

现在,设置一个进度条,因为页面之间的 10 秒延迟会让这看起来很慢。我知道您可能会遵守 robots.txt 规则,但许多其他人不太可能遵守规则,但这并不意味着您不应该这样做。

pb <- progress_estimated(length(building_urls))

最后,遍历这些 URL 并:

  • 暂停
  • 阅读页面
  • 通过从每个 CSS 选择器路径中提取节点文本来构建数据框;它们的长度不均匀,因此我们将它们全部设为list()
  • 保存HTML页面的字符源

注意:与这种粉碎和抓取方法相比,您可以通过更多单独/故意的节点提取来制作更好的数据框。

map_df(building_urls, ~{

  pb$tick()$print()

  Sys.sleep(10)

  x <- read_html(.x)

  map(target_nodes, html_nodes, x=x) %>%
    map(html_text) %>%
    set_names(clean_node_names(target_nodes)) %>%
    map(~list(.x)) %>%
    as_data_frame() -> tmpdf

  tmpdf$src_html <- as.character(pg)

  tmpdf

}) -> xdf

而且,稍等片刻之后:

glimpse(xdf)
## Observations: 12
## Variables: 8
## $ badge_info_cert_badge <list> [<"Case Study", "Case Study", "Case Stu...
## $ event                 <list> [<"Whole Building Design Guide Case Stu...
## $ date                  <list> [<"06/20/2014", "08/13/2013", "08/13/20...
## $ media_heading_a       <list> [<"The Mutual Building  Christman Compa...
## $ truncated             <list> ["\nThe Christman Building LEED-EB, The...
## $ location              <list> ["208 N Capitol Ave, Lansing, MI, USA",...
## $ buildings_type        <list> ["\n\nThe Christman Building\n", "\n\nS...
## $ src_html              <chr> "<!DOCTYPE html>\n<html lang=\"en\">\n<h...

因为我们存储了src_html,如果您确实需要从每个建筑物中获取更多/不同的信息,您可以使用read_html() 处理那个

注意:有an alternate method 使用xml2::xml_serialize()

pb <- progress_estimated(length(building_urls))

map(building_urls, ~{

  pb$tick()$print()

  Sys.sleep(10)

  read_html(.x) %>%
    html_nodes(
      '.badge-info .cert-badge , .event , .date , .media-heading a , .truncated , .location , .buildings-type'
    ) %>%
    xml_serialize(NULL) -> nodes

  nodes

}) -> bldg_lst

现在,这是一个原始向量列表:

str(bldg_lst)
## List of 12
##  $ : raw [1:4273] 58 0a 00 00 ...
##  $ : raw [1:4027] 58 0a 00 00 ...
##  $ : raw [1:3164] 58 0a 00 00 ...
##  $ : raw [1:7718] 58 0a 00 00 ...
##  $ : raw [1:2996] 58 0a 00 00 ...
##  $ : raw [1:2908] 58 0a 00 00 ...
##  $ : raw [1:4506] 58 0a 00 00 ...
##  $ : raw [1:4127] 58 0a 00 00 ...
##  $ : raw [1:2982] 58 0a 00 00 ...
##  $ : raw [1:3034] 58 0a 00 00 ...
##  $ : raw [1:1800] 58 0a 00 00 ...
##  $ : raw [1:1877] 58 0a 00 00 ...

你可以节省下来。

当回读时,你会这样做:

map(bldg_lst, xml_unserialize)
## [[1]]
## {xml_nodeset (65)}
##  [1] <h2 class="buildings-page-title buildings-type"><img alt="Building" ...
##  [2] <p class="location">208 N Capitol Ave, Lansing, MI, USA</p>
## ...
## 
## [[2]]
## {xml_nodeset (62)}
##  [1] <h2 class="buildings-page-title buildings-type"><img alt="Building" ...
##  [2] <p class="location">3825 Wisconsin Ave NW, Washington, DC, USA</p>
## ...
## 
## [[3]]
## {xml_nodeset (54)}
##  [1] <h2 class="buildings-page-title buildings-type"><img alt="Building" ...
##  [2] <p class="location"> San Francisco, CA, USA</p>
## ...
## 
## [[4]]
## {xml_nodeset (127)}
##  [1] <h2 class="buildings-page-title buildings-type"><img alt="Building" ...
##  [2] <p class="location"> Washington, DC, USA</p>
## ...
## 
## [[5]]
## {xml_nodeset (50)}
##  [1] <h2 class="buildings-page-title buildings-type"><img alt="Building" ...
##  [2] <p class="location">4940 N 118th St, Omaha, NE, USA</p>
## ...
## 
## [[6]]
## {xml_nodeset (47)}
##  [1] <h2 class="buildings-page-title buildings-type"><img alt="Building" ...
##  [2] <p class="location"> Dallas, TX, USA</p>
## ...
## 
### (etc)

我仍然认为第一个建议的方法更好。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-08
    • 1970-01-01
    • 2011-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多