2014-09-21 71 views
1

背景

許多問題詢問如何獲得給定CSS選擇器的特定DOM element。這個問題是關於相反的方向。文檔進行解析jsoup,但可以很容易地轉換爲任何的:基於元素實例的派生CSS選擇器

使用案例

對於一個特定的問題域(例如,化學化合物),成千上萬的網頁以類似的方式列出了化學品,但各網站的標記不盡相同。例如:

<div id="chemical-list"> 
    <div class="compound"> 
    <span class="compound-name">water</span> 
    <span class="compound-periodic">H2O</span> 
    </div> 
    <div class="compound"> 
    <span class="compound-name">sodium hypochlorite</span> 
    <span class="compound-periodic">NaClO</span> 
    </div> 
</div> 

另一個網站可能列出它們是不同的:

<ul class="chemical-compound"> 
    <li class="chem-name">water, H2O</li> 
    <li class="chem-name">sodium hypochlorite, NaClO</li> 
</ul> 

另一個網站可能會再次使用不同的標記:

<table border="0" cellpadding="0" cellspacing="0"> 
    <tbody> 
    <tr><td>water</td><td>H2O</td></tr> 
    <tr><td>sodium hypochlorite</td><td>NaClO</td></tr> 
    </tbody> 
</table> 

幾個示例頁面從每個數以千計的網站被下載。然後,使用現有的化學品列表,檢索候選網頁元素列表相對簡單。使用jsoup,這很簡單:

Elements elements = chemicals.getElementsMatchingOwnText(chemicalNames); 

這將允許跨越數千頁的高精度分析。 (該頁面可以討論次氯酸鈉和次氯酸鈉的應用,但是隻有該清單正在分析中。)瞭解CSS將極大地簡化分析並提高其準確性。

另一種方法是處理整個頁面,查找化學物質的「組」,然後嘗試提取列表。這兩個問題都很難,但使用CSS選擇器跳轉到頁面中的確切位置要高效得多,而且可能要準確得多。這兩個問題都需要一些手工操作,但我想盡可能自動化。

問題

上述API不出現有生成給予Element instance(越獨特越好)CSS選擇器的方法。可以迭代父元素並手動生成選擇器。這已經是demonstrated使用JavaScript在幾個。還有生成XPath的答案,並且可能使用Selenium

具體來說,你會怎麼做:

String selector = element.getCSSPath(); 
Elements elements = document.select(selector); 

這會:

  1. 返回CSS選擇給定元素。
  2. 搜索給定CSS選擇器的文檔。
  3. 返回與選擇器匹配的元素列表。

第二行不是問題;第一行是有問題的。

問題

什麼API可以從DOM元素生成CSS選擇器(儘可能唯一)?

如果沒有現有的API,那麼很高興知道。

+0

Jsoup不提供這一點,但如果它這樣做了,最獨特的選擇將是使用'>'和'選擇:EQ()'模仿XPath表達式。目前還不清楚它的用途是什麼 - 它將精確地選擇該元素,而不再是其他內容,所以您的示例代碼將毫無用處。這種API的實際用例是什麼? – 2014-09-21 20:08:19

+0

