HTMLParser(一個比較流行的html程式碼解析、處理開源專案)學習,總結
http://htmlparser.sourceforge.net/
HtmlParser初步研究 這兩天準備做一些網站程式設計的工作,於是對HtmlParse小研究了一下,目的是快速入手,而不是深入研究,做了一下整理,和大家共同討論一下。 一,資料組織分析:
HtmlParser主要靠Node、AbstractNode和Tag來表達Html,因為Remark和Text相對簡單,此處就將其忽略了。
- Node是形成樹結構表示HTML的基礎,所有的資料表示都是介面Node的實現,Node定義了與頁面樹結構所表達的頁面Page物件,定義了獲取父、子、兄弟節點的方法,定義了節點到對應html文字的方法,定義了該節點對應的起止位置,定義了過濾方法,定義了Visitor訪問機制。
- AbstractNode是Node的一種具體的類實現,起到構成樹形結構的作用,除了同具體Node相關的accetp方法,toString,toHtml,toPlainTextString方法以外,AbstractNode實現了大多基本的方法,使得它的子類,不用理會具體的樹操作。
- Tag是具體分析的主要內容。Tag分成composite的Tag和不能包含其他Tag的簡單Tag兩類,其中前者的基類是CompositeTag,其子類包含BodyTag,Div,FrameSetTag,OptionTag,等27個子類;而簡單Tag有BaseHrefTag、DoctypeTag,FrameTag,ImageTag,InputTag,JspTag,MetaTag,ProcessingInstructionTag這八類。
Node分成三類:
- RemarkNode:代表Html中的註釋
- TagNode:標籤節點,是種類最多的節點型別,上述Tag的具體節點類都是TagNode的實現。
- TextNode:文字節點
- 用一個URL或頁面String做一個Parser
- 用這個Parser做一個Visitor
- 使用Parser.visitAllNodeWith(Visitor)來遍歷節點
- 獲取Visitor遍歷後得到的資料
- 做解析之前做的事情:visitor.beginParsing();
- 每次取到一個節點Node,讓該Node接受accept該Visitor
- 做解析後做的事情:visitor.finishedParsing();
- 對於所有TagNode都使用一個accept方法,即TagNode的accept方法。首先判斷是否是標籤結尾,如果是就visitor.visitEndTag (this);否則visitor.visitTag (this);
- 如果是TextNode,那就visitor.visitStringNode (this);就可以了。
- 如果是RemarkNode,那就visitor.visitRemarkNode (this);就可以了。
實際上NodeVisitor裡邊這四種visit方法都是空的,因為在不同的Visitor中對於這三類節點的處理是不同的;對於需要處理的節點,只要過載對應的visit方法就行了,如果不處理那就不理會就可以了;另外,如果使用者用自己的Visitor,那麼還可以靈活的處理不同型別的節點了。
- ObjectFindingVisitor:用來找出所有指定型別的節點,採用getTags()來獲取結果。
- StringBean:用來從一個指定的URL獲取移除了<SCRIPT></SCRIPT>和<PRE></PRE>之間程式碼的Html程式碼,也可以用做Visitor,用來移除這兩種標籤內部的程式碼,採用StringBean.getStrings()來獲取結果。
- HtmlPage:提取Title,body中的節點和頁面中的TableTag節點。
- LinkFindingVisitor:找出節點中包含某個連結的總個數。
- StringFindingVisitor:找出遍歷的TextNode中含有指定字串的個數。
- TagFindingVisitor:找出指定Tag的所有節點,可以指定多種型別。
- TextExtractingVisitor:從網頁中把所有標籤去掉來提取文字,這個提取文字的Visitor有時是很實用的,只是注意在提取文字時將標籤的屬性也去掉了,也就是說只剩下標籤之間的文字,例如<a>中的連結也去掉了。
- UrlModifyingVisitor:用來修改網頁中的連結。
郵件列表:
http://sourceforge.net/mail/?group_id=24399
主頁上的sample:
http://htmlparser.sourceforge.net/samples.html
在釋出的包裡沒有demo或example資料夾,但是在有些類,比如org.htmlparser.Parser
有public static void main(String[] args)這個方法,其中有一些該類的使用方法。
org.htmlparser.Parser.main(String[] args)中就有Parse a web page and print the tags in a simple loop的方法。這在http://htmlparser.sourceforge.net/samples.html上有說明。
特別的:一個java web start
http://htmlparser.sourceforge.net/samples/filterbuilder.jnlp
幫助認識和使用filter。
一些常見處理:
應用HtmlParser處理含HTML標籤的字串或網頁
String Extraction
To get all the text content from a web page, use the TextExtractingVisitor, like so:
import org.htmlparser.Parser;
import org.htmlparser.util.ParserException;
import org.htmlparser.visitors.TextExtractingVisitor;
public class StringDemo
{
public static void main (String[] args) throws ParserException
{
Parser parser = new Parser ("http://pageIwantToParse.com");
TextExtractingVisitor visitor = new TextExtractingVisitor ();
parser.visitAllNodesWith (visitor);
System.out.println (visitor.getExtractedText());
}
}
If you want a more browser like behaviour, use the StringBean like so:
import org.htmlparser.beans.StringBean;
public class StringDemo
{
public static void main (String[] args)
{
StringBean sb = new StringBean ();
sb.setLinks (false);
sb.setReplaceNonBreakingSpaces (true);
sb.setCollapse (true);
sb.setURL ("http://pageIwantToParse.com");
System.out.println (sb.getStrings ());
}
}
To get all the text content from a web page you already have in a string:
import org.htmlparser.Parser;
import org.htmlparser.Node;
import org.htmlparser.nodes.TextNode;
import org.htmlparser.util.ParserException;
public class StringDemo
{
public static void main (String[] args) throws ParserException
{
Parser myParser;
Node[] nodes = null;
String content = "";
myParser = Parser.createParser(content, null);
nodes = myParser.extractAllNodesThatAre(TextNode.class); //exception could be thrown here
for (int i = 0; i < nodes.length; i++)
{
TextNode textnode = (TextNode) nodes[i];
String line = textnode.toPlainTextString().trim();
if (line.equals("")) continue;
System.out.println(line);
}
}
http://blog.csdn.net/redez/archive/2005/11/21/534277.aspx
最近在研究lucene,主要做ftp搜尋和網頁的站內搜尋。
ftp搜尋比較好做,主流的FTP有兩種一種是IIS的一種是Server-U的.
真對這兩種FTP分別進行分析就可以得到FTP資源的檔名和路徑及大小和日期
然後對其進行索引就可以了,比較簡單。
網頁檢索可不像ftp那樣了,我試著用lucene自帶的htmlparser,解析純英文的網頁
沒有問題,可解析中文的網頁時有時會遇到編碼問題。鬱悶。。。
SourceForge搜到了一個開源的HTMLParser。網址是http://htmlparser.sourceforge.net
目前的版本為1.6。
測試程式碼為:
import java.io.*;
import org.htmlparser.filters.*;
import org.htmlparser.*;
import org.htmlparser.nodes.*;
import org.htmlparser.tags.*;
import org.htmlparser.util.*;
import org.htmlparser.visitors.*;
public class HTMLParserTest
{
public static void main(String args[]) throws Exception
{
String path = "D://Webdup//MyWebsites//biti//download//latest//cisco.biti.edu.cn//index.html";
StringBuffer sbStr = new StringBuffer();
BufferedReader reader = new BufferedReader(new FileReader(new File(path)));
String temp = "";
while((temp=reader.readLine())!=null)
{
sbStr.append(temp);
sbStr.append("/r/n");
}
reader.close();
String result = sbStr.toString();
readAll(result);
readTextAndLink(result);
readByHtml(result);
readTextAndTitle(result);
}
//按頁面方式處理.解析標準的html頁面
public static void readByHtml(String content) throws Exception
{
Parser myParser;
myParser = Parser.createParser(content, "GB2312");
HtmlPage visitor = new HtmlPage(myParser);
myParser.visitAllNodesWith(visitor);
String textInPage = visitor.getTitle();
System.out.println(textInPage);
NodeList nodelist ;
nodelist = visitor.getBody();
System.out.print(nodelist.asString().trim());
}
//讀取文字內容和標題
public static void readTextAndTitle(String result) throws Exception
{
Parser parser ;
NodeList nodelist ;
parser = Parser.createParser(result,"GB2312");
NodeFilter textFilter = new NodeClassFilter(TextNode.class);
NodeFilter titleFilter = new NodeClassFilter(TitleTag.class);
OrFilter lastFilter = new OrFilter();
lastFilter.setPredicates(new NodeFilter[]{textFilter,titleFilter});
nodelist = parser.parse(lastFilter);
Node[] nodes = nodelist.toNodeArray();
String line ="";
for(int i=0;i<nodes.length;i++)
{
Node node = nodes[i];
if(node instanceof TextNode)
{
TextNode textnode = (TextNode) node;
line = textnode.getText();
}
else
if(node instanceof TitleTag)
{
TitleTag titlenode = (TitleTag) node;
line = titlenode.getTitle();
}
if (isTrimEmpty(line))
continue;
System.out.println(line);
}
}
//分別讀純文字和連結
public static void readTextAndLink(String result) throws Exception
{
Parser parser;
NodeList nodelist;
parser = Parser.createParser(result,"GB2312");
NodeFilter textFilter = new NodeClassFilter(TextNode.class);
NodeFilter linkFilter = new NodeClassFilter(LinkTag.class);
OrFilter lastFilter = new OrFilter();
lastFilter.setPredicates(new NodeFilter[] { textFilter, linkFilter });
nodelist = parser.parse(lastFilter);
Node[] nodes = nodelist.toNodeArray();
String line ="";
for(int i=0;i<nodes.length;i++)
{
Node node = nodes[i];
if(node instanceof TextNode)
{
TextNode textnode = (TextNode) node;
line = textnode.getText();
}
else
if(node instanceof LinkTag)
{
LinkTag link = (LinkTag)node;
line = link.getLink();
}
if (isTrimEmpty(line))
continue;
System.out.println(line);
}
}
public static void readAll(String result) throws Exception
{
Parser parser;
Node[] nodes ;
parser = Parser.createParser(result,"GB2312");
nodes = parser.extractAllNodesThatAre(TextNode.class);
//讀取所有的內容節點
for (int i = 0; i < nodes.length; i++)
{
TextNode textnode = (TextNode) nodes[i];
String line = textnode.toPlainTextString().trim();
if (line.equals(""))
continue;
System.out.println(line);
}
}
/**
* 去掉左右空格後字串是否為空
*/
public static boolean isTrimEmpty(String astr)
{
if ((null == astr) || (astr.length() == 0))
{
return true;
}
if (isBlank(astr.trim()))
{
return true;
}
return false;
}
/**
* 字串是否為空:null或者長度為0.
*/
public static boolean isBlank(String astr)
{
if ((null == astr) || (astr.length() == 0))
{
return true;
}
else
{
return false;
}
}
}
}
處理特定tag:
參考:http://sourceforge.net/mailarchive/message.php?msg_id=25aac9fc0704200423h78893925y72cb75136be7330%40mail.gmail.com
package cn.yethyeth.forTest;
思路:建立一個新的NodeVisitor,在其中處理visitTag這個函式(具體原理見上面第一篇文章)
import org.htmlparser.Parser;
import org.htmlparser.Tag;
import org.htmlparser.util.ParserException;
import org.htmlparser.visitors.NodeVisitor;
publicclass HTMLParserHandleTag extends NodeVisitor{
publicvoid visitTag (Tag tag)
{
if( tag.getAttribute("class")!=null ){
System.out.println (""+ tag.getTagName () + tag.getAttribute("class"));
}
}
publicstaticvoid main (String[] args) throws ParserException
{
Parser parser =new Parser ("http://bbs.qihoo.com/ttgz/index.html");
NodeVisitor visitor =new HTMLParserHandleTag ();
parser.visitAllNodesWith (visitor);
}
}
更簡單的方法:使用TagNameFilter , HasAttributeFilter,AndFilter。
TagNameFilter過濾特定名字的tag,
HasAttributeFilter過濾特定名字和值的tag,
AndFilter將多個filter組合起來。
/*
* TestFilter.java, 2007-5-13 1:22:45.
*
* CopyRight (c) 2007-2007, yethyeth ,All rights reserved.
*
* This file is licenced under the Apache License.
*/
package com.bighai.forTest;
import org.apache.commons.lang.StringUtils;
import org.htmlparser.Parser;
import org.htmlparser.Tag;
import org.htmlparser.filters.AndFilter;
import org.htmlparser.filters.HasAttributeFilter;
import org.htmlparser.filters.TagNameFilter;
import org.htmlparser.util.NodeList;
import org.htmlparser.util.ParserException;
public class TestFilter {
/**
* @param args
* @throws ParserException
*/
public static void main(String[] args) throws ParserException {
// TODO Auto-generated method stub
Parser parser = new Parser("http://bbs.qihoo.com/ttgz/index.html");
AndFilter filter =
new AndFilter(
new TagNameFilter("div"),
new HasAttributeFilter("class","rLCon") );
NodeList nodes = parser.parse(filter);
for( int i = 0; i < nodes.size(); i++ ){
System.out.println(
((Tag)nodes.elementAt(i)).getTagName()+" class="+
((Tag)nodes.elementAt(i)).getAttribute("class") );
}
}
}
結果:
DIV class=rLCon