2011-05-24 107 views
1

我想用HtmlUnit解析Feedburner提要。 飼料是這一個:http://feeds.feedburner.com/alcoanewsreleases使用HtmlUnit在XPath中選擇默認名稱空間

從這個飼料我想讀的所有項目節點,所以通常是//item的XPath應該做的伎倆。不幸的是,在這種情況下不起作用。

Groovy代碼片段:

def page = webClient.getPage("http://feeds.feedburner.com/alcoanewsreleases") 
def elements = page.getByXPath("//item") 

的示例XML資訊:

<?xml version="1.0" encoding="UTF-8"?> 
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss1full.xsl"?> 
<?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?> 

<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns="http://purl.org/rss/1.0/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0"> 

[...SNIP...] 

<item rdf:about="http://www.alcoa.com/global/en/news/news_detail.asp?newsYear=2011&amp;pageID=20110518006002en"> 
    <title>Chris L. Ayers Named President, Alcoa Global Primary Products</title> 
    <dc:date>2011-05-18</dc:date 
    <link>http://feedproxy.google.com/~r/alcoanewsreleases/~3/PawvdhpJrkc/news_detail.asp</link> 
    <description>NEW YORK--(BUSINESS WIRE)--Alcoa (NYSE:AA) announced today that Chris L. Ayers has been named President of Alcoa’s Global Primary Products (GPP) business, effective May 18, 2011. Ayers, previously Chief Operating Officer of GPP, succeeds John Thuestad, who will be handling special projects for the Company. Ayers joined Alcoa in February 2010 as Chief Operating Officer of Alcoa Cast, Forged and Extruded Products, a new position. He was elected a Vice President of Alcoa in April 2010 and Executive</description> 
    <feedburner:origLink xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0">http://www.alcoa.com/global/en/news/news_detail.asp?newsYear=2010&amp;pageID=20100104006194en</feedburner:origLink> 
</item> 

[...SNIP...] 

</rdf:RDF> 

我懷疑這是與命名空間的問題,因爲這個文件有4個命名空間。的命名空間是

  • (這是默認值)的xmlns = 「http://purl.org/rss/1.0/」
  • 的xmlns:RDF =「http://www.w3.org/1999/02/22-rdf-syntax-ns#「
  • xmlns:dc =」http://purl.org/dc/elements/1.1/「
  • xmlns:feedburner =」http:// rssnamespace .org/feedburner/ext/1.0「

我嘗試過使用Nokogiri(這是另一個用於ruby腳本的XML解析器)。 與Nokogiri我可以只是我們的XPath //xmlns:item工作並返回從飼料中的所有節點。

我已經嘗試過與HtmlUnit相同的XPath,但它不起作用。

所以我想我可以將我的問題解釋爲: 如何從HtmlUnit的默認命名空間中選擇一個節點?

任何想法?

回答

3

從這個飼料我想讀的所有項目 節點,所以通常是//item的XPath 應該做的伎倆。不幸的是 在這種情況下不起作用。

在XPath,這意味着「選擇其本地名稱是item是沒有命名空間所有元素」。在RSS中,item元素必須位於命名空間中。因此,上述內容不應用於符合標準的XML解析器和XPath引擎。

有什麼困惑的是,在XML,<item>機構「的一個元素命名的項目是在默認命名空間,即不管默認的命名空間是在這個地方在文檔中範圍;」而在XPath中,「item」表示名稱空間中的一個元素,其名稱空間爲no。 (或者,你可以說,它意味着默認命名空間中的一個元素,但是除非你有辦法告訴XPath默認的命名空間是什麼,否則默認的命名空間不是命名空間。通常(總是?)在XPath 1.0中是沒有辦法的聲明XPath表達式的默認名稱空間。)

另一個讓初學者感到困惑的是,源XML文檔中的命名空間前綴映射不被XPath處理器視爲重要。在解析XML文檔時,會構建一個數據結構,用於記錄每個元素(和其他節點)的名稱和名稱空間。名稱空間使用的前綴,包括默認名稱空間的空白前綴,都被視爲單純的語法方便。更多關於這下面...

隨着引入nokogiri我可以只是我們 的XPath //xmlns:item其工作,並從進料 回報所有節點。

無論如何,它不是XPath。也許這是對它的一種Nokogiri擴展(非常方便,但它的語法實際上是違反直覺的)。

所以我想我可以那句我的問題 爲:我怎樣才能從與該的HtmlUnit默認 命名空間的節點?

