2009-11-04 45 views
8

我正在寫一個HTML解析器,它使用TagSoup將格式良好的結構傳遞給XMLSlurper。使用XmlSlurper:如何選擇子元素,同時迭代GPathResult

這裏的通用代碼:

def htmlText = """ 
<html> 
<body> 
<div id="divId" class="divclass"> 
<h2>Heading 2</h2> 
<ol> 
<li><h3><a class="box" href="#href1">href1 link text</a> <span>extra stuff</span></h3><address>Here is the address<span>Telephone number: <strong>telephone</strong></span></address></li> 
<li><h3><a class="box" href="#href2">href2 link text</a> <span>extra stuff</span></h3><address>Here is another address<span>Another telephone: <strong>0845 1111111</strong></span></address></li> 
</ol> 
</div> 
</body> 
</html> 
"""  

def html = new XmlSlurper(new org.ccil.cowan.tagsoup.Parser()).parseText(htmlText); 

html.'**'.grep { [email protected] == 'divclass' }.ol.li.each { linkItem -> 
    def link = [email protected] 
    def address = linkItem.address.text() 
    println "$link: $address\n" 
} 

我希望每個讓我依次選擇每個「禮」這樣我就可以獲取相應的href和詳細地址。相反,我得到這樣的輸出:

#href1#href2: Here is the addressTelephone number: telephoneHere is another addressAnother telephone: 0845 1111111 

我檢查網絡上的各種例子,這些無論是處理XML,或者是一個班輪的例子,如「檢索該文件的所有鏈接」。似乎it.h3.a. @ href表達式正在收集文本中的所有hrefs,即使我將它傳遞給父'li'節點的引用。

你可以讓我知道:

  • 爲什麼我越來越顯示
  • 我怎樣才能檢索HREF /地址對每一個「禮」項目

由於輸出。

回答

11

與查找替換的grep:

html.'**'.find { [email protected] == 'divclass' }.ol.li.each { linkItem -> 
    def link = [email protected] 
    def address = linkItem.address.text() 
    println "$link: $address\n" 
} 

,那麼你會得到

#href1: Here is the addressTelephone number: telephone 

#href2: Here is another addressAnother telephone: 0845 1111111 

的grep返回一個ArrayList卻發現回報NodeChild類:

println html.'**'.grep { [email protected] == 'divclass' }.getClass() 
println html.'**'.find { [email protected] == 'divclass' }.getClass() 

結果:

class java.util.ArrayList 
class groovy.util.slurpersupport.NodeChild 

因此,如果你想使用grep你可以再嵌套另一個每個這樣的,它的工作

html.'**'.grep { [email protected] == 'divclass' }.ol.li.each { 
    it.each { linkItem -> 
     def link = [email protected] 
     def address = linkItem.address.text() 
     println "$link: $address\n" 
    } 
} 

長話短說,你的情況,使用find而不是grep的。

+0

優秀的答案! – 2009-11-09 12:23:54

1

這是一個棘手的問題。當class ='divclass'只有一個元素時,以前的答案肯定沒問題。如果grep有多個結果,那麼單個結果的find()不是答案。指出結果是一個ArrayList是正確的。插入一個外部嵌套的.each()循環將在封閉參數div中提供一個GPathResult。從這裏開始下鑽可以繼續獲得預期的結果。

html."**".grep { [email protected] == 'divclass' }.each { div -> div.ol.li.each { linkItem -> 
    def link = [email protected] 
    def address = linkItem.address.text() 
    println "$link: $address\n" 
}} 

原始代碼的行爲也可以使用更多的解釋。當在Groovy中的List上訪問屬性時,您將得到一個新列表(相同大小),並且列表中的每個元素的屬性都相同。 grep()找到的列表只有一個條目。然後我們得到一個房產ol,這很好。接下來我們得到ol.it的結果。它是一個size()== 1的列表,但是這次輸入size()== 2。我們可以有申請外循環,並得到相同的結果,如果我們想:

html."**".grep { [email protected] == 'divclass' }.ol.li.each { it.each { linkItem -> 
    def link = [email protected] 
    def address = linkItem.address 
    println "$link: $address\n" 
}} 

在任何GPathResult代表多個節點,我們得到的所有文字串聯。那是原來的結果,首先爲@href,然後爲地址

0

我相信以前的答案在撰寫本文時使用的版本都是正確的。但是我在Groovy 2.3.7中使用HTTPBuilder 0.7.1和Grails 2.4.4,並且存在一個大問題 - 將HTML元素轉換爲大寫。看來,這是由於NekoHTML引擎蓋下使用:

http://nekohtml.sourceforge.net/faq.html#uppercase

正因爲如此,在接受答案的解決方案必須被寫爲:

html.'**'.find { [email protected] == 'divclass' }.OL.LI.each { linkItem -> 
    def link = [email protected] 
    def address = linkItem.ADDRESS.text() 
    println "$link: $address\n" 
} 

這是非常令人沮喪的調試希望它能幫助別人。