HTMLParser 学习与运用

来源:互联网 发布:tif软件 编辑:程序博客网 时间:2024/04/29 22:33

HtmlParser 简介

   当今的 Internet 上面有数亿记的网页,越来越多应用程序将这些网页作为分析和处理的数据对象。这些网页多为半结构化的文本,有着大量的标签和嵌套的结构。当我们自己开发一 些处理网页的应用程序时,会想到要开发一个单独的网页解析器,这一部分的工作必定需要付出相当的精力和时间。事实上,做为 JAVA 应用程序开发者, HtmlParser 为其提供了强大而灵活易用的开源类库,大大节省了写一个网页解析器的开销。 HtmlParser 是 http://sourceforge.net 上活跃的一个开源项目,它提供了线性和嵌套两种方式来解析网页,主要用于 html 网页的转换(Transformation) 以及网页内容的抽取 (Extraction)。HtmlParser 有如下一些易于使用的特性:过滤器 (Filters),访问者模式 (Visitors),处理自定义标签以及易于使用的 JavaBeans。正如 HtmlParser 首页所说:它是一个快速,健壮以及严格测试过的组件;以它设计的简洁,程序运行的速度以及处理 Internet 上真实网页的能力吸引着越来越多的开发者。 本文中就是利用HtmlParser 里提取网页里的链接,实现简易爬虫里的关键部分。HtmlParser 最新的版本是HtmlParser1.6,可以登陆这里下载其源码、 API 参考文档以及 JAR 包。


 简单强大的 StringBean

 如果你想要网页中去掉所有的标签后剩下的文本,那就是用 StringBean 吧。以下简单的代码可以帮你解决这样的问题:

  清单5

  StringBean sb = new StringBean();

  sb.setLinks(false);//设置结果中去点链接

  sb.setURL(url);//设置你所需要滤掉网页标签的页面 url

  System.out.println(sb.getStrings());//打印结果

  HtmlParser 提供了强大的类库来处理网页,由于本文旨在简单的介绍,因此只是将与笔者后续爬虫部分有关的关键类库进行了示例说明。感兴趣的读者可以专门来研究一下 HtmlParser 更为强大的类库。

  简易爬虫的实现

  HttpClient 提供了便利的 HTTP 协议访问,使得我们可以很容易的得到某个网页的源码并保存在本地;HtmlParser 提供了如此简便灵巧的类库,可以从网页中便捷的提取出指向其他网页的超链接。笔者结合这两个开源包,构建了一个简易的网络爬虫。

  爬虫 (Crawler) 原理

   学过数据结构的读者都知道有向图这种数据结构。如下图所示,如果将网页看成是图中的某一个节点,而将网页中指向其他网页的链接看成是这个节点指向其他节 点的边,那么我们很容易将整个 Internet 上的网页建模成一个有向图。理论上,通过遍历算法遍历该图,可以访问到Internet 上的几乎所有的网页。最简单的遍历就是宽度优先以及深度优先。以下笔者实现的简易爬虫就是使用了宽度优先的爬行策略。

  图 2. 网页关系的建模图

 

   简易爬虫实现流程

  在看简易爬虫的实现代码之前,先介绍一下简易爬虫爬取网页的流程。

 

 图 3. 爬虫流程图

 

 

各个类的源码以及说明

  对应上面的流程图,简易爬虫由下面几个类组成,各个类职责如下:

  Crawler.java:爬虫的主方法入口所在的类,实现爬取的主要流程。

  LinkDb.java:用来保存已经访问的 url 和待爬取的 url 的类,提供url出对入队操作。

  Queue.java: 实现了一个简单的队列,在 LinkDb.java 中使用了此类。

  FileDownloader.java:用来下载 url 所指向的网页。

  HtmlParserTool.java: 用来抽取出网页中的链接。

  LinkFilter.java:一个接口,实现其 accept() 方法用来对抽取的链接进行过滤。

  下面是各个类的源码,代码中的注释有比较详细的说明。

 

 

3.Htmlparser汇总说明
 

关键字: htmlparser

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

    html的功能还是官方说得最为清楚,

引用

    HTML Parser is a Java library used to parse HTML in either a linear or nested fashion. Primarily used for transformation or extraction, it features filters, visitors, custom tags and easy to use JavaBeans. It is a fast, robust and well tested package.

    The two fundamental use-cases that are handled by the parser are extraction and transformation (the syntheses use-case, where HTML pages are created from scratch, is better handled by other tools closer to the source of data). While prior versions concentrated on data extraction from web pages, Version 1.4 of the HTMLParser has substantial improvements in the area of transforming web pages, with simplified tag creation and editing, and verbatim toHtml() method output.

 

    研究的重点还是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 (NodeVisitor visitor)

    Filter方法:collectInto (NodeList list, NodeFilter filter)

    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页面处理的算法

