2016-03-26 82 views
4

_嗨,這是我的網頁:如何生成匹配Jsoup中特定元素的XPath查詢?

<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 

請任何人給我想法如何得到這個結果:)。提前致謝。

回答

2

在這裏問一個想法。 即使我很確定有更好的解決方案來獲取給定節點的xpath。例如,使用xslt作爲answer中的「從XML節點java生成/獲取xpath」。

這裏可能的解決方案基於您當前的嘗試。

對於每個(父)元素檢查是否有多個元素具有這個名稱。 僞代碼:if (count (el.select('../' + el.nodeName()) > 1)
如果真計數preceding-sibling::具有相同的名稱,並添加1
count (el.select('preceding-sibling::' + el.nodeName()) +1

+0

是它的邏輯分析,我會盡力,謝謝:) – kivok94

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

烏爾答案是我想要的附近, – kivok94

+0

div [1] = text div 1 div [2]/span [1] =第一段的文本 div [2]/span [2] =第二段文本 div [3] =文本div 2 body/div [1] =文本div 1 body/div [2]/span [1] =第一範圍的文本 body/div [2]/span [2] =第二範圍的文本 body/div [3] = text div 2 span [1] = text第一跨度 span [2] =第二跨度文本 – kivok94

1

這是我解決這個問題:

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

似乎對我很好:) –