0

我不明白为什么这段代码

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。

如果您查看目标页面的 HTML 源代码:

  • 只有一个<div>符合我的查询。它从第 177 行开始。
  • <div>停靠在 396 号线。
  • 然而,html_nodes上面代码中返回的许多 URL 都是从该行之外的节点中提取的,该<div>.

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

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

还有其他想法吗?

4

1 回答 1

1

嗯,主要问题是网站上的 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库正在“修复”轻微的 HTML (XML) 错误,并且有自己的处理方式。由于解析器“修复” 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函数进行所有选择/提取,您将得到不同的结果,因为它从虚拟浏览器中提取当前 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" ...
于 2017-01-08T16:15:19.237 回答