`
kqy929
  • 浏览: 19478 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

HtmlParser设计解析(1) - 解析器模式(Interpreter)

阅读更多

      对于HtmlParser的使用,这方面的介绍很多,而且详细。前段时间我将HtmlParser的源码读了一篇,在此,总结下其HtmlParser的设计,跟大家交流,我们只关注是设计。

      一、Filter设计

      NodeFilter 是htmlParser主要的提取节点的一种方式,其结构灵活,通过组合解释器查找页面上的任一个节点。

      1、先看个测试用例:

    /**
     * Test and filtering.
     */
    public void testAnd () throws ParserException
    {
        String guts;
        String html;
        NodeList list;

        guts = "<body>Now is the <a id=one><b>time</b></a> for all good <a id=two><b>men</b></a>..</body>";
        html = "<html>" + guts + "</html>";
        createParser (html);
        list = parser.extractAllNodesThatMatch (
            new AndFilter (
                new HasChildFilter (
                    new TagNameFilter ("b")),
                new HasChildFilter (
                    new StringFilter ("men")))
                );
        assertEquals ("only one element", 1, list.size ());
        assertType ("should be LinkTag", LinkTag.class, list.elementAt (0));
        LinkTag link = (LinkTag)list.elementAt (0);
        assertEquals ("attribute value", "two", link.getAttribute ("id"));
    }

 

       2、NodeFilter 结构图

      

     3、所使用的设计模式

      NodeFilter接口的主要作用是判断该节点是否是客户端所查找的节点,返回一个boolean值。从上图中也可以看出,其接口中只有一个方法:

     boolean accept (Node node); //接受一个Node类型的参数

     在这,HtmlParser作者采用的是解析器模式来实现这个模式。

     我们先了解下解释器模式,然后再结合作者的源码来理解解释器模式,体会作者的设计灵活性。

    Interpreter模式可以定义出其方法的一种表示,并同时提供一个解释器。客户端可以使用解释器来解释这个语言中的句子。

    其中,Interpreter模式的几个要点:

    1、Interpreter模式应用场合是Interpreter模式应用中的难点,只有满足“业务规则频繁变化,且类似的模式不断重复出现,并且容易抽象为语法规则问题”才适合使用Interpreter模式

    2、使用Interpreter模式来表示方法规则,从而可以使用面向对象技艺来方便地“扩展”方法。

 

    4、HtmlParser NodeFilter 解释器模式的应用

    抽象表达式角色:    

public interface NodeFilter extends Serializable, Cloneable {
    /**
     * Predicate to determine whether or not to keep the given node.
     * The behaviour based on this outcome is determined by the context
     * in which it is called. It may lead to the node being added to a list
     * or printed out. See the calling routine for details.
     * @return <code>true</code> if the node is to be kept, <code>false</code>
     * if it is to be discarded.
     * @param node The node to test.
     */
    boolean accept (Node node);
}

 

   下面看一个逻辑“与”的操作的实现,这里表示二个过滤器通过逻辑与操作给出一个boolean表达式的操作。代码如下:   

/**
 * Accepts nodes matching all of its predicate filters (AND operation).
 */
public class AndFilter implements NodeFilter {
	protected NodeFilter[] mPredicates;

	/**
	 * Creates an AndFilter that accepts nodes acceptable to both filters.
	 * 
	 * @param left One filter.
	 * @param right The other filter.
	 */
	public AndFilter(NodeFilter left, NodeFilter right) {
		NodeFilter[] predicates;

		predicates = new NodeFilter[2];
		predicates[0] = left;
		predicates[1] = right;
		setPredicates(predicates);
	}

	public void setPredicates(NodeFilter[] predicates) {
		if (null == predicates)
			predicates = new NodeFilter[0];
		mPredicates = predicates;
	}

	public boolean accept(Node node) {
		boolean ret;

		ret = true;

		for (int i = 0; ret && (i < mPredicates.length); i++)
			if (!mPredicates[i].accept(node)) // 这里调用本身构造的解释器再进行判断
				ret = false;

		return (ret);
	}
}

 

  再来看一个测试用例中的另外一些过滤操作,HasChildFilter 其代码如下:  

public class HasChildFilter implements NodeFilter {
	protected NodeFilter mChildFilter;

	protected boolean mRecursive;

	public HasChildFilter(NodeFilter filter) {
		this(filter, false);
	}

	public HasChildFilter(NodeFilter filter, boolean recursive) {
		mChildFilter = filter;
		mRecursive = recursive;
	}

	public boolean accept(Node node) {
		CompositeTag tag; // ?1
		NodeList children;
		boolean ret;

		ret = false;
		if (node instanceof CompositeTag) {
			tag = (CompositeTag) node;
			children = tag.getChildren();
			if (null != children) {
				for (int i = 0; !ret && i < children.size(); i++)
					if (mChildFilter.accept(children.elementAt(i))) // 判断是否包括该元素
						ret = true;
				// do recursion after all children are checked
				// to get breadth first traversal
				if (!ret && mRecursive) // 搜索下层节点
					for (int i = 0; !ret && i < children.size(); i++)
						if (accept(children.elementAt(i)))
							ret = true;
			}
		}

		return (ret);
	}
}

 

   TagNameFilter 的代码如下:  

public class TagNameFilter implements NodeFilter {
	protected String mName;

	public TagNameFilter(String name) {
		mName = name.toUpperCase(Locale.ENGLISH);
	}

	public boolean accept(Node node) {
		return ((node instanceof Tag) 
				&& !((Tag) node).isEndTag() 
				&& ((Tag) node).getTagName().equals(mName));
	}
}

  NodeFilter的另外13个子类,都按此实现包装不同的业务逻辑。并且非常容易增加其子类来实现新的“文法”规则。

   客户端则可灵活组装解释器,执行解释。非常灵活,这也满足用户自定义逻辑去查找HTML文件中的各个节点。

   至于HtmlParser是如何人存储HTML结构,在此不做深挖,只需要知道将提供一个迭代器可遍历所有的节点即可(其实HtmlParser中是通过遍历各个字符来映射Node对象及装载各字符的坐标(列数,行数))。

 

   5、HtmlParser中客户端的调用

   现在来看看测试用例中的Parser类中extractAllNodesThatMatch()。

   Parser: 

public class Parser implements Serializable {
    ... ....

    /**
     * Extract all nodes matching the given filter.
    */
    public NodeList extractAllNodesThatMatch (NodeFilter filter) throws ParserException {
        NodeIterator e;
        NodeList ret;

        ret = new NodeList ();
        for (e = elements (); e.hasMoreNodes (); ) // elements()返回一个简单的迭代器,遍历所有节点
            e.nextNode ().collectInto (ret, filter);

        return (ret);
    }
    ... ...
}

   AbstractNode:  

public abstract class AbstractNode implements Node, Serializable {
   ... ...
    public void collectInto (NodeList list, NodeFilter filter) {
        if (filter.accept (this))
            list.add (this);
    }
    ... ...
}

 

public class CompositeTag extends TagNode { //TagNode extends AbstractNode, AbstractNode implements Node
    ... ...
    public void collectInto (NodeList list, NodeFilter filter) {
        super.collectInto (list, filter); //AbstractNode collectInto
        for (SimpleNodeIterator e = children(); e.hasMoreNodes ();) {
            // e.nextNode() 返回一个Node类型 e.nextNode ().collectInto() = this.collectInto() 递归遍历所有节点,并对每个节点进行过滤,将符合条件的节点添加至结果集中(NodeList)
            e.nextNode ().collectInto (list, filter); 
        }
        if ((null != getEndTag ()) && (this != getEndTag ()))     
                  getEndTag ().collectInto (list, filter);
    }
    ... ... 
}

 

  • 描述: HtmlParser NodeFilter
  • 大小: 70.4 KB
3
0
分享到:
评论
2 楼 deng_1987 2011-12-08  
5、HtmlParser中客户端的调用
// elements()返回一个简单的迭代器,遍历所有节点
我利用断点 观察
e.nextNode ().collectInto (ret, filter);
这句话其实在遍历最上层节点,但是AbstractNode的collectInto()方法是遍历所有节点,让人很纠结。。不知道怎么原因
1 楼 iammonster 2009-08-12  
NodeFilter 比较灵活,不错。

相关推荐

    前端开源库-parse5-htmlparser2-tree-adapter

    Parse5是一个遵循HTML5规范的JavaScript解析器,其设计目标是提供高效、准确且易于使用的API来解析和序列化HTML文档。Parse5的主要特点包括对HTML5标准的严格遵循,支持最新的特性和语法,以及对错误处理的精细控制...

    java解析html工具htmlparser的jar包及api文档

    在提供的压缩包中,`HTMLParser-2.0-SNAPSHOT-bin`可能是可执行的二进制文件,包括了编译好的JAR文件和其他运行所需的资源。而`HTMLParser-2.0-SNAPSHOT`可能包含了源代码,这对于开发者来说是宝贵的,因为他们可以...

    HtmlParser学习笔记-- htmlparser简介

    HtmlParser 是一个用于解析HTML文档的Java库,它允许开发者以结构化的方式处理HTML内容,以便提取信息或进行数据抓取。在HtmlParser中,HTML页面的结构通过三种主要的数据结构来表示:Node、AbstractNode和Tag。 1....

    HTMLParser-2.0-SNAPSHOT

    这个"HTMLParser-2.0-SNAPSHOT"版本可能是HTMLParser的一个开发版本或测试版本,包含了最新的特性和改进。 HTMLParser提供了丰富的API,使得开发者可以方便地操作HTML元素,如标签、属性和文本。以下是一些关键的...

    前端开源库-htmlparser-to-html

    1. **数据解析**:在爬虫项目中,可以使用`htmlparser`先解析HTML页面,然后用`htmlparser-to-html`将解析后的数据还原,以便进一步处理或存储。 2. **模板渲染**:在前端模板引擎中,可以先将HTML模板转换成JSON,...

    HTMLParser-2.0-SNAPSHOT-bin.zip JAVA html解析库

    if (end == -1) end = frame.indexOf("&gt;"); String frameUrl = frame.substring(5, end - 1); if (filter.accept(frameUrl)) links.add(frameUrl); } } } catch (ParserException e) {//捕捉parser的...

    基于java的开发源码-HTML文档解析器 HTMLParser.zip

    基于java的开发源码-HTML文档解析器 HTMLParser.zip 基于java的开发源码-HTML文档解析器 HTMLParser.zip 基于java的开发源码-HTML文档解析器 HTMLParser.zip 基于java的开发源码-HTML文档解析器 HTMLParser.zip 基于...

    HTMLParser-2.0-API.CHM

    HTMLParser-2.0-API.CHM 是一个关于HTMLParser 2.0版本API的离线帮助文档,通常用于Java开发者在处理HTML解析时参考。HTMLParser是一个开源的Java库,设计用于解析HTML文档,提取结构化信息或者进行数据抓取。这个...

    htmlparser-1.6p.jar

    "htmlparser-1.6p.jar"是该库的特定版本,用于在Java环境中集成和使用。 HTMLParser的核心功能包括: 1. **标签和属性处理**:它可以识别并解析HTML文档中的各种标签,如`&lt;html&gt;`, `&lt;head&gt;`, `&lt;body&gt;`等,同时处理...

    HtmlParser的使用

    HTMLParser支持过滤器(Filter)和访问者模式(Visitor),允许用户根据需求选择感兴趣的节点进行处理。例如,如果你想提取所有的链接(a标签),可以创建一个`TagFilter`: ```java import org.htmlparser.filters...

    正则表达式+_HTMLParser使用详解-2010-03-21

    正则表达式(Regular Expression)是一种模式匹配语言,常用于字符串的查找、替换和提取等操作,而HTMLParser则是用来解析HTML文档结构的工具,尤其在网页抓取和信息提取中不可或缺。 一、正则表达式 1. **基本...

    htmlparser_Java网页解析器

    在事件驱动模式下,HTMLParser会监听并触发一系列的解析事件,如遇到开始标签、结束标签、文本内容等。开发者可以通过注册事件处理器来响应这些事件,从而提取所需的数据。这种方式对于处理大量HTML文档且只需要关注...

    htmlparser1_6.jar

    3. **事件驱动模型**:HTMLParser采用事件驱动的解析模式。当解析器遇到HTML元素、属性或其他结构时,会触发相应的事件,开发者可以通过监听这些事件来执行自定义操作。 4. **灵活性**:HTMLParser允许用户自定义...

    Winista.Htmlparser.Net 解析Html 的.net类库

    HtmlParser.Net是来源于Java的一个用来解析html的组件,主要用于改造或提取html。它能够高速解析html,是非常好的一个html解析和分析工具。 这个是.Net版本包括源代码和帮助文档。 版本:HTMLParser.Net - Community...

    htmlparser解析Html的jar包和源文件包(两个)

    - **事件驱动的解析模型**:通过监听器模式,用户可以注册事件处理器,当解析到特定标签、文本或其他元素时触发回调。 - **DOM树构建**:HTMLParser可以构建一个DOM树表示HTML文档,方便进行结构化查询和操作。 - ...

    htmlparser-c++

    在使用HTMLParser-C++时,开发人员需要包含相关的头文件,如`htmlparser.h`,然后创建解析器实例,例如`HTMLParser parser`。解析器通常会有一个解析HTML字符串或文件的方法,如`parseString`或`parseFile`。解析...

Global site tag (gtag.js) - Google Analytics