扩展 HTMLParser 对自定义标签的处理能力

转载 2011年01月19日 08:34:00

HTMLParser 是一个用来解析 HTML 文档的开放源码项目,它具有小巧、快速、使用简单的特点以及拥有强大的功能。对该项目还不了解的朋友可以参照 2004 年三月份我发表的文章--《从HTML中攫取你所需的信息》,这篇文章介绍如何通过 HTMLParser 来提取 HTML 文档中的文本数据以及提取出文档中的所有链接或者是图片等信息。

现在该项目的最新版本是 Integration Build 1.6,与之前版本的差别在于代码结构的调整、当然也有一些功能的提升以及 BugFix,同时对字符集的处理也更加自动了。比较遗憾的该项目并没有详尽的使用文档,你只能借助于它的 API 文档、一两个简单例子以及源码来熟悉它。

如果是 HTML 文档,那么用 HTMLParser 已经差不多可以满足你至少 90% 的需求。一个 HTML 文档中可能出现的标签差不多在 HTMLParser 中都有对应的类,甚至包括一些动态的脚本标签,例如 <%...%> 这种 JSP 和 ASP 用到的标签都有相应的 JspTag 对应。HTMLParser 的强大功能还体现在你可以修改每个标签的属性或者它所包含的文本内容并生成新的 HTML 文档,比如你可以文档中的链接地址偷偷的改成你自己的地址等等。关于 HTMLParser 的强大功能,其实上一篇文章已经介绍很多,这里不再累赘,我们今天要讲的是另外一个用途--处理自定义标签。

首先我们先解释一下什么叫自定义标签,我把所有不是 HTML 脚本语言中定义的标签称之为自定义标签,比如可以是 <scriptlet>、<book> 等等,这是我们自己创造出来的标签。你可能会很奇怪,因为这些标签一旦用在 HTML 文档中是没有任何效果的,那么我们换另外一个例子,假如你要解析的不是 HTML 文档,而是一个 WML(Wireless Markup Lauguage)文档呢?WML 文档中的 card,anchor 等标签 HTMLParser 是没有现成的标签类来处理的。还有就是你同样可以用 HTMLParser 来处理 XML 文档,而 XML 文档中所有的标签都是你自己定义的。

为了使我们的例子更具有代表意义,接下来我们将给出一段代码用来解析出 WML 文档中的所有链接,了解 WML 文档的人都知道,WML 文档中除了与 HTML 文档相同的链接写法外,还多了一种标签叫 <anchor>,例如在一个 WML 文档我们可以用下面两种方式来表示一个链接。


<a href="http://www.javayou.com?cat_id=1">Java自由人</a>
或者:
<anchor>
Java自由人
    <go href="http://www.javayou.com" method="get">
        <postfield name="cat_id" value="1"/>
</go>
</anchor>

(更多的时候使用 anchor 的链接用来提交一个表单。) 如果我们还是使用 LinkTag 来遍历整个 WML 文档的话,那 Anchor 中的链接将会被我们所忽略掉。

下面我们先给出一个简单的例子,然后再叙述其中的道理。这个例子包含两个文件,一个是WML 的测试脚本文件 test.wml,另外一个是 Java 程序文件 HyperLinkTrace.java,内容如下:

1. test.wml


<?xml version="1.0"?>
<!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN"
"http://www.wapforum.org/DTD/wml_1.1.xml">
<wml>
<card title="Java自由人登录">
<p>  
	用户名:<input type="text" name="username" size="15"/>
     密码:<input type="text" name="password" size="15"/>
	<br/>
	<anchor>现在登录
		<go href="/wap/user.do" method="get">
    		<postfield name="name" value="$(username)"/>
    		<postfield name="password" value="$(password)"/>
    		<postfield name="eventSubmit_Login" value="WML"/>
		</go>
	</anchor><br/>
	<a href="/wap/index.vm">返回首页</a>
</p>
</card>
</wml>

test.wml 中的粗体部分是我们需要提取出来的链接。

2. HyperLinkTrace.java


package demo.htmlparser;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.net.URL;
import org.htmlparser.Node;
import org.htmlparser.NodeFilter;
import org.htmlparser.Parser;
import org.htmlparser.PrototypicalNodeFactory;
import org.htmlparser.tags.CompositeTag;
import org.htmlparser.tags.LinkTag;
import org.htmlparser.util.NodeList;
/**
 * 用来遍历WML文档中的所有超链接
 * @author Winter Lau
 */