讓我們將其理解爲:如何使用HtmlUnit選擇RSS項目元素?我是這樣說的,因爲RSS規範(實際上一般符合任何XML詞彙表規範)不需要它的元素將在默認名稱空間中。在您收到的示例中,情況恰好如此,但服務提供商可能會在明天改變這一點,並且仍然完全符合RSS。明天,服務提供者可以使用該命名空間的「rss」命名空間前綴;或任何其他任意前綴。什麼RSS 確實指定是其元素將在其中的名稱空間:其URI爲http://purl.org/rss/1.0/的名稱空間。

這有點像問:「我該如何編寫一個函數(用Javascript,C,Java等)來告訴我變量a的值?」通常一個函數不知道在調用者中使用了什麼變量名稱。它所知道的是它的論據的。如果您致電sqrt(4),您將得到與a = 4; sqrt(a)rumpelstiltzkin = 4; sqrt(rumpelstiltzkin)相同的答案。顯然,變量參數的名稱對函數調用的結果沒有直接影響。它只需要是一個具有正確值的變量的名稱。如果編譯器抱怨,因爲你寫了b = 4; return sqrt(b)而不是使用a,你會認爲編譯器是瘋了。只要你使用有效的標識符,它不應該關心變量名稱。

同樣,在處理RSS時,我們不應該關心使用哪個命名空間前綴,只要它是標識正確命名空間的前綴即可。它可以不是前綴(標識默認名稱空間)。

在XPath 2.0中,可以通配名稱空間。如果你知道你不需要命名空間來消除歧義,這非常方便。在這種情況下,您可以選擇//*:item。但是,我不認爲HTMLUnit支持XPath 2.0。同樣在XPath 2.0環境(如XSLT 2.0)中,您可以爲XPath表達式指定默認名稱空間,但這對HTMLUnit無幫助。

所以,你有兩個選擇:

  • 使用XPath表達式忽略的命名空間,如//*[local-name = 'item']

  • 的可靠的方法:註冊一個命名空間前綴http://purl.org/rss/1.0/和您的XPath表達式使用它://rss:item。接下來的問題就是,如何在HTMLUnit中註冊一個名稱空間前綴並將其傳遞給XPath處理器?我快速瀏覽了文檔,沒有找到任何設施來做這件事。

注意:我應該補充一點,以上是關於符合XPath處理器。我不知道XPath處理器HTMLUnit使用什麼。那裏有一些XPath處理器忽略了規範,並且讓世界更加困惑於每個人。

我看到here有人用下面的語法在默認命名空間的元素中的HtmlUnit:

//:item 

但我不會建議,原因有三:

  1. 這不是有效的XPath,所以你不能指望它與其他程序一起工作。

  2. 它只適用於將RSS名稱空間聲明爲默認名稱空間的RSS源。使用名稱空間前綴的RSS提要將導致上述操作失敗。

  3. 它將阻止您瞭解XML名稱空間如何真正起作用,並有助於保持不充分支持名稱空間的工具的現狀。

HTMLUnit主要是爲HTML設計的,所以XML的不完整處理是可以理解的。但聲稱支持XPath,然後不提供聲明名稱空間前綴的方法是bug。 HTMLUnit使用似乎是Xalan-J一部分的XPath包。該軟件包有ways to provide namespace mappings to XPath,但我不知道HTMLUnit是否公開該功能。

+0

謝謝您的** **非常詳細的解答! XPath的'//:item'確實可以和HtmlUnit一起工作,儘管不像你所描述的那樣推薦實踐。 – spier 2011-05-25 19:07:50

0

這聽起來很熟悉,我很確定我已經在HtmlUnit中成功地使用了名稱空間和XPath,但是當然我找不到代碼。我懷疑它只能用於HTML頁面:在你的例子中page參考是一個XmlPage,它有許多特定於命名空間的方法,所有這些方法在使用時都會拋出「未實現」異常。 :-(

HtmlUnit的當前版本(2.8)已有近一年的歷史,因此可能會在此期間完成一些工作來支持XML命名空間。"HtmlUnit Users" mailing list將是需要查找的地方。

同時,一如既往地有一種變通方法:

final XmlPage page = webClient.getPage("http://feeds.feedburner.com/alcoanewsreleases"); 

// no good 
List elements = page.getByXPath("//item"); 
System.out.println(elements.size()) ; 

// ugly, but it works 
DomElement de = (DomElement)page.getFirstByXPath("//rdf:RDF"); 
List<DomNode> items = new ArrayList<DomNode>() ; 
for(DomNode dn : de.getChildNodes()) 
{ 
    String name = dn.getLocalName() ; 
    if((name != null) && (name.equals("item"))) 
     items.add(dn) ; 
} 
System.out.println("found " + items.size()) ; 

噢男孩Java是痛苦在斯卡拉工作後... ;-)

相關問題