html 如何在Jsoup中生成匹配特定元素的XPath查询?

liwlm1x9  于 2022-11-20  发布在  其他
关注(0)|答案(4)|浏览(261)

您好,这是我的网页(_H):

<html>
    <head>
    </head>
    <body>
        <div> text div 1</div>
        <div>
            <span>text of first span </span>
            <span>text of second span </span>
        </div>
        <div> text div 3 </div>
    </body>
</html>

我使用jsoup来解析它,然后浏览页面中的所有元素并获取它们的路径:

Document doc = Jsoup.parse(new File("C:\\Users\\HC\\Desktop\\dataset\\index.html"), "UTF-8");
 Elements elements = doc.body().select("*");
ArrayList all = new ArrayList();
        for (Element element : elements) {
            if (!element.ownText().isEmpty()) {

                StringBuilder path = new StringBuilder(element.nodeName());
                String value = element.ownText();
                Elements p_el = element.parents();

                for (Element el : p_el) {
                    path.insert(0, el.nodeName() + '/');
                }
                all.add(path + " = " + value + "\n");
                System.out.println(path +" = "+ value);
            }
        }

        return all;

我代码给予了以下结果:

html/body/div = text div 1
html/body/div/span = text of first span
html/body/div/span = text of second span
html/body/div = text div 3

其实我想得到这样结果:

html/body/div[1] = text div 1
html/body/div[2]/span[1] = text of first span
html/body/div[2]/span[2] = text of second span
html/body/div[3] = text div 3

请任何一个人能给予我的想法如何达到这个结果:)。提前感谢。

aurhwmvo

aurhwmvo1#

就像这里问的一个想法。即使我很确定有更好的解决方案来获得给定节点的xpath。例如,在answer中使用xslt来“从XML节点java生成/获得xpath”。
这里可能的解决方案基于您目前的尝试。
对于每个(父)元素,检查是否有多个元素具有此名称。伪代码:if ( count (el.select('../' + el.nodeName() ) > 1)
如果为true,则对具有相同名称的preceding-sibling::进行计数并加1。
count (el.select('preceding-sibling::' + el.nodeName() ) +1

snvhrwxg

snvhrwxg2#

这是我对这个问题的解决办法:

StringBuilder absPath=new StringBuilder();
Elements parents = htmlElement.parents();

for (int j = parents.size()-1; j >= 0; j--) {
    Element element = parents.get(j);
    absPath.append("/");
    absPath.append(element.tagName());
    absPath.append("[");
    absPath.append(element.siblingIndex());
    absPath.append("]");
}
jgwigjjp

jgwigjjp3#

如果你从根到叶遍历文档,而不是从叶到根遍历文档,这会更容易。这样你就可以很容易地按标记名对元素进行分组,并相应地处理多个出现的元素。下面是一个递归方法:

private final List<String> path = new ArrayList<>();
private final List<String> all = new ArrayList<>();

public List<String> getAll() {
    return Collections.unmodifiableList(all);
}

public void parse(Document doc) {
    path.clear();
    all.clear();
    parse(doc.children());
}

private void parse(List<Element> elements) {
    if (elements.isEmpty()) {
        return;
    }
    Map<String, List<Element>> grouped = elements.stream().collect(Collectors.groupingBy(Element::tagName));

    for (Map.Entry<String, List<Element>> entry : grouped.entrySet()) {
        List<Element> list = entry.getValue();
        String key = entry.getKey();
        if (list.size() > 1) {
            int index = 1;
            // use paths with index
            key += "[";
            for (Element e : list) {
                path.add(key + (index++) + "]");
                handleElement(e);
                path.remove(path.size() - 1);
            }
        } else {
            // use paths without index
            path.add(key);
            handleElement(list.get(0));
            path.remove(path.size() - 1);
        }
    }

}

private void handleElement(Element e) {
    String value = e.ownText();
    if (!value.isEmpty()) {
        // add entry
        all.add(path.stream().collect(Collectors.joining("/")) + " = " + value);
    }
    // process children of element
    parse(e.children());
}
mkshixfv

mkshixfv4#

这是Kotlin的答案。它是正确的,而且有效。其他的答案都是错误的,让我损失了几个小时的工作。

fun Element.xpath(): String = buildString {
    val parents = parents()

    for (j in (parents.size - 1) downTo 0) {
        val parent = parents[j]
        append("/*[")
        append(parent.siblingIndex() + 1)
        append(']')
    }

    append("/*[")
    append(siblingIndex() + 1)
    append(']')
}

相关问题