public class HyperLinkTrace {
	public static void main(String[] args) throws Exception {
		//初始化HTMLParser
		Parser parser = new Parser();
		parser.setEncoding("8859_1");
		parser.setInputHTML(getWmlContent());
		
		//注册新的结点解析器
		PrototypicalNodeFactory factory = new PrototypicalNodeFactory ();
		factory.registerTag(new WmlGoTag ());
		parser.setNodeFactory(factory);
		//遍历符合条件的所有节点
		NodeList nlist = parser.extractAllNodesThatMatch(lnkFilter);
		for(int i=0;i<nlist.size();i++){
			CompositeTag node = (CompositeTag)nlist.elementAt(i);
			if(node instanceof LinkTag){
				LinkTag link = (LinkTag)node;
				System.out.println("LINK: /t" + link.getLink());
			}
			else if(node instanceof WmlGoTag){
				WmlGoTag go = (WmlGoTag)node;
				System.out.println("GO: /t" + go.getLink());
			}
		}
	}
	/**
	 * 获取测试的WML脚本内容
	 * @return
	 * @throws Exception
	 */
	static String getWmlContent() throws Exception{
		URL url = ParserTester.class.getResource("/demo/htmlparser/test.wml");
		File f = new File(url.toURI());
		BufferedReader in = new BufferedReader(new FileReader(f));
		StringBuffer wml = new StringBuffer();
		do{
			String line = in.readLine();
			if(line==null)
				break;
			if(wml.length()>0)
				wml.append("/r/n");
			wml.append(line);			
		}while(true);
		return wml.toString();		
	}
	/**
	 * 解析出所有的链接,包括行为<a>与<go>
	 */
	static NodeFilter lnkFilter = new NodeFilter() {
		public boolean accept(Node node) {
			if(node instanceof WmlGoTag)
				return true;
			if(node instanceof LinkTag)
				return true;
			return false;
		}
	};
	
	/**
	 * WML文档的GO标签解析器
	 * @author Winter Lau
	 */
	static class WmlGoTag extends CompositeTag {
	    private static final String[] mIds = new String[] {"GO"};
	    private static final String[] mEndTagEnders = new String[] {"ANCHOR"};
	    public String[] getIds (){
	        return (mIds);
	    }
	    public String[] getEnders (){
	        return (mIds);
	    }
	    public String[] getEndTagEnders (){
	        return (mEndTagEnders);
	    }
	    
	    public String getLink(){
	    	return super.getAttribute("href");
	    }
	    
	    public String getMethod(){
	    	return super.getAttribute("method");
	    }
	}
}

上面这段代码比较长,可以分成下面几部分来看:

1. getWmlContent方法: 该方法用来获取在同一个包中的test.wml脚本文件的内容并返回字符串。

2. 静态属性lnkFilter:这是一个NodeFilter的匿名类所构造的实例。该实例用来传递给HTMLParser告知需要提取哪些节点。在这个例子中我们仅需要提取链接标签以及我们自定义的一个GO标签。

3. 嵌套类WmlGoTag:这也是最为重要的一部分,这个类用来告诉HTMLParser如何去解析<go>这样一个节点。我们先看看下面这个HTMLParser的节点类层次图:


 

如上图所示,HTMLParser将一个文档分成三种节点分别是:Remark(注释);Text(文本);Tag(标签)。而标签又分成两种分别是简单标签(Tag)和复合标签(CompositeTag),像<img><br/>这种标签称为简单标签,因为标签不会再包含其它内容。而像<a href="xxxx">Home</a>这种类型的标签,因为标签会嵌套文本或者其他标签的称为复合标签,也就是对应着CompositeTag这个类。简单标签的实现类很简单,只需要扩展Tag类并覆盖getIds方法以返回标签的识别文本,例如<img>标签应该返回包含"img"字符串的数组,具体的代码可以参考HTMLParser自带的ImageTag标签类的实现。

从上图可清楚看出,复合标签事实上是对简单标签的扩展,HTMLParser在处理一个复合标签时需要知道该标签的起始标识以及结束标识,也就是我们在前面给出的源码中的两个方法getIds和getEnders,一般来讲,标签出现都是成对的,因此这两个方法一般返回相同的值。另外一个方法getEndTagEnders,这个方法用来返回父一级的标签名称,例如<tr>的父一级标签应该是<table>。这个方法的必要性在于HTML对格式的要求很不严格,在很多的HTML文档中的一些标签经常是有开始标识,但是没有结束标识,由于浏览器的超强适应能力使这种情况出现的很频繁,因此HTMLParser利用这个方法来辅助判断一个标签是否已经结束。由于WML文档的格式要求非常严格,因此上例源码中的getEndTagEnders方法事实上可有可无。

4. 入口方法main:该方法初始化HTMLParser并注册新的节点解析器,解析文档并打印运行结果。

最后我们编译并运行这个例子,便可以得到下面的运行结果:


GO: 	/wap/user.do
LINK: 	/wap/index.vm

HTMLParser本身就是一个开放源码的项目,它对于HTML文档中出现的标签定义已经应有尽有,我们尽可以参考这些标签解析类的源码来学习如何实现一个标签的解析类,从而扩展出更丰富多彩的应用程序。

扩展 HTMLParser 对自定义标签的处理能力

本文阐述如何利用 HTMLParser 项目对 HTML 或者 WML 文档中出现的一些特殊的或者是自定义的标签进行处理。 HTMLParser 是一个用来解析 HTML 文档的开放源码项目,它...
  • qiezikuaichuan
  • qiezikuaichuan
  • 2015年10月09日 11:20
  • 163

HTMLParser使用详解(5)- 扩展 HTMLParser 对自定义标签的处理能力

HTMLParser使用详解(5)- 扩展 HTMLParser 对自定义标签的处理能力 HTMLParser 是一个用来解析 HTML 文档的开放源码项目,它具有小巧、快速、使用简单的特点...
  • mwq30123
  • mwq30123
  • 2014年05月29日 13:22
  • 308

【垂直搜索引擎搭建11】使用htmlparser获取页面的字符编码encoding

1,确定目标。对于html页面来说,一般都有确定编码的语句:可以通过这一行的特征来取出网页的编码。2,选出特征。      1)它是meta标签       2)具有http-equiv属性值为Co...
  • kevinelstri
  • kevinelstri
  • 2016年08月24日 17:04
  • 484

大数据处理之道 (htmlparser获取数据<一>)

一:简介 (1)HTML Parser是一个用于解析Html的Java的库,可采用线性或嵌套两种方式。主要用于网页的转换或提取,他有一些特性:过滤器filter,遍历器visitors,通常的标签t...
  • u010700335
  • u010700335
  • 2014年12月24日 11:12
  • 2097

htmlparser解析网站时服务器返回的文件编码和页面编码不一致问题

1、用htmlparser解析http://gz.fang.com/抛出如下异常: Exception in thread "main" org.htmlparser.util.EncodingCha...
  • fjssharpsword
  • fjssharpsword
  • 2016年02月29日 10:34
  • 3044

spring 自定义标签 学习

自定义配置文件到spring 中,有时候想做一些数据结构的配置化信息,根据业务做一个扩展。   首先: 在项目的META-INF目录下新建两个文件spring.handlers,和spring....
  • ruishenh
  • ruishenh
  • 2014年06月23日 17:37
  • 5022

基于mybatis 自定义标签分页的实现

ssm框架 ,mybatis 分页一直以来都是一个难题,博主也是参考大神的代码,整合到项目中的,废话不多说,看代码0、最最首先是把page 基础类加进来了...
  • TT_DUL
  • TT_DUL
  • 2016年06月06日 16:55
  • 1864

基于htmlparser实现网页内容解析

网页解析,即程序自动分析网页内容、获取信息,从而进一步处理信息。 网页解析是实现网络爬虫中不可缺少而且十分重要的一环.。 首先,必须说在最前的是我们使用的工具——htmlparser 简要地说,htm...
  • sx_ygl
  • sx_ygl
  • 2016年05月17日 14:03
  • 1664

Spring自定义标签示例(亲测可运行)

看这篇博文要有对Spring有一定了解,还有对xsd schema文档定义语言也要有所知,对spring源码兴趣,自定义标签有特殊使用方式的可以看下。...
  • shinebar
  • shinebar
  • 2017年07月22日 00:14
  • 861

Dubbo源码分析(三):自定义Schema--基于Spring可扩展Schema提供自定义配置支持(spring配置文件中 配置标签支持)

在很多情况下,我们需要为系统提供可配置化支持,简单的做法可以直接基于Spring的标准Bean来配置,但配置较为复杂或者需要更多丰富控制的时候,会显得非常笨拙。一般的做法会用原生态的方式去解析定义好的...
  • ZuoAnYinXiang
  • ZuoAnYinXiang
  • 2016年05月06日 14:10
  • 1981
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:扩展 HTMLParser 对自定义标签的处理能力
举报原因:
原因补充:

(最多只允许输入30个字)