HTMLParser使用詳解(3)- 通過Filter訪問內容


 HTMLParser使用詳解(3)- 通過Filter訪問內容


2009-03-12 19:45:10
 標籤:HTMLParser Java   [推送到技術圈
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也是根據字符串對不同的域進行判斷,與前面這些的區別主要就是支持正則表達式。這個不在本文的討論範圍以內,大家可以自己實驗一下。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章