代码改变世界

【转】HtmlParser

2011-04-15 18:47  shy.ang  阅读(330)  评论(0编辑  收藏  举报

一,数据组织分析: 
  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:文本节点 

二,Visitor方式访问Html: 

1,整体解析过程 
  用一个URL或页面String做一个Parser 
  用这个Parser做一个Visitor 
  使用Parser.visitAllNodeWith(Visitor)来遍历节点 
  获取Visitor遍历后得到的数据 
2,Visit过程 
  做解析之前做的事情:visitor.beginParsing(); 
  每次取到一个节点Node,让该Node接受accept该Visitor 
  做解析后做的事情:visitor.finishedParsing(); 
3,获取节点的过程:逐步遍历Html,分析出Node。此部分较为复杂,且对于我们应用来说无需很多了解,暂跳过。 

4,节点访问 
  节点访问采用Visitor模式,Node的accept方法和具体Visitor的visit方法是关键。 
  首先三类Node来accept的方式各不相同: 
    对于所有TagNode都使用一个accept方法,即TagNode的accept方法。首先判断是否是标签结尾,如果是就visitor.visitEndTag (this);否则visitor.visitTag (this); 
    如果是TextNode,那就visitor.visitStringNode (this);就可以了。 
    如果是RemarkNode,那就visitor.visitRemarkNode (this);就可以了。 

  实际上NodeVisitor里边这四种visit方法都是空的,因为在不同的Visitor中对于这三类节点的处理是不同的;对于需要处理的节点,只要重载对应的visit方法就行了,如果不处理那就不理会就可以了;另外,如果用户用自己的Visitor,那么还可以灵活的处理不同类型的节点了。 

  系统为我们实现了下面我要介绍的8种Visitor,实际上可以看作是系统给我们演示了如何做各种各样的Visitor来访问Html,因为实际上我们要真正来用HtmlParser的话,还需要特定的Visitor,而通过简单的这些系统提供的Visitor组合是难以做成什么事情的。 

三,系统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:用来修改网页中的链接。 


四,Filter 

  如果说visitor是遍历提取信息,当然这个信息可以包括某些节点或者从节点分析出来的更有效的信息,这都取决于我们的Visitor做成什么样子,那么Filter则目标很明确,就是用来提取节点的。所以说要想用HtmlParser,首先要熟悉上面讲到的数据组织。 
  系统定义了17种具体的Filter,包括依据节点父子关系的Filter,连接Filter组合的Filter,依据网页内容匹配情况的filter,等等。我们也可以implement Filter来做自己的Filter来提取节点。 
  Filter的调用是同Visitor独立的,因为也无需先filter出一些NodeList,再用Visitor来访问。调用Filter的方法是: 
NodeList nodeList = myParser.parse(someFilter); 
  解析之后,我们可以采用: 
    Node[] nodes = nodeList.toNodeArray(); 来获取节点数组,也可以直接访问: 
    Node node = nodeList.elementAt(i)来获取Node。 

  另外,在Filter后得到NodeList以后,我们仍然可以使用NodeList的extractAllNodesThatMatch(someFilter)来进一步过滤,同时又可以用NodeList的visitAllNodesWith(someVisitor)来做进一步的访问。 
  这样,我们可以看到HtmlParser为我们提供了非常方便的Html解析方式,针对不同的应用可以采用visitor来遍历Html节点提取数据,也可以用Filter来过滤节点,提取出我们所关注的节点,再对节点进行处理。通过这样的组合,一定能够找出我们所需要的信息。

//第一种创建Parser方法
Parser parser = new Parser();
parser.setURL(
"xxx");
parser.setEncoding(parser.getEncoding());
NodeList list
= parser.extractAllNodesThatMatch(filter);//filter
parser.visitAllNodeWith(visitor);//visitor

//第二种
Parser parser = Parser.createParser(String,"uft-8"/"GBK");
//String通过connect.getInputStream()下载,要使用InputStreamReader(inputStream,"GBK");

parser.parse(filter);