机械化搜索无法找到 CSS 选择器(肯定存在)

Mechanize search unable to find CSS selector (it's definitely present)

我有一个很长的 CSS 选择器,在实际用于 CSS、jQuery 等时工作得很好。但是这个完全相同的选择器在 [=15= 上不起作用] 对象 - 它只是 return 一个空数组。

选择器以段落为目标,在我的另一种情况下是 header1。我还使用 page.body 将我的页面结果转换为字符串,并且该元素肯定存在,但是 search(或 at)方法不会 return 我任何东西。

这可能是什么原因?

我的代码如下所示:

agent = Mechanize.new
page  = agent.get 'http://example.com'

page.search(source.read_more_selector).each do |read_more|
  inner_page = agent.get(read_more['href'])
  # displaying inner_page.body gives me a few valid HTML pages, but...

  inner_page.search(source.inner_title_selector).each do |inner_content|
    # but here, there's nothing here, inner_content is nil even though the selector should get us something back definitely
  end
end

正常工作 CSS 选择器 (source.inner_content_selector)

div#main-container-body > div#body-container > table > tbody > tr > td > span#ajaxprochoice > table > tbody > tr > td > table > tbody > tr > td > table > tbody > tr > td > div > h1.h1productHead

inner_page.body的输出(众多循环结果之一。由于字符太多,无法在此处添加):

http://pastebin.com/MtXDVADR

所以上面的选择器应该绝对匹配 HTML 代码中的段落(当然,虽然它是一个 Mechanize::Page 对象,而不是字符串)和 inner_page.search,但是不是。

我转到了实际的在线页面并打开了我的控制台,然后 运行 这个简单的 jQuery 命令进行了尝试:

$('div#main-container-body > div#body-container > table > tbody > tr > td > span#ajaxprochoice > table > tbody > tr > td > table > tbody > tr > td > table > tbody > tr > td > div > h1.h1productHead').hide();

它奏效了!这几乎意味着选择器在这里有效。

编辑

当我添加这段代码时:

inner_page.at('.h1productHead').to_s

这 return 给我一个结果。但是当我使用完整选择器时,它没有 return 任何东西。为什么 Mechanize 在这种情况下对选择器不灵活?

您正在搜索的页面不包含任何 tbody 标签。当您的浏览器解析页面时,它会将缺少的 tbody 元素添加到它创建的 DOM 中。这意味着当您通过浏览器的检查器和控制台检查页面时,它就像 tbody 标签存在一样。

Nokogiri 在解析时不添加这个标签。当您使用 Nokogiri 搜索您的查询(包含 tbody)时,它会查找明确的 tbody 标记,因此 returns 在找不到匹配项时不会匹配。

最简单的解决方法是从查询中删除所有 tbody(连同任何额外的 >)。

您还可以查看 Nokogumbo, which extends Nokogiri with Google’s Gumbo HTML5 parser,它会将 tbody 元素添加到已解析的文档中。

使用 DOM 时要学习的一个重要策略是定位文档中的关键地标并使用它们进行导航,而不是尝试指定从顶部看到的每个标签到所需节点.如果您可以使用特定 ID 或 类,请使用这些 ID。如果有特定的节点模式,那么它们可能会有用。指定从 A 到 B 的每个标签很容易出错(如您所见),而且通常没有必要。

而不是像这样的选择器:

$('div#main-container-body > div#body-container > table > tbody > tr > td > span#ajaxprochoice > table > tbody > tr > td > table > tbody > tr > td > table > tbody > tr > td > div > h1.h1productHead').hide();

您可以尝试类似的方法:

div#body-container span#ajaxprochoice table table table h1.h1productHead

并让 libXML 定位所需的结束节点。

您甚至可以将其减少到:

div#body-container h1.h1productHead

由于在一个格式良好的HTML页面中只能有一个#body-container,这意味着要找到它下面的<h1 class="h1productHead">。如果有多个,您可以使用 CSS 索引或告诉 Nokogiri 全部获取它们然后使用 searchat.