如何在 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() ) > 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 查询?的主要内容,如果未能解决你的问题,请参考以下文章