【问题标题】:XPath statement to find nearest preceding siblingXPath 语句查找最近的前同级
【发布时间】:2017-11-29 13:19:48
【问题描述】:

我在 C# WPF 应用程序中使用 HTMLAgilityPack 来遍历本地 HTML 页面中的一些锚标记并提取 href 属性。这很好用,但是我需要在 HTML 文档中找到锚所在的标题(这也是一个锚标记)。使用 XPath 应该很容易做到这一点,但我似乎无法得到适用于所有场景的语句。

这是我的 HTML 示例(我无法控制):

<html>
    <body>
        <table>
            <tr>
                <td><div><a href="#maintitle" class="title">maintitle</a></div></td>
            </tr>
            <tr>
                <td><div><a href="#subtitle1" class="subtitle">subtitle1</a></div></td>
            </tr>
            <tr>
                <td><div><a href="link1.pdf">link1</a></div></td>
            </tr>
            <tr>
                <td><div><a href="link2.pdf">link2</a></div></td>
            </tr>
            <tr>
                <td><div><a href="link3.pdf">link3</a></div></td>
            </tr>
            <tr>
                <td><div><a href="#subtitle2" class="subtitle">subtitle2</a></div></td>
            </tr>
            <tr>
                <td><div><a href="link4.pdf">link4</a></div></td>
            </tr>
            <tr>
                <td><div><a href="link5.pdf">link5</a></div></td>
            </tr>
        </table>
    </body>
</html>

找到link1后,我想找到subtitle1。链接 2 和链接 3 也是如此。但是对于link4和link5,我想找subtitle2。我正在使用这个 XPath 语句(第一部分只是为了模拟锚标记的选择,我一直在使用在线 XPath 评估器https://www.freeformatter.com/xpath-tester.html):

//a[@href='link4.pdf']/ancestor::tr/preceding-sibling::tr//a[@class='subtitle']

这适用于链接 1 到链接 3,但对于链接 4 和链接 5,它会返回 subtitle1 和 subtitle2。将[1] 添加到preceding-sibling::t 可以修复link4,但会破坏link2、link3 和link5:

//a[@href='link4.pdf']/ancestor::tr/preceding-sibling::tr[1]//a[@class='subtitle']

我也尝试将last() 添加到preceding-sibling::t,但这导致找不到任何链接:

//a[@href='link4.pdf']/ancestor::tr/preceding-sibling::tr[last()]//a[@class='subtitle']

我确信有一个简单的解决方案,但我绝不会胜任 XPath,所以我很苦恼。如何让我的原始 XPath 语句返回最近的兄弟?

【问题讨论】:

    标签: c# xpath html-agility-pack


    【解决方案1】:

    通过链接文本('link4')获取字幕的定位器

    (//a[text()='link5']/preceding::tr[.//a[@class='subtitle']])[last()]

    逻辑:

    //a[text()='link4'] - 通过链接文本获取元素

    //a[text()='link4']/preceding::tr - 搜索所有 tr 父母

    [.//a[@class='subtitle']] - 获取包含标签a的第一个父级 类'subtitle'

    (someLocator)[last()] - 获取最后一个匹配定位器的元素,在我们的例子中 - 获取最后一个包含标签 a 的父级,类为 'subtitle'

    另一种选择 - 最初搜索 tr 而不是 a 元素

    (//tr[.//a[text()='link5']]/preceding-sibling::tr//a[contains(@class,'subtitle')])[last()]
    

    希望它能帮助任何人获得逻辑 ob 构建定位器

    【讨论】:

    • 这仍然返回两个trs。
    • 请立即查看。整个定位器是(//a[text()='link5']/preceding::tr[.//a[@class='subtitle']])[last()]
    • 谢谢,这行得通。我实际上使用以下内容来做我需要的事情,但你的回答让我到了那里:(//a[@href='link1.pdf']/ancestor::tr/preceding-sibling::tr[.//a[@class='subtitle']])[last()]//a[@class='subtitle']
    • 是的,它都是可选的,这就是为什么我不写定位器,而是写它们是如何构建的逻辑
    【解决方案2】:

    尝试使用 xpath :

    //a[@href='&lt;your_input&gt;']/preceding-sibling::tr[.//a[@class='subtitle']][1]

    其中&lt;your_input&gt; 可以是link1.pdflink5.pdf

    【讨论】:

    • 谢谢,但如果不进行一些调整,这将无法正常工作。我不得不添加一个缺失的:/ancestor::tr/
    • 输入link5.pdf 代替your_input 时得到什么输出?
    • 是的,是的,我想我的第一个原始答案中有它,后来以某种方式删除了它。更新了答案。谢谢。
    猜你喜欢
    • 2011-01-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-07
    • 2010-11-12
    • 2016-04-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多