我有一个很长的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 对选择器不灵活?
您正在搜索的页面不包含任何tbody
标签。当您的浏览器解析页面时,它会将缺少的tbody
元素添加到它创建的 DOM 中。这意味着当您通过浏览器的检查器和控制台检查页面时,它就像存在tbody
标签一样。
Nokogiri 在解析时不会添加此标记。当您使用 Nokogiri 搜索查询(包含 tbody
)时,它会查找显式 tbody
标记,因此在找不到匹配项时不返回任何匹配项。
最简单的解决方法是从查询中删除所有tbody
(以及任何额外的>
)。
你也可以看看Nokogumbo,它用谷歌的Gumbo HTML5解析器扩展了Nokogiri,并将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获取所有内容,然后使用search
或at
获取所需的特定序列。