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

时间:2016-03-26 09:30:09

标签: java html dom xpath jsoup

_嗨,这是我的网页:

<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

请任何人都可以告诉我如何达到这个结果:)。提前致谢。

3 个答案:

答案 0 :(得分:2)

这里有一个想法。 即使我非常确定有更好的解决方案来获取给定节点的xpath。例如,使用xslt,如answer到&#34;从XML节点java&#34;生成/获取xpath。

这是基于您当前尝试的可能解决方案。

对于每个(父)元素,检查是否有多个具有此名称的元素。 伪代码:if ( count (el.select('../' + el.nodeName() ) > 1)
如果为true,则计算具有相同名称的preceding-sibling::并添加1.
count (el.select('preceding-sibling::' + el.nodeName() ) +1

答案 1 :(得分:2)

这是我解决这个问题的方法:

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("]");
}

答案 2 :(得分:0)

如果您遍历文档从根到叶子而不是相反的方向,这将更容易。这样,您可以轻松地按标记名称对元素进行分组,并相应地处理多个出现的元素。这是一种递归方法:

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());
}