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

时间:2015-06-20 23:14:12

标签: ruby css-selectors nokogiri mechanize

我有一个很长的CSS选择器,当它实际用在CSS,jQuery等中时效果很好。但是这个相同的选择器不适用于Mechanize::Page对象 - 它只返回一个空数组。

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

这可能是什么原因?

我的代码如下所示:

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,但它&#39 ;不是。

我在线访问了实际的页面并打开了我的控制台并运行了这个简单的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

这给了我一个结果。但是当我使用完整选择器时,它不会返回任何内容。在这种情况下,为什么Mechanize对选择器不灵活?

2 个答案:

答案 0 :(得分:3)

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

Nokogiri在解析时不添加此标记。当您使用Nokogiri搜索您的查询(包含tbody)时,它会查找显式的tbody标记,因此在找不到匹配项时不会返回匹配项。

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

您还可以查看使用Nokogumbo扩展Nokogiri的Google’s Gumbo HTML5 parser,并将tbody元素添加到已解析的文档中。

答案 1 :(得分:0)

使用DOM时要学习的一个重要策略是在文档中定位关键标记并使用它们进行导航,而不是尝试指定从顶部到所需节点的每个标记。如果您可以使用特定的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获取您想要的特定值。