[獲取元素的CSS選擇器(當它沒有id時)](https://stackoverflow.com/questions/4588119/get-elements-css-selector-when-it-doesnt-have-an -id) – 10basetom 2017-05-31 12:44:55

回答

0

據我所知,沒有API提供這種功能。以下似乎工作:

/** 
    * Returns the shortest CSS path identify a given element. Note that this 
    * will not return a unique element, but can be used to obtain all elements 
    * that match the selector returned. 
    * 
    * @param cssElement The element that must be identified by its CSS selector. 
    * @return The CSS selector for the given element, or the empty string if 
    * no selector is found. 
    */ 
    private String cssPath(Element cssElement) { 
    StringBuilder result = new StringBuilder(256); 

    String id = cssElement.id(); 

    // If the element has an ID, then return it as the shortest path (IDs are 
    // supposed to be unique). 
    if(id.length() > 0) { 
     // This will break the chain of recursion. 
     result.append('#').append(id); 
    } 
    else { 
     Element parent = cssElement.parent(); 

     // If there is a parent node, then recurse to determine its CSS path. 
     // Otherwise, the chain of recursion ends here. 
     if(parent != null) { 
     result.append(cssPath(parent)); 
     } 

     // Generate a CSS path using the element's tag name and classes. 
     if(cssElement.className().length() > 0) { 
     result.append(" > ").append(cssElement.tagName()); 
     Set<String> cssClasses = cssElement.classNames(); 
     cssClasses.forEach(c -> result.append('.').append(c)); 
     result.append(' '); 
     } 
    } 

    // Return the (possibly incomplete) CSS selector through recursion.   
    return result.toString(); 
    } 
1

只需使用Java的actual JavaScript engine並運行一些普通的JavaScript?

function getSelector(element) { 
    var selector = element.id; 

    // if we have an ID, that's all we need. IDs are unique. The end. 
    if(selector.id) { return "#" + selector; } 

    selector = []; 
    var cl; 
    while(element.parentNode) { 
    cl = element.getAttribute("class"); 
    cl = cl ? "." + cl.trim().replace(/ +/g,'.') : ''; 
    selector.push(element.localName + cl); 
    element = element.parentNode; 
    } 
    return selector.reverse().join(' '); 
} 

而且我們確認對

<div class="main"> 
    <ul class=" list of things"> 
    <li><a href="moo" class="link">lol</a></li> 
    </ul> 
</div> 

var a = document.querySelector("a"); 
console.log(getSelector(a)); 

http://jsfiddle.net/c8k6Lxtj/ - 結果:html body div.main ul.list.of.things li a.link ...金。

+0

謝謝,邁克。我用這個來創建一個(遞歸的)[純Java解決方案](http://stackoverflow.com/a/25982121/59087)。 – 2014-09-22 19:55:03

+0

在許多情況下,這不會返回唯一的選擇器。例如,看到'getSelector2()'控制檯輸出在這裏:https://codepen.io/thdoan/pen/WjVRyG?editors=1111 – 10basetom 2017-05-31 10:20:33

+0

我不明白爲什麼這將是一個問題,只要*映射*是穩定。 – 2017-05-31 17:42:24

1

我用邁克的回答以下更改,使返回的CSS選擇器,更短。

更新:同樣使用name屬性,縮短CSS選擇器和檢查每個迭代如果選擇,到目前爲止,返回的頁面上的單個元素

更新:由於@ 10basetom在評論中指出,在情況其中元素沒有唯一的id或唯一的類名稱或唯一的類名稱+ name屬性,該方法可能會生成一個非唯一的css路徑,但在其他情況下會生成最短的css選擇器。所以,我建議使用document.querySelectorAll(result).length === 1檢查CSS路徑結果和後備其他方法described here

function getShortestSelector(element) { 
    var selector = element.id; 

    // if we have an ID, that's all we need. IDs are unique. The end. 
    if(selector.id) { 
     return "#" + selector; 
    } 

    selector = []; 
    var cl, name; 
    while(element.parentNode && (selector.length === 0 || document.querySelectorAll(selector.join(' ')).length !== 1)) { 

     // if exist, add the first found id and finish building the selector 
     var id = element.getAttribute("id"); 
     if (id) { 
      selector.unshift("#" + id); 
      break; 
     } 

     cl = element.getAttribute("class"); 
     cl = cl ? "." + cl.trim().replace(/ +/g,'.') : ''; 
     name = element.getAttribute("name"); 
     name = name ? ("[name=" + name.trim() + "]") : ''; 
     selector.unshift(element.localName + cl + name); 
     element = element.parentNode; 
    } 

    var result = selector[0]; 
    if (selector.length > 1) { 
     result += " " + selector.slice(1).join(" ").replace(/\[name=[^\]]*]/g, ''); 
    } 

    return result; 
} 
+0

這在許多情況下不起作用。例如,請參閱'getSelector1()'控制檯輸出:https://codepen.io/thdoan/pen/WjVRyG?editors=1111 – 10basetom 2017-05-31 10:19:27

0

由於2014年9月28日/ 1.8.1 JSoup已經有了這個功能(多虧了pull request)通過方法Element.cssSelector()

cssSelector

public String cssSelector() - 獲取CSS選擇將 唯一選擇這個元素。如果元素具有ID,則返回#id; 否則返回父(如果有的話)CSS選擇器,隨後 '>', 隨後爲元素 一個唯一的選擇器(tag.class.class:第n個孩子(n)的)。

返回:可用於檢索的 選擇元素的CSS 路徑

這將返回使用元素ID(如果存在),否則創建形式tag.class.class的選擇器返回一個唯一的元件選擇器:第n個孩子(n)的

如:"html > body > h2.section:nth-child(3)"