网络爬虫---HTMLPARSER使用指南

纪秋月
2023-12-01

HTMLPARSER使用指南

Posted五月19,2008Comments(11)

需要做一个垂直搜索引擎,比较了nekohtml和htmlparser的功能,尽管nekohtml在容错性、性能等方面的口碑好像比htmlparser好(htmlunit也用的是nekohtml),但感觉nekohtml的测试用例和文档都比htmlparser都少,而且htmlparser基本上能够满足垂直搜索引擎页面处理分析的需求,因此先研究一下htmlparser的使用,有空再研究nekohtml和mozillahtmlparser的使用。html的功能还是官方说得最为清楚,

HTMLParserisaJavalibraryusedtoparseHTMLineitheralinearornestedfashion.Primarilyusedfortransformationorextraction,itfeaturesfilters,visitors,customtagsandeasytouseJavaBeans.Itisafast,robustandwelltestedpackage.

Thetwofundamentaluse-casesthatarehandledbytheparserareextractionandtransformation(thesynthesesuse-case,whereHTMLpagesarecreatedfromscratch,isbetterhandledbyothertoolsclosertothesourceofdata).Whilepriorversionsconcentratedondataextractionfromwebpages,Version1.4oftheHTMLParserhassubstantialimprovementsintheareaoftransformingwebpages,withsimplifiedtagcreationandediting,andverbatimtoHtml()methodoutput.

研究的重点还是extraction的使用,有空再研究transformation的使用。

1、htmlparser对html页面处理的数据结构

如图所示,HtmlParser采用了经典的Composite模式,通过RemarkNode、TextNode、TagNode、AbstractNode和Tag来描述HTML页面各元素。

·org.htmlparser.Node:

Node接口定义了进行树形结构节点操作的各种典型操作方法,包括:

节点到html文本、text文本的方法:toPlainTextString、toHtml

典型树形结构遍历的方法:getParent、getChildren、getFirstChild、getLastChild、getPreviousSibling、getNextSibling、getText

获取节点对应的树形结构结构的顶级节点Page对象方法:getPage

获取节点起始位置的方法:getStartPosition、getEndPosition

Visitor方法遍历节点时候方法:accept(NodeVisitorvisitor)

Filter方法:collectInto(NodeListlist,NodeFilterfilter)

Object方法:toString、clone

·org.htmlparser.nodes.AbstractNode:

AbstractNode是形成HTML树形结构抽象基类,实现了Node接口。

在htmlparser中,Node分成三类:

RemarkNode:代表Html中的注释

TagNode:标签节点。

TextNode:文本节点

这三类节点都继承AbstractNode。

·org.htmlparser.nodes.TagNode:

TagNode包含了对HTML处理的核心的各个类,是所有TAG的基类,其中有分为包含其他TAG的复合节点ComositeTag和不包含其他TAG的叶子节点Tag。

复合节点CompositeTag:

AppletTag,BodyTag,Bullet,BulletList,DefinitionList,DefinitionListBullet,Div,FormTag,FrameSetTag,HeadingTag,

HeadTag,Html,LabelTag,LinkTag,ObjectTag,ParagraphTag,ScriptTag,SelectTag,Span,StyleTag,TableColumn,

TableHeader,TableRow,TableTag,TextareaTag,TitleTag

叶子节点TAG:

BaseHrefTag,DoctypeTag,FrameTag,ImageTag,InputTag,JspTag,MetaTag,ProcessingInstructionTag,

2、htmlparser对html页面处理的算法

主要是如下几种方式

·采用Visitor方式访问Html

