【问题标题】:Mechanize search unable to find CSS selector (it's definitely present)机械化搜索无法找到 CSS 选择器(它肯定存在)
【发布时间】:2015-06-20 23:14:12
【问题描述】:

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

选择器的目标是一个段落,在我的其他情况下是一个标题1。我还使用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,但事实并非如此。

我进入了实际的在线页面并打开了我的控制台并运行了这个简单的 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 对选择器不灵活?

【问题讨论】:

  • 一个 html sn-p 和实际的选择器会很好。
  • 我会尽快把它们放在这里。
  • 您将 HTML 分配给 page,但随后您正在搜索 inner_page。它还有助于查看确切的 URL 和 CSS 选择器。你真的在看example.com吗?
  • 正如我提到的,inner_page 确实返回有效页面。我已经对它们进行了分析,它们确实包含了我正在使用相同的选择器寻找的结果,但是它没有返回一些东西。 read_more_selector 查找a 标签并获取它们的href 属性,然后我们使用它来创建inner_page。我将很快添加代码示例 - 我发布此内容时已经很晚了。
  • @pguardiario 添加了示例代码。

标签: ruby css-selectors nokogiri mechanize


【解决方案1】:

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

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

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

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

【讨论】:

  • 在尝试查找带有代码的节点之前查看 HTML 时,我总是将 HTML 转储到磁盘或直接转储到编辑器中,然后在没有浏览器的情况下查看它。使用nokogiri some_url 非常方便,它将读取页面并将您转储到带有填充@doc 的IRB 中,让您也可以这样浏览。做这些事情会立即表明tbody 不存在并避免这种红鲱鱼。
【解决方案2】:

使用 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 获取您想要的特定一个。

【讨论】:

    猜你喜欢
    • 2011-01-15
    • 2013-01-21
    • 1970-01-01
    • 2015-12-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多