主要是如下几种方式

l       采用Visitor方式访问Html

 

 

3、htmlparser关键包结构说明  
  
    htmlparser其实核心代码并不多,好好研究一下其代码,弥补文档不足的问题。同时htmlparser的代码注释和单元测试用例还是很齐全的,也有助于了解htmlparser的用法。  
  
  
3.1、org.htmlparser  
  
    定义了htmlparser的一些基础类。其中最为重要的是Parser类。  
  
    Parser 是htmlparser的最核心的类,其构造函数提供了如下:Parser.createParser (String html,  String charset)、 Parser ()、Parser (Lexer lexer, ParserFeedback fb)、 Parser (URLConnection connection, ParserFeedback fb)、Parser  (String resource, ParserFeedback feedback)、 Parser (String resource)  
  
  各构造函数的具体用法及含义可以查看其代码,很容易理解。  
  
  Parser常用的几个方法:  
  
    *   elements获取元素  
  
    Parser parser = new Parser (”http://www.google.com”);  
    for (NodeIterator i = parser.elements (); i.hasMoreElements (); )  
      processMyNodes (i.nextNode ());  
  
    * parse (NodeFilter filter):通过NodeFilter方式获取  
    * visitAllNodesWith (NodeVisitor visitor):通过Nodevisitor方式  
    * extractAllNodesThatMatch (NodeFilter filter):通过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  (NodeFilter filter)来对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 (NodeVisitor visitor)来对 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的使用样例  
  
   
  
import java.net.URL;  
  
import junit.framework.TestCase;  
  
import org.apache.log4j.Logger;  
import org.htmlparser.Node;  
import org.htmlparser.NodeFilter;  
import org.htmlparser.Parser;  
import org.htmlparser.Tag;  
import org.htmlparser.beans.LinkBean;  
import org.htmlparser.filters.NodeClassFilter;  
import org.htmlparser.filters.OrFilter;  
import org.htmlparser.filters.TagNameFilter;  
import org.htmlparser.tags.HeadTag;  
import org.htmlparser.tags.ImageTag;  
import org.htmlparser.tags.InputTag;  
import org.htmlparser.tags.LinkTag;  
import org.htmlparser.tags.OptionTag;  
import org.htmlparser.tags.SelectTag;  
import org.htmlparser.tags.TableColumn;  
import org.htmlparser.tags.TableRow;  
import org.htmlparser.tags.TableTag;  
import org.htmlparser.tags.TitleTag;  
import org.htmlparser.util.NodeIterator;  
import org.htmlparser.util.NodeList;  
import org.htmlparser.util.ParserException;  
import org.htmlparser.visitors.HtmlPage;  
import org.htmlparser.visitors.NodeVisitor;  
import org.htmlparser.visitors.ObjectFindingVisitor;  
  
public class ParserTestCase extends TestCase {  
  
    private static final Logger logger = Logger.getLogger(ParserTestCase.class);  
  
    public ParserTestCase(String name) {  
        super(name);  
    }  
    /* 
     * 测试ObjectFindVisitor的用法 
     */  
    public void testImageVisitor() {  
        try {  
            ImageTag imgLink;  
            ObjectFindingVisitor visitor = new ObjectFindingVisitor(  
                    ImageTag.class);  
            Parser parser = new Parser();  
            parser.setURL(”http://www.google.com”);  
            parser.setEncoding(parser.getEncoding());  
            parser.visitAllNodesWith(visitor);  
            Node[] nodes = visitor.getTags();  
            for (int i = 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 (Exception e) {  
            e.printStackTrace();  
        }  
    }  
    /* 
     * 测试TagNameFilter用法 
     */  
    public void testNodeFilter() {  
        try {  
            NodeFilter filter = new TagNameFilter(”IMG”);  
            Parser parser = new Parser();  
            parser.setURL(”http://www.google.com”);  
            parser.setEncoding(parser.getEncoding());  
            NodeList list = parser.extractAllNodesThatMatch(filter);  
            for (int i = 0; i < list.size(); i++) {  
                logger.fatal(”testNodeFilter() ” + list.elementAt(i).toHtml());  
            }  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
  
    }  
    /* 
     * 测试NodeClassFilter用法 
     */  
    public void testLinkTag() {  
        try {  
  
            NodeFilter filter = new NodeClassFilter(LinkTag.class);  
            Parser parser = new Parser();  
            parser.setURL(”http://www.google.com”);  
            parser.setEncoding(parser.getEncoding());  
            NodeList list = parser.extractAllNodesThatMatch(filter);  
            for (int i = 0; i < list.size(); i++) {  
                LinkTag node = (LinkTag) list.elementAt(i);  
                logger.fatal(”testLinkTag() Link is :” + node.extractLink());  
            }  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
  
    }  
    /* 
     * 测试<link href=” text=’text/css’ rel=’stylesheet’ />用法 
     */  
    public void testLinkCSS() {  
        try {  
  
            Parser parser = new Parser();  
            parser  
                    .setInputHTML(”<head><title>Link Test</title>”  
                            + “<link href=’/test01/css.css’ text=’text/css’ rel=’stylesheet’ />”  
                            + “<link href=’/test02/css.css’ text=’text/css’ rel=’stylesheet’ />”  
                            + “</head>” + “<body>”);  
            parser.setEncoding(parser.getEncoding());  
            NodeList nodeList = null;  
  
            for (NodeIterator e = parser.elements(); e.hasMoreNodes();) {  
                Node node = e.nextNode();  
                logger  
                        .fatal(”testLinkCSS()” + node.getText()  
                                + node.getClass());  
  
            }  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
    }  
    /* 
     * 测试OrFilter的用法 
     */  
    public void testOrFilter() {  
        NodeFilter inputFilter = new NodeClassFilter(InputTag.class);  
        NodeFilter selectFilter = new NodeClassFilter(SelectTag.class);  
        Parser myParser;  
        NodeList nodeList = null;  
  
        try {  
            Parser parser = new Parser();  
            parser  
                    .setInputHTML(”<head><title>OrFilter Test</title>”  
                            + “<link href=’/test01/css.css’ text=’text/css’ rel=’stylesheet’ />”  
                            + “<link href=’/test02/css.css’ text=’text/css’ rel=’stylesheet’ />”  
                            + “</head>”  
                            + “<body>”  
                            + “<input type=’text’ value=’text1′ name=’text1′/>”  
                            + “<input type=’text’ value=’text2′ name=’text2′/>”  
                            + “<select><option id=’1′>1</option><option id=’2′>2</option><option id=’3′></option></select>”  
                            + “<a href=’http://www.yeeach.com’>yeeach.com</a>”  
                            + “</body>”);  
  
            parser.setEncoding(parser.getEncoding());  
            OrFilter lastFilter = new OrFilter();  
            lastFilter.setPredicates(new NodeFilter[] { selectFilter,  
                    inputFilter });  
            nodeList = parser.parse(lastFilter);  
            for (int i = 0; i <= nodeList.size(); i++) {  
                if (nodeList.elementAt(i) instanceof InputTag) {  
                    InputTag tag = (InputTag) nodeList.elementAt(i);  
                    logger.fatal(”OrFilter tag name is :” + tag.getTagName()  
                            + ” ,tag value is:” + tag.getAttribute(”value”));  
                }  
                if (nodeList.elementAt(i) instanceof SelectTag) {  
                    SelectTag tag = (SelectTag) nodeList.elementAt(i);  
                    NodeList list = tag.getChildren();  
  
                    for (int j = 0; j < list.size(); j++) {  
                        OptionTag option = (OptionTag) list.elementAt(j);  
                        logger  
                                .fatal(”OrFilter Option”  
                                        + option.getOptionText());  
                    }  
  
                }  
            }  
  
        } catch (ParserException e) {  
            e.printStackTrace();  
        }  
    }  
    /* 
     * 测试对<table><tr><td></td></tr></table>的解析 
     */  
    public void testTable() {  
        Parser myParser;  
        NodeList nodeList = null;  
        myParser = Parser.createParser(”<body> ” + “<table id=’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>”  
                + “<table id=’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”);  
        NodeFilter tableFilter = new NodeClassFilter(TableTag.class);  
        OrFilter lastFilter = new OrFilter();  
        lastFilter.setPredicates(new NodeFilter[] { tableFilter });  
        try {  
            nodeList = myParser.parse(lastFilter);  
            for (int i = 0; i <= nodeList.size(); i++) {  
                if (nodeList.elementAt(i) instanceof TableTag) {  
                    TableTag tag = (TableTag) nodeList.elementAt(i);  
                    TableRow[] rows = tag.getRows();  
  
                    for (int j = 0; j < rows.length; j++) {  
                        TableRow tr = (TableRow) rows[j];  
                        TableColumn[] td = tr.getColumns();  
                        for (int k = 0; k < td.length; k++) {  
                            logger.fatal(”<td>” + td[k].toPlainTextString());  
                        }  
  
                    }  
  
                }  
            }  
  
        } catch (ParserException e) {  
            e.printStackTrace();  
        }  
    }  
    /* 
     * 测试NodeVisitor的用法,遍历所有节点 
     */  
    public void testVisitorAll() {  
        try {  
            Parser parser = new Parser();  
            parser.setURL(”http://www.google.com”);  
            parser.setEncoding(parser.getEncoding());  
            NodeVisitor visitor = new NodeVisitor() {  
                public void visitTag(Tag tag) {  
                    logger.fatal(”testVisitorAll()  Tag name is :”  
                            + tag.getTagName() + ” /n Class is :”  
                            + tag.getClass());  
                }  
  
            };  
  
            parser.visitAllNodesWith(visitor);  
        } catch (ParserException e) {  
            e.printStackTrace();  
        }  
    }  
    /* 
     * 测试对指定Tag的NodeVisitor的用法 
     */  
    public void testTagVisitor() {  
        try {  
  
            Parser parser = new Parser(  
                    “<head><title>dddd</title>”  
                            + “<link href=’/test01/css.css’ text=’text/css’ rel=’stylesheet’ />”  
                            + “<link href=’/test02/css.css’ text=’text/css’ rel=’stylesheet’ />”  
                            + “</head>” + “<body>”  
                            + “<a href=’http://www.yeeach.com’>yeeach.com</a>”  
                            + “</body>”);  
            NodeVisitor visitor = new NodeVisitor() {  
                public void visitTag(Tag tag) {  
                    if (tag instanceof HeadTag) {  
                        logger.fatal(”visitTag() HeadTag : Tag name is :”  
                                + tag.getTagName() + ” /n Class is :”  
                                + tag.getClass() + “/n Text is :”  
                                + tag.getText());  
                    } else if (tag instanceof TitleTag) {  
                        logger.fatal(”visitTag() TitleTag : Tag name is :”  
                                + tag.getTagName() + ” /n Class is :”  
                                + tag.getClass() + “/n Text is :”  
                                + tag.getText());  
  
  
                    } else if (tag instanceof LinkTag) {  
                        logger.fatal(”visitTag() LinkTag : Tag name is :”  
                                + tag.getTagName() + ” /n Class is :”  
                                + tag.getClass() + “/n Text is :”  
                                + tag.getText() + ” /n getAttribute is :”  
                                + tag.getAttribute(”href”));  
                    } else {  
                        logger.fatal(”visitTag() : Tag name is :”  
                                + tag.getTagName() + ” /n Class is :”  
                                + tag.getClass() + “/n Text is :”  
                                + tag.getText());  
                    }  
  
                }  
  
            };  
  
            parser.visitAllNodesWith(visitor);  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
    }  
    /* 
     * 测试HtmlPage的用法 
     */  
    public void testHtmlPage() {  
        String inputHTML = “<html>” + “<head>”  
                + “<title>Welcome to the HTMLParser website</title>”  
                + “</head>” + “<body>” + “Welcome to HTMLParser”  
                + “<table id=’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>”  
                + “<table id=’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>”;  
        Parser parser = new Parser();  
        try {  
            parser.setInputHTML(inputHTML);  
            parser.setEncoding(parser.getURL());  
            HtmlPage page = new HtmlPage(parser);  
            parser.visitAllNodesWith(page);  
            logger.fatal(”testHtmlPage -title is :” + page.getTitle());  
            NodeList list = page.getBody();  
  
            for (NodeIterator iterator = list.elements(); iterator  
                    .hasMoreNodes();) {  
                Node node = iterator.nextNode();  
                logger.fatal(”testHtmlPage -node  is :” + node.toHtml());  
            }  
  
        } catch (ParserException e) {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
        }  
    }  
    /* 
     * 测试LinkBean的用法 
     */  
    public void testLinkBean() {  
        Parser parser = new Parser();  
  
        LinkBean linkBean = new LinkBean();  
        linkBean.setURL(”http://www.google.com”);  
        URL[] urls = linkBean.getLinks();  
  
        for (int i = 0; i < urls.length; i++) {  
            URL url = urls[i];  
            logger.fatal(”testLinkBean() -url  is :” + url);  
        }  
  
    }  
  

原创粉丝点击