Jsoup 选择器:h2 之后的第 2 个 div

Jsoup selectors: 2nd div after h2

我有以下 HTML:

<html>
<body>

...

<h2> Blah Blah 1</h2>
<p>blah blah</p>
<div>
    <div>
        <table>
            <tbody>
                <tr><th>Col 1 Header</th><th>Col 2 Header</th></tr>
                <tr><td>Line 1.1 Value</td><td>Line 2.1 Header</td></tr>
                <tr><td>Line 2.1 Value</td><td>Line 2.2 Value</td></tr>
            </tbody>
        </table>
    </div>
</div>
<div>
    <div>
        <table>
            <tbody>
                <tr><th>Col 1 Header T2</th><th>Col 2 Header T2</th></tr>
                <tr><td>Line 1.1 Value T2</td><td>Line 2.1 Header T2</td></tr>
                <tr><td>Line 2.1 Value T2</td><td>Line 2.2 Value T2</td></tr>
                </tbody>
        </table>
    </div>
</div>

<h2> Blah Blah 2</h2>

<div>
    <div>
        <table>
            <tbody>
                <tr><th>XCol 1 Header</th><th>XCol 2 Header</th></tr>
                <tr><td>XLine 1.1 Value</td><td>XLine 2.1 Header</td></tr>
                <tr><td>XLine 2.1 Value</td><td>XLine 2.2 Value</td></tr>
            </tbody>
        </table>
    </div>
</div>
<p>blah blah</p>
<div>
    <div>
        <table>
            <tbody>
                <tr><th>XCol 1 Header T2</th><th>XCol 2 Header T2</th></tr>
                <tr><td>XLine 1.1 Value T2</td><td>XLine 2.1 Header T2</td></tr>
                <tr><td>XLine 2.1 Value T2</td><td>XLine 2.2 Value T2</td></tr>
                </tbody>
        </table>
    </div>
</div>

</body>
</html>

我想提取包含给定文本的 h2 标签后的第二个 DIV。

您可能会注意到,在第一个和第二个 div 中,p 标签不在同一位置。

要提取第一个 h2 之后的 DIV,可以使用以下公式:

h2:contains(Blah 1) + p + div +div

但是要提取第二个,将 "Blah 1" 替换为 "Blah 2" 将不起作用,因为“"p"”标记位于其他位置,因此静态选择器将是:

h2:contains(Blah 2) + div + p +div

我需要的是一个单一的选择器公式,无论 p 块在哪里,只要更改文本就可以使它起作用

我尝试了几种方法: 就像...选择器 nth-of-type 也不起作用,因为我只知道 DIV 的位置 wrt h2 不是 DIV 的父亲,而是前一个兄弟姐妹 ...

请帮忙

我有两个实现方法。
第一个是删除每个 <p> 然后你只需要 select "h2:contains(" + text + ")+div+div"。请小心,仅当您确定 <div> 不包含任何 <p> 时才使用它。否则会缺少一些内容。

    public void execute1(String html) {
        Document doc = Jsoup.parse(html);
        // first approach: remove every <p> to simplify document
        Elements paragraphs = doc.select("p");
        for (Element paragraph : paragraphs) {
            paragraph.remove();
        }
        // then one selector will return what you want in both cases
        System.out.println(selectSecondDivAfterH2WithText(doc, "Blah 1"));
        System.out.println(selectSecondDivAfterH2WithText(doc, "Blah 2"));
    }

    private Element selectSecondDivAfterH2WithText(Document doc, String text) {
        return doc.select("h2:contains(" + text + ")+div+div").first();
    }

第二种方法是遍历 "h2:contains(" + text+ ")" 和 "manually" 的兄弟姐妹,找到第二个 <div>,忽略其他任何东西。它更好,因为它不会破坏原始文档,并且会跳过任意​​数量的 <p> 个元素。

    public void execute2(String html) {
        Document doc = Jsoup.parse(html);
        System.out.println(selectSecondDivAfterH2WithText2(doc, "Blah 1"));
        System.out.println(selectSecondDivAfterH2WithText2(doc, "Blah 2"));
    }

    private Element selectSecondDivAfterH2WithText2(Document doc, String text) {
        int counter = 2;
        // find h2 with given text
        Element h2 = doc.select("h2:contains(" + text + ")").first();
        // select every sibling after this h2 element
        Elements siblings = h2.nextElementSiblings();
        // loop over them
        for (Element sibling : siblings) {
            // skip everything that's not a div
            if (sibling.tagName().equals("div")) {
                // count how many divs left to skip
                counter--;
                if (counter == 0) {
                    // return when found nth div
                    return sibling;
                }
            }
        }
        return null;
    }

我还有第三个使用 "h2:contains(" + text + ")~div:nth-of-type(2)" 的想法。它适用于第一种情况,但对于第二种情况可能会失败,因为 div 之间有一个 <p>

一种简单的方法是使用逗号 (,) 查询运算符,它在选择器之间执行 OR。因此,您可以结合 P 标签所在位置的两种变体。

h2:contains(Blah 2) + div ~ div, h2:contains(Blah 2) ~ div + div

这是 try.jsoup 操场上的示例。