R:xml2 提取的节点比预期的多

R: xml2 extracts more nodes than expected

我不明白为什么这个代码

library(rvest)
u <- "http://alistapart.com/article/daemonskin"
h <- read_html(u)
html_nodes(h, "div.main-content[itemprop='articleBody'] a") %>%
  html_attr("href")

匹配许多超出目标 <div> 元素范围的 URL。

如果您查看 target page 的 HTML 来源:

注意: 您可能会注意到,在 HTML 的第 242 行,有一个错误的 </div 元素。但是,修复它似乎并不能解决问题。

我想这个问题与 xml2(即 libxml2)如何解析代码有关。

还有其他想法吗?

好吧,main 问题是精心设计的,不合规的 HTML 在以在这方面实际上比其他人更聪明而自豪的网站上。

当你这样做时:

library(rvest)
library(purrr)

URL <- "http://alistapart.com/article/daemonskin"

read_html(URL) %>%
  html_nodes("div.main-content[itemprop='articleBody'] a") %>%
  html_attr("href") %>%
  str()
##  chr [1:74] "#comments" ...

底层 libxml2 库是 "fixing up" 次要 HTML (XML) 错误并且有它自己的做事方式。由于解析器 "fixed" HTML.

的方式,这种方式会导致更多元素位于您所定位的特定 <div> 之下

您可以看到与真实浏览器结果的差异,我们可以用 selenium 进行模拟:

注意:我在 R 之外的 webdriver 模式下启动了 phantomjs

library(seleniumPipes)

rd <- remoteDr(browserName = "phantomjs", port = 8910)
rd %>% go(URL)

当你这样做时,它是 phantomjs(实际上是 webkit)将它自己的基于浏览器的 HTML 解析器修复方法应用到它接收到的 HTML(并且,它还添加或减去基于节点在页面加载时的任何 javascript 触发器上)。

当您使用 document.querySelectorAll() 的等价物检索节点时,您会得到 34(这也是我在开发人员工具控制台中 Chrome 中得到的):

rd %>%
  findElements("css", "div.main-content[itemprop='articleBody'] a") %>%
  map_chr(getElementAttribute, "href") %>%
  str()
##  chr [1:34] "http://alistapart.com/article/daemonskin#comments" ...

请注意,如果您决定按照我通常的方式使用 getPageSource() 通过 rvest/xml2 函数执行所有 selecting/extracting,您将得到一个不同的结果,因为它从虚拟浏览器中提取当前 HTML 页面并将其传递给 read_html(),其中 libxml2 对 webkit 理论上已经清理的内容应用了一些额外的修复:

rd %>%
  go(URL) %>%
  getPageSource() %>%
  html_nodes("div.main-content[itemprop='articleBody'] a") %>%
  html_attr("href") %>%
  str()
##  chr [1:32] "#comments" ...