try{
Parserparser=newParser();
parser.setURL(http://www.google.com);
parser.setEncoding(parser.getEncoding());
NodeVisitorvisitor=newNodeVisitor(){
publicvoidvisitTag(Tagtag){
logger.fatal(testVisitorAll()Tagnameis:
+tag.getTagName()+\nClassis:
+tag.getClass());
}

};

parser.visitAllNodesWith(visitor);
}catch(ParserExceptione){
e.printStackTrace();
}

·采用Filter方式访问html

try{

NodeFilterfilter=newNodeClassFilter(LinkTag.class);
Parserparser=newParser();
parser.setURL(http://www.google.com);
parser.setEncoding(parser.getEncoding());
NodeListlist=parser.extractAllNodesThatMatch(filter);
for(inti=0;i<list.size();i++){
LinkTagnode=(LinkTag)list.elementAt(i);
logger.fatal(testLinkTag()Linkis:+node.extractLink());
}
}catch(Exceptione){
e.printStackTrace();
}

·采用org.htmlparser.beans方式

另外htmlparser还在org.htmlparser.beans中对一些常用的方法进行了封装,以简化操作,例如:

Parserparser=newParser();

LinkBeanlinkBean=newLinkBean();
linkBean.setURL(http://www.google.com);
URL[]urls=linkBean.getLinks();

for(inti=0;i<urls.length;i++){
URLurl=urls[i];
logger.fatal(testLinkBean()-urlis:+url);
}

3、htmlparser关键包结构说明

htmlparser其实核心代码并不多,好好研究一下其代码,弥补文档不足的问题。同时htmlparser的代码注释和单元测试用例还是很齐全的,也有助于了解htmlparser的用法。

3.1、org.htmlparser

定义了htmlparser的一些基础类。其中最为重要的是Parser类。

Parser是htmlparser的最核心的类,其构造函数提供了如下:Parser.createParser(Stringhtml,Stringcharset)、Parser()、Parser(Lexerlexer,ParserFeedbackfb)、Parser(URLConnectionconnection,ParserFeedbackfb)、Parser(Stringresource,ParserFeedbackfeedback)、Parser(Stringresource)

各构造函数的具体用法及含义可以查看其代码,很容易理解。

Parser常用的几个方法:

·elements获取元素

Parserparser=newParser(http://www.google.com);
for(NodeIteratori=parser.elements();i.hasMoreElements();)
processMyNodes(i.nextNode());

·parse(NodeFilterfilter):通过NodeFilter方式获取

·visitAllNodesWith(NodeVisitorvisitor):通过Nodevisitor方式

·extractAllNodesThatMatch(NodeFilterfilter):通过NodeFilter方式

3.2、org.htmlparser.beans

对Visitor和Filter的方法进行了封装,定义了针对一些常用html元素操作的bean,简化对常用元素的提取操作。

包括:FilterBean、HTMLLinkBean、HTMLTextBean、LinkBean、StringBean、BeanyBaby等。

3.3、org.htmlparser.nodes

定义了基础的node,包括:AbstractNode、RemarkNode、TagNode、TextNode等。

3.4、org.htmlparser.tags

定义了htmlparser的各种tag。

3.5、org.htmlparser.filters

定义了htmlparser所提供的各种filter,主要通过extractAllNodesThatMatch(NodeFilterfilter)来对html页面指定类型的元素进行过滤,包括:AndFilter、CssSelectorNodeFilter、HasAttributeFilter、HasChildFilter、HasParentFilter、HasSiblingFilter、IsEqualFilter、LinkRegexFilter、LinkStringFilter、NodeClassFilter、NotFilter、OrFilter、RegexFilter、StringFilter、TagNameFilter、XorFilter

3.6、org.htmlparser.visitors

定义了htmlparser所提供的各种visitor,主要通过visitAllNodesWith(NodeVisitorvisitor)来对html页面元素进行遍历,包括:HtmlPage、LinkFindingVisitor、NodeVisitor、ObjectFindingVisitor、StringFindingVisitor、TagFindingVisitor、TextExtractingVisitor、UrlModifyingVisitor

3.7、org.htmlparser.parserapplications

定义了一些实用的工具,包括LinkExtractor、SiteCapturer、StringExtractor、WikiCapturer,这几个类也可以作为htmlparser使用样例。

3.8、org.htmlparser.tests

对各种功能的单元测试用例,也可以作为htmlparser使用的样例。

4、htmlparser的使用样例

importjava.net.URL;

importjunit.framework.TestCase;

importorg.apache.log4j.Logger;
importorg.htmlparser.Node;
importorg.htmlparser.NodeFilter;
importorg.htmlparser.Parser;
importorg.htmlparser.Tag;
importorg.htmlparser.beans.LinkBean;
importorg.htmlparser.filters.NodeClassFilter;
importorg.htmlparser.filters.OrFilter;
importorg.htmlparser.filters.TagNameFilter;
importorg.htmlparser.tags.HeadTag;
importorg.htmlparser.tags.ImageTag;
importorg.htmlparser.tags.InputTag;
importorg.htmlparser.tags.LinkTag;
importorg.htmlparser.tags.OptionTag;
importorg.htmlparser.tags.SelectTag;
importorg.htmlparser.tags.TableColumn;
importorg.htmlparser.tags.TableRow;
importorg.htmlparser.tags.TableTag;
importorg.htmlparser.tags.TitleTag;
importorg.htmlparser.util.NodeIterator;
importorg.htmlparser.util.NodeList;
importorg.htmlparser.util.ParserException;
importorg.htmlparser.visitors.HtmlPage;
importorg.htmlparser.visitors.NodeVisitor;
importorg.htmlparser.visitors.ObjectFindingVisitor;

publicclassParserTestCaseextendsTestCase{

privatestaticfinalLoggerlogger=Logger.getLogger(ParserTestCase.class);

publicParserTestCase(Stringname){
super(name);
}
/*
*测试ObjectFindVisitor的用法
*/
publicvoidtestImageVisitor(){
try{
ImageTagimgLink;
ObjectFindingVisitorvisitor=newObjectFindingVisitor(
ImageTag.class);
Parserparser=newParser();
parser.setURL(http://www.google.com);
parser.setEncoding(parser.getEncoding());
parser.visitAllNodesWith(visitor);
Node[]nodes=visitor.getTags();
for(inti=0;i<nodes.length;i++){
imgLink=(ImageTag)nodes[i];
logger.fatal(testImageVisitor()ImageURL=
+imgLink.getImageURL());
logger.fatal(testImageVisitor()ImageLocation=
+imgLink.extractImageLocn());
logger.fatal(testImageVisitor()SRC=
+imgLink.getAttribute(SRC));
}
}
catch(Exceptione){
e.printStackTrace();
}
}
/*
*测试TagNameFilter用法
*/
publicvoidtestNodeFilter(){
try{
NodeFilterfilter=newTagNameFilter(IMG);
Parserparser=newParser();
parser.setURL(http://www.google.com);
parser.setEncoding(parser.getEncoding());
NodeListlist=parser.extractAllNodesThatMatch(filter);
for(inti=0;i<list.size();i++){
logger.fatal(testNodeFilter()+list.elementAt(i).toHtml());
}
}catch(Exceptione){
e.printStackTrace();
}

}
/*
*测试NodeClassFilter用法
*/
publicvoidtestLinkTag(){
try{

NodeFilterfilter=newNodeClassFilter(LinkTag.class);
Parserparser=newParser();
parser.setURL(http://www.google.com);
parser.setEncoding(parser.getEncoding());
NodeListlist=parser.extractAllNodesThatMatch(filter);
for(inti=0;i<list.size();i++){
LinkTagnode=(LinkTag)list.elementAt(i);
logger.fatal(testLinkTag()Linkis:+node.extractLink());
}
}catch(Exceptione){
e.printStackTrace();
}

}
/*
*测试<linkhref=text=text/cssrel=stylesheet/>用法
*/
publicvoidtestLinkCSS(){
try{

Parserparser=newParser();
parser
.setInputHTML(<head><title>LinkTest</title>
+<linkhref=/test01/css.csstext=text/cssrel=stylesheet/>
+<linkhref=/test02/css.csstext=text/cssrel=stylesheet/>
+</head>+<body>);
parser.setEncoding(parser.getEncoding());
NodeListnodeList=null;

for(NodeIteratore=parser.elements();e.hasMoreNodes();){
Nodenode=e.nextNode();
logger
.fatal(testLinkCSS()+node.getText()
+node.getClass());

}
}catch(Exceptione){
e.printStackTrace();
}
}
/*
*测试OrFilter的用法
*/
publicvoidtestOrFilter(){
NodeFilterinputFilter=newNodeClassFilter(InputTag.class);
NodeFilterselectFilter=newNodeClassFilter(SelectTag.class);
ParsermyParser;
NodeListnodeList=null;

try{
Parserparser=newParser();
parser
.setInputHTML(<head><title>OrFilterTest</title>
+<linkhref=/test01/css.csstext=text/cssrel=stylesheet/>
+<linkhref=/test02/css.csstext=text/cssrel=stylesheet/>
+</head>
+<body>
+<inputtype=textvalue=text1name=text1/>
+<inputtype=textvalue=text2name=text2/>
+<select><optionid=1>1</option><optionid=2>2</option><optionid=3></option></select>
+<ahref=http://www.yeeach.com>yeeach.com</a>
+</body>);

parser.setEncoding(parser.getEncoding());
OrFilterlastFilter=newOrFilter();
lastFilter.setPredicates(newNodeFilter[]{selectFilter,
inputFilter});
nodeList=parser.parse(lastFilter);
for(inti=0;i<=nodeList.size();i++){
if(nodeList.elementAt(i)instanceofInputTag){
InputTagtag=(InputTag)nodeList.elementAt(i);
logger.fatal(OrFiltertagnameis:+tag.getTagName()
+,tagvalueis:+tag.getAttribute(value));
}
if(nodeList.elementAt(i)instanceofSelectTag){
SelectTagtag=(SelectTag)nodeList.elementAt(i);
NodeListlist=tag.getChildren();

for(intj=0;j<list.size();j++){
OptionTagoption=(OptionTag)list.elementAt(j);
logger
.fatal(OrFilterOption
+option.getOptionText());
}

}
}

}catch(ParserExceptione){
e.printStackTrace();
}
}
/*
*测试对<table><tr><td></td></tr></table>的解析
*/
publicvoidtestTable(){
ParsermyParser;
NodeListnodeList=null;
myParser=Parser.createParser(<body>+<tableid=table1>
+<tr><td>1-11</td><td>1-12</td><td>1-13</td>
+<tr><td>1-21</td><td>1-22</td><td>1-23</td>
+<tr><td>1-31</td><td>1-32</td><td>1-33</td></table>
+<tableid=table2>
+<tr><td>2-11</td><td>2-12</td><td>2-13</td>
+<tr><td>2-21</td><td>2-22</td><td>2-23</td>
+<tr><td>2-31</td><td>2-32</td><td>2-33</td></table>
+</body>,GBK);
NodeFiltertableFilter=newNodeClassFilter(TableTag.class);
OrFilterlastFilter=newOrFilter();
lastFilter.setPredicates(newNodeFilter[]{tableFilter});
try{
nodeList=myParser.parse(lastFilter);
for(inti=0;i<=nodeList.size();i++){
if(nodeList.elementAt(i)instanceofTableTag){
TableTagtag=(TableTag)nodeList.elementAt(i);
TableRow[]rows=tag.getRows();

for(intj=0;j<rows.length;j++){
TableRowtr=(TableRow)rows[j];
TableColumn[]td=tr.getColumns();
for(intk=0;k<td.length;k++){
logger.fatal(<td>+td[k].toPlainTextString());
}

}

}
}

}catch(ParserExceptione){
e.printStackTrace();
}
}
/*
*测试NodeVisitor的用法,遍历所有节点
*/
publicvoidtestVisitorAll(){
try{
Parserparser=newParser();
parser.setURL(http://www.google.com);
parser.setEncoding(parser.getEncoding());
NodeVisitorvisitor=newNodeVisitor(){
publicvoidvisitTag(Tagtag){
logger.fatal(testVisitorAll()Tagnameis:
+tag.getTagName()+\nClassis:
+tag.getClass());
}

};

parser.visitAllNodesWith(visitor);
}catch(ParserExceptione){
e.printStackTrace();
}
}
/*
*测试对指定Tag的NodeVisitor的用法
*/
publicvoidtestTagVisitor(){
try{

Parserparser=newParser(
<head><title>dddd</title>
+<linkhref=/test01/css.csstext=text/cssrel=stylesheet/>
+<linkhref=/test02/css.csstext=text/cssrel=stylesheet/>
+</head>+<body>
+<ahref=http://www.yeeach.com>yeeach.com</a>
+</body>);
NodeVisitorvisitor=newNodeVisitor(){
publicvoidvisitTag(Tagtag){
if(taginstanceofHeadTag){
logger.fatal(visitTag()HeadTag:Tagnameis:
+tag.getTagName()+\nClassis:
+tag.getClass()+\nTextis:
+tag.getText());
}elseif(taginstanceofTitleTag){
logger.fatal(visitTag()TitleTag:Tagnameis:
+tag.getTagName()+\nClassis:
+tag.getClass()+\nTextis:
+tag.getText());

}elseif(taginstanceofLinkTag){
logger.fatal(visitTag()LinkTag:Tagnameis:
+tag.getTagName()+\nClassis:
+tag.getClass()+\nTextis:
+tag.getText()+\ngetAttributeis:
+tag.getAttribute(href));
}else{
logger.fatal(visitTag():Tagnameis:
+tag.getTagName()+\nClassis:
+tag.getClass()+\nTextis:
+tag.getText());
}

}

};

parser.visitAllNodesWith(visitor);
}catch(Exceptione){
e.printStackTrace();
}
}
/*
*测试HtmlPage的用法
*/
publicvoidtestHtmlPage(){
StringinputHTML=<html>+<head>
+<title>WelcometotheHTMLParserwebsite</title>
+</head>+<body>+WelcometoHTMLParser
+<tableid=table1>
+<tr><td>1-11</td><td>1-12</td><td>1-13</td>
+<tr><td>1-21</td><td>1-22</td><td>1-23</td>
+<tr><td>1-31</td><td>1-32</td><td>1-33</td></table>
+<tableid=table2>
+<tr><td>2-11</td><td>2-12</td><td>2-13</td>
+<tr><td>2-21</td><td>2-22</td><td>2-23</td>
+<tr><td>2-31</td><td>2-32</td><td>2-33</td></table>
+</body>+</html>;
Parserparser=newParser();
try{
parser.setInputHTML(inputHTML);
parser.setEncoding(parser.getURL());
HtmlPagepage=newHtmlPage(parser);
parser.visitAllNodesWith(page);
logger.fatal(testHtmlPage-titleis:+page.getTitle());
NodeListlist=page.getBody();

for(NodeIteratoriterator=list.elements();iterator
.hasMoreNodes();){
Nodenode=iterator.nextNode();
logger.fatal(testHtmlPage-nodeis:+node.toHtml());
}

}catch(ParserExceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}
}
/*
*测试LinkBean的用法
*/
publicvoidtestLinkBean(){
Parserparser=newParser();

LinkBeanlinkBean=newLinkBean();
linkBean.setURL(http://www.google.com);
URL[]urls=linkBean.getLinks();

for(inti=0;i<urls.length;i++){
URLurl=urls[i];
logger.fatal(testLinkBean()-urlis:+url);
}

}

}

5、相关的项目

nekohtml:评价比htmlparser好,把html正规化标准的xml文档,用xerces处理,但文档较少。

mozillahtmlparserhttp://www.dapper.net/网站采用的html解析器,开源了,基于mozilla的解析器,值得研究一下。

http://jerichohtml.sourceforge.net/

http://htmlcleaner.sourceforge.net/

http://html.xamjwg.org/cobra.jsp

http://jrex.mozdev.org/

https://xhtmlrenderer.dev.java.net

其他一些htmlparser可以参考相关的汇总文章:

http://www.manageability.org/blog/stuff/screen-scraping-tools-written-in-java/view

http://java-source.net/open-source/html-parsers

http://www.open-open.com/30.htm

6、参考文档

http://www.blogjava.net/lostfire/archive/2006/07/02/56212.html

http://blog.csdn.net/scud/archive/2005/08/11/451397.aspx

http://chasethedevil.blogspot.com/2006/05/java-html-parsing-example-with.html

http://javaboutique.internet.com/tutorials/HTMLParser/

Technorati标签:nekohtml,htmlparser,scraping,scrape,spider,爬虫,crawler


站内标签:crawler,爬虫,htmlparser,nekohtml,scrape,scraping,spider

 类似资料: