HTMLParser使用詳解(3)- 通過Filter訪問內容
HTMLParser遍歷了網頁的內容以後,以樹(森林)結構保存了結果。HTMLParser訪問結果內容的方法有兩種。使用Filter和使用Visitor。
(一)Filter類 顧名思義,Filter就是對於結果進行過濾,取得需要的內容。HTMLParser在org.htmlparser.filters包之內一共定義了16個不同的Filter,也可以分爲幾類。 判斷類Filter: TagNameFilter HasAttributeFilter HasChildFilter HasParentFilter HasSiblingFilter IsEqualFilter 邏輯運算Filter: AndFilter NotFilter OrFilter XorFilter 其他Filter: NodeClassFilter StringFilter LinkStringFilter LinkRegexFilter RegexFilter CssSelectorNodeFilter 所有的Filter類都實現了org.htmlparser.NodeFilter接口。這個接口只有一個主要函數: boolean accept (Node node); 各個子類分別實現這個函數,用於判斷輸入的Node是否符合這個Filter的過濾條件,如果符合,返回true,否則返回false。 (二)判斷類Filter 2.1 TagNameFilter TabNameFilter是最容易理解的一個Filter,根據Tag的名字進行過濾。 下面是用於測試的HTML文件: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<head><meta http-equiv="Content-Type" content="text/html; charset=gb2312"><title>白澤居-www.baizeju.com</title>< /head> <html xmlns="http://www.w3.org/1999/xhtml"> <body > <div id="top_main"> <div id="logoindex"> <!--這是註釋--> 白澤居-www.baizeju.com <a href="http://www.baizeju.com">白澤居-www.baizeju.com</a> </div> 白澤居-www.baizeju.com </div> </body> </html> 測試代碼:(這裏只列出了Main函數,全部代碼請參考 HTMLParser使用入門(2)- Node內容,自己添加import部分) public static void main(String[] args) {
try{ Parser parser = new Parser( (HttpURLConnection) (new URL("http://127.0.0.1:8080/HTMLParserTester.html")).openConnection() ); // 這裏是控制測試的部分,後面的例子修改的就是這個地方。 NodeFilter filter = new TagNameFilter ("DIV"); NodeList nodes = parser.extractAllNodesThatMatch(filter); if(nodes!=null) { for (int i = 0; i < nodes.size(); i++) { Node textnode = (Node) nodes.elementAt(i); message("getText:"+textnode.getText()); message("================================================="); } } } catch( Exception e ) { e.printStackTrace(); } } 輸出結果: getText:div id="top_main" ================================================= getText:div id="logoindex" ================================================= 可以看出文件中兩個Div節點都被取出了。下面可以針對這兩個DIV節點進行操作 2.2 HasChildFilter 下面讓我們看看HasChildFilter。剛剛看到這個Filter的時候,我想當然地認爲這個Filter返回的是有Child的Tag。直接初始化了一個 NodeFilter filter = new HasChildFilter(); 結 果調用NodeList nodes = parser.extractAllNodesThatMatch(filter);的時候HasChildFilter內部直接發生 NullPointerException。讀了一下HasChildFilter的代碼,才發現,實際HasChildFilter是返回有符合條件的 子節點的節點,需要另外一個Filter作爲過濾子節點的參數。缺省的構造函數雖然可以初始化,但是由於子節點的Filter是null,所以使用的時候 發生了Exception。從這點來看,HTMLParser的代碼還有很多可以優化的的地方。呵呵。 修改代碼: NodeFilter innerFilter = new TagNameFilter ("DIV"); NodeFilter filter = new HasChildFilter(innerFilter); NodeList nodes = parser.extractAllNodesThatMatch(filter); 輸出結果: getText:body ================================================= getText:div id="top_main" ================================================= 可以看到,輸出的是兩個有DIV子Tag的Tag節點。(body有子節點DIV "top_main","top_main"有子節點"logoindex"。 注意HasChildFilter還有一個構造函數: public HasChildFilter (NodeFilter filter, boolean recursive) 如果recursive是false,則只對第一級子節點進行過濾。比如前面的例子,body和top_main都是在第一級的子節點裏就有DIV節點,所以匹配上了。如果我們用下面的方法調用: NodeFilter filter = new HasChildFilter( innerFilter, true ); 輸出結果: getText:html xmlns="http://www.w3.org/1999/xhtml" ================================================= getText:body ================================================= getText:div id="top_main" ================================================= 可以看到輸出結果中多了一個html xmlns="http://www.w3.org/1999/xhtml",這個是整個HTML頁面的節點(根節點),雖然這個節點下直接沒有DIV節點,但是它的子節點body下面有DIV節點,所以它也被匹配上了。 2.3 HasAttributeFilter HasAttributeFilter有3個構造函數: public HasAttributeFilter (); public HasAttributeFilter (String attribute); public HasAttributeFilter (String attribute, String value); 這個Filter可以匹配出包含制定名字的屬性,或者制定屬性爲指定值的節點。還是用例子說明比較容易。 調用方法1: NodeFilter filter = new HasAttributeFilter(); NodeList nodes = parser.extractAllNodesThatMatch(filter); 輸出結果: 什麼也沒有輸出。 調用方法2: NodeFilter filter = new HasAttributeFilter( "id" ); NodeList nodes = parser.extractAllNodesThatMatch(filter); 輸出結果: getText:div id="top_main" ================================================= getText:div id="logoindex" ================================================= 調用方法3: NodeFilter filter = new HasAttributeFilter( "id", "logoindex" ); NodeList nodes = parser.extractAllNodesThatMatch(filter); 輸出結果: getText:div id="logoindex" ================================================= 很簡單吧。呵呵 2.4 其他判斷列Filter HasParentFilter和HasSiblingFilter的功能與HasChildFilter類似,大家自己試一下就應該瞭解了。 IsEqualFilter的構造函數參數是一個Node: public IsEqualFilter (Node node) { mNode = node; } accept函數也很簡單: public boolean accept (Node node) { return (mNode == node); } 不需要過多說明了。 (三)邏輯運算Filter 前面介紹的都是簡單的Filter,只能針對某種單一類型的條件進行過濾。HTMLParser支持對於簡單類型的Filter進行組合,從而實現複雜的條件。原理和一般編程語言的邏輯運算是一樣的。 3.1 AndFilter AndFilter可以把兩種Filter進行組合,只有同時滿足條件的Node纔會被過濾。 測試代碼: NodeFilter filterID = new HasAttributeFilter( "id" ); NodeFilter filterChild = new HasChildFilter(filterA); NodeFilter filter = new AndFilter(filterID, filterChild); 輸出結果: getText:div id="logoindex" ================================================= 3.2 OrFilter 把前面的AndFilter換成OrFilter 測試代碼: NodeFilter filterID = new HasAttributeFilter( "id" ); NodeFilter filterChild = new HasChildFilter(filterA); NodeFilter filter = new OrFilter(filterID, filterChild); 輸出結果: getText:div id="top_main" ================================================= getText:div id="logoindex" ================================================= 3.3 NotFilter 把前面的AndFilter換成NotFilter 測試代碼: NodeFilter filterID = new HasAttributeFilter( "id" ); NodeFilter filterChild = new HasChildFilter(filterA); NodeFilter filter = new NotFilter(new OrFilter(filterID, filterChild)); 輸出結果: getText:!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" ================================================= getText: ================================================= getText:head ================================================= getText:meta http-equiv="Content-Type" content="text/html; charset=gb2312" ================================================= getText:title ================================================= getText:白澤居-www.baizeju.com ================================================= getText:/title ================================================= getText:/head ================================================= getText: ================================================= getText:html xmlns="http://www.w3.org/1999/xhtml" ================================================= getText: ================================================= getText:body ================================================= getText: ================================================= getText: ================================================= getText: ================================================= getText:這是註釋 ================================================= getText: 白澤居-www.baizeju.com ================================================= getText:a href="http://www.baizeju.com" ================================================= getText:白澤居-www.baizeju.com ================================================= getText:/a ================================================= getText: ================================================= getText:/div ================================================= getText: 白澤居-www.baizeju.com ================================================= getText:/div ================================================= getText: ================================================= getText:/body ================================================= getText: ================================================= getText:/html ================================================= getText: ================================================= 除了前面3.2中輸出的幾個Tag,其餘的Tag都在這裏了。 3.4 XorFilter 把前面的AndFilter換成NotFilter 測試代碼: NodeFilter filterID = new HasAttributeFilter( "id" ); NodeFilter filterChild = new HasChildFilter(filterA); NodeFilter filter = new XorFilter(filterID, filterChild); 輸出結果: getText:div id="top_main" ================================================= (四)其他Filter: 4.1 NodeClassFilter 這個Filter用於判斷節點類型是否是某個特定的Node類型。在HTMLParser使用入門(2)- Node內容 中我們已經瞭解了Node的不同類型,這個Filter就可以針對類型進行過濾。 測試代碼: NodeFilter filter = new NodeClassFilter(RemarkNode.class); NodeList nodes = parser.extractAllNodesThatMatch(filter); 輸出結果: getText:這是註釋 ================================================= 可以看到只有RemarkNode(註釋)被輸出了。 4.2 StringFilter 這個Filter用於過濾顯示字符串中包含制定內容的Tag。注意是可顯示的字符串,不可顯示的字符串中的內容(例如註釋,鏈接等等)不會被顯示。 修改一下例子代碼: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <head><meta http-equiv="Content-Type" content="text/html; charset=gb2312"><title>白澤居-title-www.baizeju.com</title>& lt;/head> <html xmlns="http://www.w3.org/1999/xhtml"> <body > <div id="top_main"> <div id="logoindex"> <!--這是註釋 白澤居-www.baizeju.com --> 白澤居-字符串1-www.baizeju.com <a href="http://www.baizeju.com">白澤居-鏈接文本-www.baizeju.com</a> </div> 白澤居-字符串2-www.baizeju.com </div> </body> </html> 測試代碼: NodeFilter filter = new StringFilter("www.baizeju.com"); NodeList nodes = parser.extractAllNodesThatMatch(filter); 輸出結果: getText:白澤居-title-www.baizeju.com ================================================= getText: 白澤居-字符串1-www.baizeju.com ================================================= getText:白澤居-鏈接文本-www.baizeju.com ================================================= getText: 白澤居-字符串2-www.baizeju.com ================================================= 可以看到包含title,兩個內容字符串和鏈接的文本字符串的Tag都被輸出了,但是註釋和鏈接Tag本身沒有輸出。 4.3 LinkStringFilter 這個Filter用於判斷鏈接中是否包含某個特定的字符串,可以用來過濾出指向某個特定網站的鏈接。 測試代碼: NodeFilter filter = new LinkStringFilter("www.baizeju.com"); NodeList nodes = parser.extractAllNodesThatMatch(filter); 輸出結果: getText:a href="http://www.baizeju.com" ================================================= 4.4 其他幾個Filter 其他幾個Filter也是根據字符串對不同的域進行判斷,與前面這些的區別主要就是支持正則表達式。這個不在本文的討論範圍以內,大家可以自己實驗一下。 |