根据上一个和下一个兄弟提取文本

时间:2012-07-30 10:23:52

标签: xpath web-scraping

我正在尝试从以下结构中提取数据:

<span>Heading</span>
<br />
<br />
<span>Heading1</span>
<br />
data#1
<br />
<br />
<span>Heading4</span><br />
&acirc;&euro;&cent; data#4.1
<br />
&acirc;&euro;&cent; data#4.2
<br />
&acirc;&euro;&cent; data#4.3
<br />
&acirc;&euro;&cent; data#4.4
<br />
<br />
<span>Heading5</span>
<br />
&acirc;&euro;&cent; data#5.1
<br />
&acirc;&euro;&cent; data#5.2
<br />
&acirc;&euro;&cent; data#5.3
<br />
<br />

我可以使用以下内容提取数据#1:

span[text()='Heading1']/following-sibling::br[1]/following::text()[1]

但我无法弄清楚如何在Heading4下提取数据。我需要提取data#4.1data#4.2data#4.3&amp; data#4.4。 积分数不固定,可能会有所不同。

4 个答案:

答案 0 :(得分:3)

此XPath 1.0表达式精确选择所需节点

  /*/span[.='Heading4']
        /following-sibling::text()
           [count(.|/*/span[.='Heading5']/preceding-sibling::text())
           =
            count(/*/span[.='Heading5']/preceding-sibling::text())
            ]
                  [normalize-space()]

它是由着名的Kayessian方法生成的,用于交叉两个节点集$ns1$ns2

$ns1[count(.|$ns2) = count($ns2)]

如果在Kayessian公式中我们用$ns1代替:

,我们获得上面的第一个表达式
  /*/span[.='Heading4']/following-sibling::text()

$ns2

  /*/span[.='Heading5']/preceding-sibling::text()

最终谓词[normalize-space()]过滤掉此十字路口中仅有空白的文本节点。

基于XSLT的验证

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:template match="/">
     <xsl:copy-of select=
      "/*/span[.='Heading4']
            /following-sibling::text()
               [count(.|/*/span[.='Heading5']/preceding-sibling::text())
               =
                count(/*/span[.='Heading5']/preceding-sibling::text())
                ]
                [normalize-space()]
      "/>
 </xsl:template>
</xsl:stylesheet>

将此转换应用于提供的XML文档(替换实体 - 因为我们没有定义可用的DTD,这在此不重要):

<html>
    <span>Heading</span>
    <br />
    <br />
    <span>Heading1</span>
    <br /> data#1 
    <br />
    <br />
    <span>Heading4</span>
    <br /> #acirc;#euro;#cent; data#4.1 
    <br /> #acirc;#euro;#cent; data#4.2 
    <br /> #acirc;#euro;#cent; data#4.3 
    <br /> #acirc;#euro;#cent; data#4.4 
    <br />
    <br />
    <span>Heading5</span>
    <br /> #acirc;#euro;#cent; data#5.1 
    <br /> #acirc;#euro;#cent; data#5.2 
    <br /> #acirc;#euro;#cent; data#5.3 
    <br />
    <br />
</html>

评估Xpath表达式并将此评估结果复制到输出中:

 #acirc;#euro;#cent; data#4.1 
     #acirc;#euro;#cent; data#4.2 
     #acirc;#euro;#cent; data#4.3 
     #acirc;#euro;#cent; data#4.4 

答案 1 :(得分:2)

您可以使用

span[text()='Heading4']/following-sibling::text()[. != ""] 

获取Heading4之后的所有文本,然后使用。

span[text()='Heading5']/following-sibling::text()[. != ""]

获取Heading5之后不需要的文本,然后从主程序中的第一个结果集中减去第二个结果集。

如果你有XPath 2,你可以直接用except运算符排除它们:

span[text()='Heading4']/following-sibling::text()[. != ""] except span[text()='Heading5']/following::text()[. != ""]

使用data函数之前,您只能获得&acirc;&euro;&cent;而不是substring(.,5),因此最终的XPath 2表达式变为:

(span[text()='Heading4']/following-sibling::text()[. != ""] except span[text()='Heading5']/following::text()[. != ""])/substring(., 5)

由于你没有明确说出你的语言要求,你可能也想看看我的pascal based query language,因为它的方式更好:

 <span>Heading4</span><br />
 <t:loop>
    {filter(text(), "data.*")}<br/>
 </t:loop>
 <br/>
 <span>Heading5</span><br />

答案 2 :(得分:1)

我最终在答案here

的帮助下最终使用了这个

//text()[preceding-sibling::span[1] = 'Heading4']

答案 3 :(得分:0)

我用

span[text()='Heading4']/following-sibling::text()

然后单独解析生成的文本。