XPath中基于叔叔的过滤器

XPath中基于叔叔的过滤器,xpath,scrapy,Xpath,Scrapy,假设我有一个包含以下行的HTML表 ... <tr> <th title="Library of Quintessential Memes">LQM:</th> <td> <a href="docs/lqm.html"><b>Intro</b></a> <a href="P/P79/">79</a> <a href="P/P80/">

假设我有一个包含以下行的HTML表

...
<tr>
  <th title="Library of Quintessential Memes">LQM:</th>
  <td>
    <a href="docs/lqm.html"><b>Intro</b></a>
    <a href="P/P79/">79</a>
    <a href="P/P80/">80</a>
    <a href="P/P81/">81</a>
    <a href="P/P82/">82</a>
  </td>
</tr>
<tr>
  <th title="Library of Boring Books">LBB:</th>
  <td>
    <a href="docs/lbb.html"><b>Intro</b></a>
    <a href="R/R80/">80</a>
    <a href="R/R81/">81</a>
    <a href="R/R82/">82</a>
    <a href="R/R83/">83</a>
    <a href="R/R84/">84</a>
  </td>
</tr>
...
。。。
LQM:
LBB:
...
我想选择
元素中的所有
元素,其关联的
文本位于一小组固定标题中(例如LQM、LBR和RTT)。如何将其表述为XPath查询


编辑:我使用的是Scrapy,一个Python刮片工具包,因此如果将这个查询作为一组较小的查询来表达更容易,我将非常乐意使用它。例如,如果我可以选择第一个
子元素与正则表达式匹配的所有
元素,然后选择剩余
元素的所有
子元素,那就太棒了。

以下XPath可以工作:

//a[contains(',LQM:,LBR:,RTT:,',
             concat(',', ancestor::td/preceding-sibling::th, ','))]
理论上,这可能会得到一些误报(如果代码中包含逗号)

更严格的说法是:

//a[ancestor::td/preceding-sibling::th[.='LQM:']]
|//a[ancestor::td/preceding-sibling::th[.='LBR:']]
|//a[ancestor::td/preceding-sibling::th[.='RTT:']]
我通过在输入周围添加
标记并应用以下XSL转换来测试这一点:

<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:template match="/">
        <xsl:for-each select="//a[ancestor::td/preceding-sibling::th[.='LQM:']]
                                  |//a[ancestor::td/preceding-sibling::th[.='LBR:']]
                                  |//a[ancestor::td/preceding-sibling::th[.='RTT:']]">
            <xsl:text>
</xsl:text>
            <xsl:copy-of select="."/>
        </xsl:for-each>
    </xsl:template>

</xsl:transform>

它产生以下输出:

<a href="docs/lqm.html"><b>Intro</b></a>
<a href="P/P79/">79</a>
<a href="P/P80/">80</a>
<a href="P/P81/">81</a>
<a href="P/P82/">82</a>

当然,如果您使用的是XSL,那么您可能会发现这种结构更具可读性:

<xsl:for-each select="//a">
    <xsl:variable name="header" select="ancestor::td/preceding-sibling::th"/>

    <xsl:if test="$header='LQM:' or $header = 'LBR:' or $header = 'RTT:'">
        <xsl:text>
        </xsl:text>
        <xsl:copy-of select="."/>

    </xsl:if>
</xsl:for-each>

以下XPath将起作用:

//a[contains(',LQM:,LBR:,RTT:,',
             concat(',', ancestor::td/preceding-sibling::th, ','))]
理论上,这可能会得到一些误报(如果代码中包含逗号)

更严格的说法是:

//a[ancestor::td/preceding-sibling::th[.='LQM:']]
|//a[ancestor::td/preceding-sibling::th[.='LBR:']]
|//a[ancestor::td/preceding-sibling::th[.='RTT:']]
我通过在输入周围添加
标记并应用以下XSL转换来测试这一点:

<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:template match="/">
        <xsl:for-each select="//a[ancestor::td/preceding-sibling::th[.='LQM:']]
                                  |//a[ancestor::td/preceding-sibling::th[.='LBR:']]
                                  |//a[ancestor::td/preceding-sibling::th[.='RTT:']]">
            <xsl:text>
</xsl:text>
            <xsl:copy-of select="."/>
        </xsl:for-each>
    </xsl:template>

</xsl:transform>

它产生以下输出:

<a href="docs/lqm.html"><b>Intro</b></a>
<a href="P/P79/">79</a>
<a href="P/P80/">80</a>
<a href="P/P81/">81</a>
<a href="P/P82/">82</a>

当然,如果您使用的是XSL,那么您可能会发现这种结构更具可读性:

<xsl:for-each select="//a">
    <xsl:variable name="header" select="ancestor::td/preceding-sibling::th"/>

    <xsl:if test="$header='LQM:' or $header = 'LBR:' or $header = 'RTT:'">
        <xsl:text>
        </xsl:text>
        <xsl:copy-of select="."/>

    </xsl:if>
</xsl:for-each>


谢谢!这当然有效,但这真的是最好的方法吗?看看您是否在使用XPath2.0。否则,是的,我认为这是最好的方法。还要注意,我更新了一个比我的第一个版本更短的版本。谢谢!这当然有效,但这真的是最好的方法吗?看看您是否在使用XPath2.0。另外,是的,我认为这是最好的方法。还要注意的是,我更新了一个比第一个版本更短的版本。