Nokogiri和Xpath:查找两个标签之间的所有文本

时间:2014-06-12 21:00:39

标签: html ruby xpath nokogiri

我不确定它是否是语法或版本差异的问题,但我似乎无法解决这个问题。我想将td标记内的(非结束)h2内的数据转移到h3标记。这就是HTML的样子。

<td valign="top" width="350">
    <br><h2>NameIWant</h2><br>
    <br>Town<br>

    PhoneNumber<br>
    <a href="mailto:emailIwant@nowhere.com" class="links">emailIwant@nowhere.com</a>
    <br>
    <a href="http://websiteIwant.com" class="links">websiteIwant.com</a>
    <br><br>    
    <br><img src="images/spacer.gif"/><br>

    <h3><b>I want to stop before this!</b></h3>
    Lorem Ipsum Yadda Yadda<br>
    <img src="images/spacer.gif" border="0" width="20" height="11" alt=""/><br>
    <td width="25">
        <img src="images/spacer.gif" border="0" width="20" height="8" alt=""/>
        <td valign="top" width="200"><img src="images/spacer.gif"/>
            <br>
            <br>

            <table cellspacing="0" cellpadding="0" border="0"/>205"&gt;<tr><td>
                <a href="http://dontneedthis.com">
                </a></td></tr><br>
            <table border="0" cellpadding="3" cellspacing="0" width="200">
            ...

<td valign>并未关闭,直到页面的最底部,我认为这可能是我遇到问题的原因。

我的Ruby代码如下:

require 'open-uri'
require 'nokogiri'

@doc = Nokogiri::XML(open("http://www.url.com"))

content = @doc.css('//td[valign="top"] [width="350"]')

name = content.xpath('//h2').text
puts name // Returns NameIwant

townNumberLinks = content.search('//following::h2')
puts content // Returns <h2> NameIWant </h2>

据我所知,语法应该&#34;在当前节点的结束标记之后选择文档中的所有内容&#34;。如果我尝试使用preceding之类的:

townNumberLinks = content.search('//preceding::h3')
// I get: <h3><b>I want to stop before this!</b></h3>

希望我明确表达了我想要做的事情。谢谢!

2 个答案:

答案 0 :(得分:3)

这不是微不足道的。在您选择的节点(td)的上下文中,要在两个元素之间获取所有,您需要执行这两个集合的交集: / p>

  1. 设置 A 之前的所有节点 h3//h3[1]/preceding::node()
  2. 设置 B <{em> h2之后的所有节点//h2[1]/following::node()
  3. 要执行交集,您可以使用 Kaysian method (在Michael Kay之后提出它)。基本公式是:

    A[count(.|B) = count(B)]
    

    将其应用于您的集合,如上所述,其中 A = //h3[1]/preceding::node() B = //h2[1]/following::node(),我们有:

    //h3[1]/preceding::node()[ count( . | //h2[1]/following::node()) = count(//h2[1]/following::node()) ]
    

    将从<br>标记后面的第一个</h2>开始选择所有元素和文本节点到最后一个<br>之后的空白文本节点,就在下一个<h3>标记之前。

    您可以轻松选择h2h3 之间的文本节点替换表达式中node()的{​​{1}}。这个将返回两个标题之间的所有文本节点(包括空格和换行符):

    text()

答案 1 :(得分:1)

查找单元格中第一个<h3>之前的所有元素,而不是检索所有之前没有<h2>标记的兄弟姐妹作为前一个兄弟。用XPath表达式替换//td以准确检索此表格单元格。

//td/h3[1]/preceding-sibling::*[preceding-sibling::h2]