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

Posted

技术标签:

【中文标题】如何在 Jsoup 中生成匹配特定元素的 XPath 查询?【英文标题】:How to generate XPath query matching a specific element in Jsoup? 【发布时间】:2016-07-14 00:30:52 【问题描述】:

_嗨,这是我的网页:

<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

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

【问题讨论】:

【参考方案1】:

在这里问一个想法。 即使我很确定有更好的解决方案来获取给定节点的 xpath。例如,在answer 中使用 xslt 来“从 XML 节点 java 生成/获取 xpath”。

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

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

【讨论】:

【参考方案3】:

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

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

【讨论】:

你的答案接近我想要的,我只会做一些改变,它会完美地工作,因为现在它给出了这样的结果 div[1] = 文本 div 1 div[2]/span[1] = 第一个跨度的文本 div[2]/span[2] = 第二个跨度的文本 div[3] = 文本div 2 body/div[1] = text div 1 body/div[2]/span[1] = 第一个 span body/div[2]/span[2] = 第二个 span body/div[3] 的文本= text div 2 span[1] = 第一个 span 的文本 span[2] = 第二个 span 的文本

以上是关于如何在 Jsoup 中生成匹配特定元素的 XPath 查询?的主要内容,如果未能解决你的问题,请参考以下文章

在 Oracle 中生成 .Net 报价

使用特定规则在 Python 中生成排列

如何在 Java 中生成特定范围内的随机整数?

如何在 Java 中生成特定范围内的随机整数?

如何使用 Angular CLI 在特定文件夹中生成组件?

如何在swift中生成设备特定的唯一uuid