【java】【HtmlParser】HtmlParser使用


HTMLParser的核心模塊是org.htmlparser.Parser類,這個類實際完成了對於HTML頁面的分析工作。這個類有下面幾個構造函數:

public Parser ();
public Parser (Lexer lexer, ParserFeedback fb);
public Parser (URLConnection connection, ParserFeedback fb) throws ParserException;
public Parser (String resource, ParserFeedback feedback) throws ParserException;
public Parser (String resource) throws ParserException;
public Parser (Lexer lexer);
public Parser (URLConnection connection) throws ParserException;
public static Parser createParser (String html, String charset);//靜態類

 

Node中包含的方法有幾類:
對於樹型結構進行遍歷的函數,這些函數最容易理解:
Node
getParent ()取得父節點
NodeList
getChildren ()取得子節點的列表
Node
getFirstChild ()取得第一個子節點
Node
getLastChild ()取得最后一個子節點
Node
getPreviousSibling ()取得前一個兄弟(不好意思,英文是兄弟姐妹,直譯太麻煩而且不符合習慣,對不起女同胞了)
Node
getNextSibling ()取得下一個兄弟節點
取得Node內容的函數
String
getText ()取得文本
String
toPlainTextString()取得純文本信息
String
toHtml () 取得HTML信息(原始HTML
String
toHtml (boolean verbatim)取得HTML信息(原始HTML
String
toString ()取得字符串信息(原始HTML
Page
getPage ()取得這個Node對應的Page對象
int
getStartPosition ()取得這個NodeHTML頁面中的起始位置
int
getEndPosition ()取得這個NodeHTML頁面中的結束位置
用於Filter過濾的函數:
void
collectInto (NodeList list, NodeFilter filter)基於filter的條件對於這個節點進行過濾,符合條件的節點放到list中。
用於Visitor遍歷的函數:
void
accept (NodeVisitor visitor)對這個Node應用visitor
用於修改內容的函數,這類用得比較少
void
setPage (Page page)設置這個Node對應的Page對象
void
setText (String text)設置文本
void
setChildren (NodeList children)設置子節點列表
其他函數
void
doSemanticAction ()執行這個Node對應的操作(只有少數Tag有對應的操作)
Object
clone ()接口Clone的抽象函數。

類框架圖:

AbstractNodes是Node的直接子類,也是一個抽象類。它的三個直接子類實現是RemarkNode,用於保存注釋。在輸出結果的toString部分中可以看到有一個"Rem (345[6,2],356[6,13]): 這是注釋",就是一個RemarkNode。TextNode也很簡單,就是用戶可見的文字信息。TagNode是最復雜的,包含了HTML語言中的所有標簽,而且可以擴展(擴展 HTMLParser 對自定義標簽的處理能力)。TagNode包含兩類,一類是簡單的Tag,實際就是不能包含其他Tag的標簽,只能做葉子節點。另一類是CompositeTag,就是可以包含其他Tag,是分支節點

HTMLParser遍歷了網頁的內容以后,以樹(森林)結構保存了結果。HTMLParser訪問結果內容的方法有兩種。使用Filter和使用Visitor。

 

實際我們用HTMLParser最多的是處理HTML頁面,FilterVisitor相關的函數是必須的,然后第一類和第二類函數是用得最多的。第一類函數比較容易理解,下面用例子說明一下第二類函數。

下面是用於測試的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>

 

 測試代碼:

/**
* @author www.baizeju.com
*/

package com.baizeju.htmlparsertester;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.FileInputStream;
import java.io.File;
import java.net.HttpURLConnection;
import java.net.URL;

import org.htmlparser.Node;
import org.htmlparser.util.NodeIterator;
import org.htmlparser.Parser;

/**
* @author www.baizeju.com
*/
public class Main {
    private static String ENCODE = "GBK";
    private static void message( String szMsg ) {
        try{ System.out.println(new String(szMsg.getBytes(ENCODE), System.getProperty("file.encoding"))); }     catch(Exception e ){}
    }
    public static String openFile( String szFileName ) {
        try {
            BufferedReader bis = new BufferedReader(new InputStreamReader(new FileInputStream( new File(szFileName)),    ENCODE) );
            String szContent="";
            String szTemp;
            
            while ( (szTemp = bis.readLine()) != null) {
                szContent+=szTemp+"\n";
            }
            bis.close();
            return szContent;
        }
        catch( Exception e ) {
            return "";
        }
    }
    
   public static void main(String[] args) {
        
        try{
            Parser parser = new Parser( (HttpURLConnection) (new URL("http://127.0.0.1:8080/HTMLParserTester.html")).openConnection() );
        
            for (NodeIterator i = parser.elements (); i.hasMoreNodes(); ) {
                Node node = i.nextNode();
                message("getText:"+node.getText());
                message("getPlainText:"+node.toPlainTextString());
                message("toHtml:"+node.toHtml());
                message("toHtml(true):"+node.toHtml(true));
                message("toHtml(false):"+node.toHtml(false));
                message("toString:"+node.toString());
                message("=================================================");
            }            
        }
        catch( Exception e ) {     
            System.out.println( "Exception:"+e );
        }
    }
}

 輸出結果:

 

getText:!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
getPlainText:
toHtml:<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
toHtml(true):<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
toHtml(false):<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
toString:Doctype Tag : !DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd; begins at : 0; ends at : 121
=================================================
getText:

getPlainText:

toHtml:

toHtml(true):

toHtml(false):

toString:Txt (121[0,121],123[1,0]): \n
=================================================
getText:head
getPlainText:白澤居-www.baizeju.com
toHtml:<head><meta http-equiv="Content-Type" content="text/html; charset=gb2312"><title>白澤居-www.baizeju.com</title></head>
toHtml(true):<head><meta http-equiv="Content-Type" content="text/html; charset=gb2312"><title>白澤居-www.baizeju.com</title></head>
toHtml(false):<head><meta http-equiv="Content-Type" content="text/html; charset=gb2312"><title>白澤居-www.baizeju.com</title></head>
toString:HEAD: Tag (123[1,0],129[1,6]): head
Tag (129[1,6],197[1,74]): meta http-equiv="Content-Type" content="text/html; ...
Tag (197[1,74],204[1,81]): title
    Txt (204[1,81],223[1,100]): 白澤居-www.baizeju.com
    End (223[1,100],231[1,108]): /title
End (231[1,108],238[1,115]): /head

=================================================
getText:

getPlainText:

toHtml:

toHtml(true):

toHtml(false):

toString:Txt (238[1,115],240[2,0]): \n
=================================================
getText:html xmlns="http://www.w3.org/1999/xhtml"
getPlainText:


        
                
                白澤居-www.baizeju.com
白澤居-www.baizeju.com
        
        白澤居-www.baizeju.com



toHtml:<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>
toHtml(true):<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>
toHtml(false):<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>
toString:Tag (240[2,0],283[2,43]): html xmlns="http://www.w3.org/1999/xhtml"
Txt (283[2,43],285[3,0]): \n
Tag (285[3,0],292[3,7]): body 
    Txt (292[3,7],294[4,0]): \n
    Tag (294[4,0],313[4,19]): div id="top_main"
      Txt (313[4,19],316[5,1]): \n\t
      Tag (316[5,1],336[5,21]): div id="logoindex"
        Txt (336[5,21],340[6,2]): \n\t\t
        Rem (340[6,2],351[6,13]): 這是注釋
        Txt (351[6,13],376[8,0]): \n\t\t白澤居-www.baizeju.com\n
        Tag (376[8,0],409[8,33]): a href="http://www.baizeju.com"
          Txt (409[8,33],428[8,52]): 白澤居-www.baizeju.com
          End (428[8,52],432[8,56]): /a
        Txt (432[8,56],435[9,1]): \n\t
        End (435[9,1],441[9,7]): /div
      Txt (441[9,7],465[11,0]): \n\t白澤居-www.baizeju.com\n
      End (465[11,0],471[11,6]): /div
    Txt (471[11,6],473[12,0]): \n
    End (473[12,0],480[12,7]): /body
Txt (480[12,7],482[13,0]): \n
End (482[13,0],489[13,7]): /html

=================================================

 對於第一個Node的內容,對應的就是第一行<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">,這個比較好理解。
從這個輸出結果中,也可以看出內容的樹狀結構。或者說是樹林結構。在Page內容的第一層Tag,如DOCTYPEheadhtml,分別形成了一個最高層的Node節點(很多人可能對第二個和第四個Node的內容有點奇怪。實際上這兩個Node就是兩個換行符號。HTMLParserHTML頁面內容中的所有換行,空格,Tab等都轉換成了相應的Tag,所以就出現了這樣的Node。雖然內容少但是級別高,呵呵)
getPlainTextString
是把用戶可以看到的內容都包含了。有趣的有兩點,一是<head>標簽中的Title內容是在plainText中的,可能在標題中可見的也算可見吧。另外就是象前面說的,HTML內容中的換行符什么的,也都成了plainText,這個邏輯上好像有點問題。

另外可能大家發現toHtmltoHtml(true)toHtml(false)的結果沒什么區別。實際也是這樣的,如果跟蹤HTMLParser的代碼就可以發現,Node的子類是AbstractNode,其中實現了toHtml()的代碼,直接調用toHtml(false),而AbstractNode的三個子類RemarkNodeTagNodeTextNode中,toHtml(boolean verbatim)的實現中,都沒有處理verbatim參數,所以三個函數的結果是一模一樣的。如果你不需要實現你自己的什么特殊處理,簡單使用toHtml就可以了。

 

(一)Filter
顧名思義,Filter就是對於結果進行過濾,取得需要的內容。HTMLParserorg.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返回的是有ChildTag。直接初始化了一個
NodeFilter filter = new HasChildFilter();
結果調用NodeList nodes = parser.extractAllNodesThatMatch(filter);的時候HasChildFilter內部直接發生NullPointerException。讀了一下HasChildFilter的代碼,才發現,實際HasChildFilter是返回有符合條件的子節點的節點,需要另外一個Filter作為過濾子節點的參數。缺省的構造函數雖然可以初始化,但是由於子節點的Filternull,所以使用的時候發生了Exception。從這點來看,HTMLParser的代碼還有很多可以優化的的地方。呵呵。

修改代碼:
NodeFilter innerFilter = new TagNameFilter ("DIV");
NodeFilter filter = new HasChildFilter(innerFilter);
NodeList nodes = parser.extractAllNodesThatMatch(filter);
輸出結果:
getText:body 
=================================================
getText:div id="top_main"
=================================================
可以看到,輸出的是兩個有DIVTagTag節點。(body有子節點DIV "top_main""top_main"有子節點"logoindex"

注意HasChildFilter還有一個構造函數:
public HasChildFilter (NodeFilter filter, boolean recursive)
如果recursivefalse,則只對第一級子節點進行過濾。比如前面的例子,bodytop_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
HasAttributeFilter3個構造函數:
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></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也是根據字符串對不同的域進行判斷,與前面這些的區別主要就是支持正則表達式。這個不在本文的討論范圍以內,大家可以自己實驗一下。

 

 

(五)visitor

HTMLParser遍歷了網頁的內容以后,以樹(森林)結構保存了結果。HTMLParser訪問結果內容的方法有兩種。使用Filter和使用Visitor
下面介紹使用Visitor訪問內容的方法。

5.1 NodeVisitor
從簡單方面的理解,Filter是根據某種條件過濾取出需要的Node再進行處理。Visitor則是遍歷內容樹的每一個節點,對於符合條件的節點進行處理。實際的結果異曲同工,兩種不同的方法可以達到相同的結果。
下面是一個最常見的NodeVisitro的例子。
測試代碼:
    public static void main(String[] args) {
        try{
            Parser parser = new Parser( (HttpURLConnection) (new URL("http://127.0.0.1:8080/HTMLParserTester.html")).openConnection() );

            NodeVisitor visitor = new NodeVisitor( false, false ) {
                public void visitTag(Tag tag) {
                   message("This is Tag:"+tag.getText());
                }
                public void visitStringNode (Text string)    {
                     message("This is Text:"+string);
                }
                public void visitRemarkNode (Remark remark) {
                     message("This is Remark:"+remark.getText());
                }
                public void beginParsing () {
                    message("beginParsing");
                }
                public void visitEndTag (Tag tag){
                    message("visitEndTag:"+tag.getText());
                }
                public void finishedParsing () {
                    message("finishedParsing");
                }
            };

            parser.visitAllNodesWith(visitor);
        }
        catch( Exception e ) {     
            e.printStackTrace();
        }
    }
輸出結果:
beginParsing
This is Tag:!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
This is Text:Txt (121[0,121],123[1,0]): \n
This is Text:Txt (244[1,121],246[2,0]): \n
finishedParsing

可以看到,開始遍歷所以的節點以前,beginParsing先被調用,然后處理的是中間的Node,最后在結束遍歷以前,finishParsing被調用。因為我設置的 recurseChildrenrecurseSelf都是false,所以Visitor沒有訪問子節點也沒有訪問根節點的內容。中間輸出的兩個\n就是我們在HTMLParser使用詳解(1- 初始化Parser 中討論過的最高層的那兩個換行。

我們先把recurseSelf設置成true,看看會發生什么。
NodeVisitor visitor = new NodeVisitor( false, true) {
輸出結果:
beginParsing
This is Tag:!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
This is Text:Txt (121[0,121],123[1,0]): \n
This is Tag:head
This is Text:Txt (244[1,121],246[2,0]): \n
This is Tag:html xmlns="http://www.w3.org/1999/xhtml"
finishedParsing
可以看到,HTML頁面的第一層節點都被調用了。

我們再用下面的方法調用看看:
NodeVisitor visitor = new NodeVisitor( true, false) {
輸出結果:
beginParsing
This is Tag:!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
This is Text:Txt (121[0,121],123[1,0]): \n
This is Tag:meta http-equiv="Content-Type" content="text/html; charset=gb2312"
This is Text:Txt (204[1,81],229[1,106]):
白澤居-title-www.baizeju.com
visitEndTag:/title
visitEndTag:/head
This is Text:Txt (244[1,121],246[2,0]): \n
This is Text:Txt (289[2,43],291[3,0]): \n
This is Text:Txt (298[3,7],300[4,0]): \n
This is Text:Txt (319[4,19],322[5,1]): \n\t
This is Text:Txt (342[5,21],346[6,2]): \n\t\t
This is Remark:
這是注釋白澤居-www.baizeju.com 
This is Text:Txt (378[6,34],408[8,0]): \n\t\t
白澤居-字符串1-www.baizeju.com\n
This is Text:Txt (441[8,33],465[8,57]):
白澤居-鏈接文本-www.baizeju.com
visitEndTag:/a
This is Text:Txt (469[8,61],472[9,1]): \n\t
visitEndTag:/div
This is Text:Txt (478[9,7],507[11,0]): \n\t
白澤居-字符串2-www.baizeju.com\n
visitEndTag:/div
This is Text:Txt (513[11,6],515[12,0]): \n
visitEndTag:/body
This is Text:Txt (522[12,7],524[13,0]): \n
visitEndTag:/html
finishedParsing
可以看到,所有的子節點都出現了,除了剛剛例子里面的兩個最上層節點This is Tag:headThis is Tag:html xmlns="http://www.w3.org/1999/xhtml"

想讓它們都出來,只需要
NodeVisitor visitor = new NodeVisitor( true, true) {
輸出結果:
beginParsing
This is Tag:!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
This is Text:Txt (121[0,121],123[1,0]): \n
This is Tag:head
This is Tag:meta http-equiv="Content-Type" content="text/html; charset=gb2312"
This is Tag:title
This is Text:Txt (204[1,81],229[1,106]):
白澤居-title-www.baizeju.com
visitEndTag:/title
visitEndTag:/head
This is Text:Txt (244[1,121],246[2,0]): \n
This is Tag:html xmlns="http://www.w3.org/1999/xhtml"
This is Text:Txt (289[2,43],291[3,0]): \n
This is Tag:body 
This is Text:Txt (298[3,7],300[4,0]): \n
This is Tag:div id="top_main"
This is Text:Txt (319[4,19],322[5,1]): \n\t
This is Tag:div id="logoindex"
This is Text:Txt (342[5,21],346[6,2]): \n\t\t
This is Remark:
這是注釋白澤居-www.baizeju.com 
This is Text:Txt (378[6,34],408[8,0]): \n\t\t
白澤居-字符串1-www.baizeju.com\n
This is Tag:a href="http://www.baizeju.com"
This is Text:Txt (441[8,33],465[8,57]):
白澤居-鏈接文本-www.baizeju.com
visitEndTag:/a
This is Text:Txt (469[8,61],472[9,1]): \n\t
visitEndTag:/div
This is Text:Txt (478[9,7],507[11,0]): \n\t
白澤居-字符串2-www.baizeju.com\n
visitEndTag:/div
This is Text:Txt (513[11,6],515[12,0]): \n
visitEndTag:/body
This is Text:Txt (522[12,7],524[13,0]): \n
visitEndTag:/html
finishedParsing
哈哈,這下調用清楚了,大家在需要處理的地方增加自己的代碼好了。


5.2
其他Visitor
HTMLParser
還定義了幾個其他的VisitorHtmlPageNodeVisitorObjectFindingVisitorStringFindingVisitorTagFindingVisitorTextExtractingVisitorUrlModifyingVisitor,它們都是NodeVisitor的子類,實現了一些特定的功能。筆者個人的感覺是沒什么用處,如果你需要什么特定的功能,還不如自己寫一個,想在這些里面找到適合你需要的,化的時間可能更多。反正大家看看代碼就發現,它們每個都沒幾行真正有效的代碼。

 

實例:

目標頁面代碼

 

<ul class="list_ul">
    <li class="title_li">1
    <a href="http://f.wanfangdata.com.cn/download/Periodical_zhfsbx98200105004.aspx" style="" title="全文" target="_blank" class="pdf_img"></a>
    <a href="http://d.wanfangdata.com.cn/Periodical_zhfsbx98200105004.aspx" style="display:none" title="文摘" target="_blank" class="abs_img"></a>
    [url=http://d.wanfangdata.com.cn/Periodical_zhfsbx98200105004.aspx]抗環瓜氨酸肽抗體檢測在<font color="red">類風濕</font>關節炎中的意義[/url]        
    <span>(<t>被引用</t> 182 <t></t>)</span></li>
    <li class="greencolor">[<t>期刊論文</t>] [url=http://c.wanfangdata.com.cn/Periodical-zhfsbx98.aspx]《中華風濕病學雜志》[/url]
        <span>
            <span title="被中信所《中國科技期刊引證報告》收錄">ISTIC</span>
            
            
            <span title="被北京大學《中文核心期刊要目總覽》收錄">PKU</span>
            
        </span>-[url=http://c.wanfangdata.com.cn/periodical/zhfsbx98/2001-5.aspx]2001年5期[/url]<a target="_blank" href="http://social.wanfangdata.com.cn/Locate.ashx?ArticleId=zhfsbx98200105004&amp;Name=%e6%9b%be%e5%b0%8f%e5%b3%b0">曾小峰</a><a target="_blank" href="http://social.wanfangdata.com.cn/Locate.ashx?ArticleId=zhfsbx98200105004&amp;Name=%e8%89%be%e8%84%89%e5%85%b4">艾脈興</a><a target="_blank" href="http://social.wanfangdata.com.cn/Locate.ashx?ArticleId=zhfsbx98200105004&amp;Name=%e7%94%98%e6%99%93%e4%b8%b9">甘曉丹</a><a target="_blank" href="http://social.wanfangdata.com.cn/Locate.ashx?ArticleId=zhfsbx98200105004&amp;Name=%e5%8f%b2%e8%89%b3%e8%90%8d">史艷萍</a><a target="_blank" href="http://social.wanfangdata.com.cn/Locate.ashx?ArticleId=zhfsbx98200105004&amp;Name=%e5%ae%8b%e7%90%b4%e8%8a%b3">宋琴芳</a><a target="_blank" href="http://social.wanfangdata.com.cn/Locate.ashx?ArticleId=zhfsbx98200105004&amp;Name=%e5%94%90%e7%a6%8f%e6%9e%97">唐福林</a></li>
</ul>

 

目標解析上述頁面代碼,獲取標簽

 [url=http://d.wanfangdata.com.cn/Periodical_zhfsbx98200105004.aspx]抗環瓜氨酸肽抗體檢測在<font color="red">類風濕</font>關節炎中的意義[/url] 

中的內容,通過htmlparser實現如下:

package com.nit.htmlparser.test;

import java.io.File;

import org.htmlparser.Node;
import org.htmlparser.NodeFilter;
import org.htmlparser.Parser;
import org.htmlparser.filters.AndFilter;
import org.htmlparser.filters.HasAttributeFilter;
import org.htmlparser.filters.TagNameFilter;
import org.htmlparser.util.NodeList;

public class Htmlparser {

    public static void main(String[] args) {
        File file = new File("E:\\JavaEE Work\\HtmlparserTest\\source.txt");//獲取上述html代碼
        try {
            Parser parser = new Parser(file.getAbsolutePath());
            parser.setEncoding("GBK");
                        //設置編碼格式
            NodeFilter filter1 = new TagNameFilter("li");
            NodeFilter filter2 = new HasAttributeFilter("class","title_li");
            NodeFilter filter = new AndFilter(filter1,filter2);
            //設置頁面過濾條件
            NodeList nodeList = parser.extractAllNodesThatMatch(filter);
                        //根據過濾條件解析頁面
            Node node = nodeList.elementAt(0);
            String html = node.getChildren().toHtml();
                        //將抽取出來的信息轉化為html再次解析
            filter1 = new HasAttributeFilter("target", "_blank");
            parser = Parser.createParser(html, "GBK");
            nodeList = parser.extractAllNodesThatMatch(filter1);
            System.out.println(nodeList.elementAt(2).getChildren().asString());
            filter2 = new TagNameFilter("span");
            parser = Parser.createParser(html, "GBK");
            nodeList = parser.extractAllNodesThatMatch(filter2);
            System.out.println(nodeList.elementAt(0).getChildren().asString());
        } catch (Throwable e) {
            e.printStackTrace();
        }
        
    }
}

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM