熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> JSP教程 >> 正文

Java 實踐: 用 XQuery 進行屏幕搜集

2013-11-15 09:50:26  來源: JSP教程 

  上個月Java 技術講師 Sam Pullara 向我演示了他最新的支持 Java 的電話 Nokia 這個手機使用了全面的技術 —— 嵌入式 JVMGPRS 和藍牙但是它也遭遇了所有智能手機都苦惱的問題 —— 有限的屏幕實際使用區有些 Web 站點支持基於手機的浏覽器而且嵌入式浏覽器也試圖在小小的屏幕上有效地渲染頁面但是在電話屏幕上查看典型的 Web 頁面就像要把一頭大象強行塞進車後座一樣(其中的每個參與者都會感到失望包括您車和大象)Sam 構建了一個簡單的優雅的解決方案從他喜歡的 Web 站點上對數據進行屏幕搜集然後把數據重新格式化在小屏幕上顯示
  
  新方法
  從 HTML 文檔提取數據的方法有許多種但是我真的很喜歡 Sam 采用的方法既把 XQuery 當作屏幕搜集工具(從頁面中提取相當的數據)又把它當作樣式表工具(重新格式化數據以便數據適應頁面不需要進行頁面滾動)只要少量基礎設施和一些非常簡單的 XQuery 表達式就可以從大量數據源提取出相關數據 —— 例如交通天氣和財務報價等並在電話上完好地顯示數據
  
  我過去經常處於這種情況對 HTML 頁面進行屏幕搜集對某些特定問題來說似乎是可行的方案但是幾乎沒有用於屏幕搜集的 Java 工具包有許多 HTML 解析工具但它們通常缺少足夠的抽象能力(把屏幕搜集代碼弄得亂七八糟)大量不符合 HTML 規范的應用限制了它們它們也無法處理那些結構可能隨時間發生變化的動態生成的頁面
  
  為了彌補質量低下的 HTML 和豐富的 XML 處理工具之間的空白首先要把 HTML 轉換成 XML許多工具有助於完成這項工作JTidy 工具包做得很好可以使這項工作變得輕松一些JTidy 的設計目標是讀入典型質量(即很糟)的 HTML 並輸出更整潔的結果(有選項可供選擇)它還提供了一個 DOM 接口用來遍歷能夠發送給 XML 解析器的 HTML 文檔清單 中的代碼將從 InputStream 中讀取 HTML 文檔並生成文檔的 DOM 表示
  
  清單 用 JTidy 把 HTML 轉換成 XML 兼容的 DOM
  
  Tidy tidy = new Tidy();
  tidysetQuiet(true);
  tidysetShowWarnings(false);
  Document tidyDOM = tidyparseDOM(inputStream null);
  
  用這個簡單的轉換就差不多能把每個 Web 頁面都當作 XML 文檔進行處理還能用自己喜歡的任何 XML 工具(比如 SAXXSLXPath等等)提取數據雖然 XSL 可能是很明智的選擇(因為其設計目標就是為了從 XML 文檔中提取信息並轉換這些信息以便顯示它們)但是如果不了解 XSL 的話它的學習曲線就很難掌握即使是最簡單的 XSL 轉換也復雜得讓人心煩XPath 是處理信息提取的一個好選擇 —— XSL 和 XQuery 都用它進行內容選擇可以很容易地使用 XPath 把需要的數據提取出來然後對 HTML 進行格式化但是 XQuery 會讓這項工具更加容易
  
  XQuery簡介
  
  XQuery 的設計目標是從可能非常大的 XML 數據集中提取數據輸入的數據集不必是 XML 文檔雖然它可能是 XML 文檔但是也可能是已經編入索引並保存在 XML 數據庫中的文檔集合甚至是一組關系數據庫中的表像 SQL 一樣XQuery 包含從多個數據集中提取數據匯總數據聚合數據和連接數據的函數
  
  就像 JSPASP 或 Velocity 這樣的表示性模板語言一樣XQuery 把兩個域(表示域和計算域)中的元素組合成一種組合語法結果所有 XML 文檔都自動成為有效的 XQuery 表達式並對自身進行評估XQuery 還包含一些語言語句(language statement)例如forlet它們可以與 XML 元素混合使用
  
  清單 顯示了一個示例 XML 文檔 bibxml它表示一個書目然後我們將介紹一些快速的 XQuery 表達式讓您對 XQuery 能夠做什麼形成一種認識最後我們將再轉到屏幕搜集的示例上要全面介紹 XQuery 的語法和使用情況可能要用幾百頁的篇幅有關更詳細的參考材料和示例請參閱 參考資料 小節
  
  清單 示例 XML 書目
  
  <bib>
  <book year=>
    <title>TCP/IP Illustrated</title>
    <author><last>Stevens</last><first>W</first></author>
    <publisher>AddisonWesley</publisher>
    <price> </price>
  </book>
    more books
  </bib>
  
  清單 顯示了一個 XQuery 表達式它選擇 AddisonWesley 在 年以後出版的所有書籍提取它們的標題並把標題格式化成前面有項目符號的(<ul>)列表大括號表示從表示模式(數據直接傳遞到輸出 例如 <ul> 和 <li> 標簽)到代碼模式的切換然後在 return 子句之後立即進行從代碼模式表示模式的隱式切換
  
  清單 根據查詢參數選擇圖書標題的 XQuery 表達式
  
  <ul>
  {
   for $b in doc(bibxml)/bib/book
   where $b/publisher = AddisonWesley and $b/@year >
   return
  <li>{ data($b/title) }</li>
  }
  </ul>
  
  查詢語法引入了for通常稱之為Flower 表達式(來自 FLWOR是 forletwhereorderreturn 的縮寫)該語法從文檔中選擇一系列 XML 節點在該例中用 XPath 選取了來自 bibxml 文檔的 <book> 節點集然後進一步過濾出與指定查詢參數(出版商是 AddisonWesley出版日期是 年之後)匹配的節點對於選出的每個節點將在 return 子句中計算表達式在這裡是標記(<li> 標簽)與代碼(提取出每個 <book> 節點的 <title> 元素的內容)的混合
  
  這個簡單的 XQuery 示例描述了 XQuery 的幾個方面 —— 某一文檔中表示與代碼的混合XPath 的運用子條件的運用($b 引用)非凡的查詢表達式XQuery 函數(data())還有一個事實輸出文檔的結構不必與輸入文檔的結構匹配就在這個相當緊湊的讀起來不是很難的查詢中孕育著強大的處理能力
  
  清單 顯示了一個更簡單的 XQuery 表達式它把書目中不同出版商的數量在一個 <count> 元素中輸出像前一個示例一樣它用 XPath 表達式選擇一組節點然後用 XQuery 函數選擇惟一值並計算節點的數量它通過運算獲得一個數字 —— bibxml即文檔中不同出版商的數量
  
  清單 計算不同出版商數量的 XQuery 表達式
  
  <count>
  {
   let $d := distinctvalues(doc(bibxml)/book/publisher)
   return count($d)
  }
  </count>
  
  這些示例只是 XQuery 能夠執行的各種查詢類型的很少一部分提供這些例子僅僅是為了讓您對使用 XQuery 能夠做的事情有些感覺以及提示您如何才能用 XQuery 把 XML 文檔轉換成自己選擇的格式雖然 XQuery 的大部分功能主要用於查詢大型文檔或者其他數據源但是也可以使用 XQuery 非常簡單的子集來對 HTML 文檔進行屏幕搜集為各種應用程序提取出需要的數據例如在屏幕大小有限的設備(例如蜂窩電話)上顯示有關的數據或者創建一個 DIY 的門戶網站聚集並顯示來自多個站點的數據
  
  用 XQuery 進行屏幕搜集
  
  對 Web 頁面做屏幕搜集的許多挑戰之一是它們通常沒有可以自我標識的結構而且它們的結構可能隨著站點內容的編輯而變化甚至有可能根據不同的請求在頁面中插入不同的動態內容(例如廣告內容)因此對於頁面中哪一部分的內容與要提取的數據相對應通常不得不進行猜測
  
  股票價格
  
  現在讓我們從提取 Yahoo! 財經頁面中 IBM 股票的當前價格開始()這個頁面上有許多材料 —— 新聞標題廣告財經數據等等但是我想要的是股票的價格數據它放在一個表格單元格中靠近包含Last Trade的單元格清單 中的查詢語句將選擇所有文本內容中包含Last Trade的 <td> 節點然後為每個節點(希望只有一個)輸出一個包含後續<td> 節點內容的表格行內容是用 return 子句中的 data() 函數提取的否則不僅僅會得到 <td> 節點中的文本還會得到所有的標記(在這個查詢中惟一包含技巧的部分是 text()[] 這個部分在這裡text() 函數匹配的是 <td> 元素中的所有元素 —— 在這個例子只有一個元素但 XQuery 並不知道這一點 —— 所以必須進一步告訴它在進行文本匹配之前必須選擇第一個文本節點)只要頁面包含一個表格單元格的文本是Last Trade而且後續的單元格包含的是股票價格那麼即使頁面的結構隨意變化也不會造成查詢失敗
  
  清單 從 Yahoo! 財經提取股票報價的 XQuery 表達式
  
  <table>
  {
   for $d in //td
   where contains($d/text()[] Last Trade)
   return <tr><td> { data($d/followingsibling::td) } </td></tr>
  }
  </table>
  
  天氣
  
  現在來試一下另外一個頁面Yahoo! 天氣頁面包含許多 portlet 面板我想提取上面所列城市的名稱溫度和圖標(如果登錄 Yahoo! 天氣頁面 則屏幕上會顯示出在我的 Yahoo!中所選城市的天氣否則會顯示一些主要大城
From:http://tw.wingwit.com/Article/program/Java/JSP/201311/19203.html
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.