Селекторы Jsoup: 2-й див после h2 - PullRequest
2 голосов
/ 31 января 2020

У меня есть следующее 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>

Я хотел бы извлечь 2-й DIV после тега h2 , который содержит данный текст.

Как вы можете заметить, в первом и втором div теги p не находятся в одинаковом положении.

Чтобы извлечь DIV после первого h2, приведенная ниже формула будет работать:

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

Но для извлечения 2-го замена "Blah 1" на "Blah 2" не будет работать, так как тег "" p "находится в другом месте, поэтому селектор stati c будет:

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

И что мне нужно, это формула с одним селектором, где изменение текста заставит его работать, где бы p блоков не было

Я пробовал несколько способов: например .. Селектор nth-of-type также не будет работать, потому что я знаю, что положение DIV только с h2 , который не является отцом DIV, но предшествующим родным братом ...

Помогите пожалуйста

Ответы [ 2 ]

1 голос
/ 03 февраля 2020

Простой способ сделать это - использовать оператор запроса запятой (,), который делает OR между селекторами. Таким образом, вы можете объединить два варианта того, где находится тег P.

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

Вот пример на try.jsoup детской площадке.

1 голос
/ 01 февраля 2020

У меня есть две идеи, как этого добиться.
Первый - удалить каждые <p>, а затем вам нужно будет только выбрать "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+ ")" и "вручную" найти второй <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>.

...