`
wjboy49
  • 浏览: 274855 次
  • 性别: Icon_minigender_1
  • 来自: 湖南岳阳
社区版块
存档分类
最新评论

htmlparser 中修改 HTML 内容的方法

    博客分类:
  • java
阅读更多

    htmlparserHTML Parser )是 sourceforge.net 上的一个成熟的 java 项目。它可以解析 HTML 页面,用来抽取或修改其内容,通过过滤器、访问者来访问程序关心的标签(Tag )。

 

htmlparser 写道
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.

 

          一般使用 htmlparser 来做 HTML 解析后的抽取工作,但对 HTML 内容进行结构化修改则比较少用到。前段有一个对 HTML 抓取后修改其中所有链接地址的需求,使用 htmlparser 将 HTML 页面中附带资源(non html resource)的 URL 地址都做一下转换,包括链接(LinkTag)、图片(ImageTag)、框架页(FrameTag)、表单(FormTag)标签中指定的资源地址, 还包括 head 中的 link(HeaderLinkTag)标签中指定的 CSS/favicon.ico 以及 script(ScriptExTag)标签中指定的 JS 文件资源地址。对于后二者 HeaderLinkTag 和 ScriptExTag 标签的处理功能在 htmlparser 是没有实现的,需要自己通过编写继承于 TagNode/CompositeTag 基类的自定义标签来做匹配、修改逻辑。

 

          OK 言归正传,下面给出在 htmlparser 中修改 HTML 内容的 2 种方法。

 

          首先,第 1 种修改方法可以通过自定义继承 UrlModifyingVisitor 的子类来自定义修改 URL 逻辑,通过 org.htmlparser .Parser 提供的遍历模式来完成修改。 直接上代码。

import java.net.MalformedURLException;
import java.net.URL;
import java.util.logging.Logger;

import org.htmlparser.Tag;
import org.htmlparser.Text;
import org.htmlparser.tags.FormTag;
import org.htmlparser.tags.FrameTag;
import org.htmlparser.tags.ImageTag;
import org.htmlparser.tags.LinkTag;
import org.htmlparser.visitors.UrlModifyingVisitor;

import org.lzy.fwswaper.FwswaperServlet;
import org.lzy.fwswaper.htmlhandler.HtmlHandlerHelper;
import org.lzy.fwswaper.util.ExceptionUtils;

public class HtmlparserUrlModifier extends UrlModifyingVisitor
{
	private static final Logger log =
		Logger.getLogger(HtmlparserUrlModifier.class.getName());

	private URL base = null;
	
	public HtmlparserUrlModifier(URL base)
	{
		super("");
		
		this.setBaseUrl(base);
	}

	public void setBaseUrl(URL base)
	{
		if (!HtmlHandlerHelper.isHttpLikeProtocolUrl(base))
			throw new IllegalArgumentException(String.format(
					"Base url argument '%s' is not http like protocol. " +
					"They are not prefix with '%s' or '%s'", this.base.toString(),
					HtmlHandlerHelper.HttpProtocol, HtmlHandlerHelper.HttpsProtocol));

		this.base = base;
	}
	
    public void visitStringNode(Text stringNode)
    {
    	// MUST override this method.
    	// Super class UrlModifingVistor wrote: 'this.modifiedResult.append (stringNode.toHtml());'.
    	// It will append stringNode.toHtml() conent to outside of <html/> tag if not override it.
    }

	public void visitTag(Tag tag)
	{
		try {
	    	if (tag instanceof LinkTag) {

				LinkTag link = (LinkTag) tag;

				log.info(String.format("Found link: '%s' => '%s'.", link.getLinkText(), link.extractLink()));

				if (link.isHTTPLikeLink())
					link.setLink(this.modifying(new URL(base, link.getLink())));
				
			} else if (tag instanceof HeaderLinkTag) {

				HeaderLinkTag link = (HeaderLinkTag) tag;

				log.info(String.format("Found head link: '%s' => '%s'.", link.getLinkText(), link.getLink()));
				
				URL url = new URL(base, link.getLink());
				
				if (HtmlHandlerHelper.isHttpLikeProtocolUrl(url))
					link.setLink(this.modifying(url));

			} else if (tag instanceof ScriptExTag) {

				ScriptExTag script = (ScriptExTag) tag;

				String src = script.getSrc();

				if ((src != null) && (src.length() > 0)) {
					
					log.info(String.format("Found script: '%s' => '%s'.", script.getLanguage(), src));

					URL url = new URL(base, src);

					if (HtmlHandlerHelper.isHttpLikeProtocolUrl(url))
						script.setSrc(this.modifying(url));
				}

			} else if (tag instanceof ImageTag) {	

				ImageTag img = (ImageTag) tag;	

				log.info(String.format("Found image => '%s'.", img.getImageURL()));
				
				URL url = new URL(base, img.getImageURL());
				
				if (HtmlHandlerHelper.isHttpLikeProtocolUrl(url))
					img.setImageURL(this.modifying(url));
				
			} else if (tag instanceof FrameTag) {

				FrameTag frame = (FrameTag) tag;
				
				log.info(String.format("Found frame: '%s' => '%s'.", frame.getText(), frame.getFrameLocation()));
				
				URL url = new URL(base, frame.getFrameLocation());
				
				if (HtmlHandlerHelper.isHttpLikeProtocolUrl(url))
					frame.setFrameLocation(this.modifying(url));
				
			} else if (tag instanceof FormTag) {
				
				FormTag form = (FormTag) tag;
				
				log.info(String.format("Found form: '%s' => (%s) '%s'.", form.getFormName(), form.getFormMethod(), form.extractFormLocn()));
				
				URL url = new URL(base, form.extractFormLocn());
				
				if (HtmlHandlerHelper.isHttpLikeProtocolUrl(url))
					form.setFormLocation(this.modifying(url));
			}

		} catch(Exception e) {
			log.warning(String.format("Modify url failed. Exception message: '%s'.",
					ExceptionUtils.getStackTrace(e)));
		}

		super.visitTag(tag);
	}
	
	protected String modifying(URL url) throws MalformedURLException
	{
		// Modifying url and return.
		
		return null;
	}
}

 

PrototypicalNodeFactory factory = new PrototypicalNodeFactory();
factory.registerTag(new HeaderLinkTag());
factory.registerTag(new ScriptExTag());

Parser parser = Parser.createParser(html, charset);
parser.setNodeFactory(factory);

// Match and modify link image and frame tag

 url address.
HtmlparserUrlModifier modifier = new HtmlparserUrlModifier(this.base);

parser.visitAllNodesWith(modifier);

String html = modifier.getModifiedResult();

 

          通过上面的 HtmlparserUrlModifier 中的具体处理,并在 org.htmlparser .PrototypicalNodeFactory 中注册 HeaderLinkTag 和 ScriptExTag 这 2 个要匹配的自定义标签类型,我们就可以对 html 内容进行结构化修改了,实际看了 htmlparser 的源码就会发现根本上最后就是 setAttribute 方法的调用。

          这里有两个问题需要说明:

 

          1. 在继承 UrlModifyingVisitor 对它进行扩展时,一定要重载其 visitStringNode 方法,否则会发现在 htmlparser 处理后的结果中,在 html 标签外还会有页面所有的文本内容的副本,这里的文本是指那些用于在浏览器中显示的文字内容。正如上述代码中所示,在我重载的 visitStringNode 方法中没有做任何处理。通过 UrlModifyingVisitor 源码可以看到 visitStringNode 方法默认实现如下。

 


public void visitStringNode(Text stringNode)
{
	modifiedResult.append (stringNode.toHtml());
}

 

          2. 不能通过 org.htmlparser .Parser 类的 parse 方法在解析过程中对 html 内容进行修改,因为在解析完成后,你会发现必须通过 reset 方法来复位,这样之前的处理结果就全部失效了。

 

          其次,第 2 种方法是通过 org.htmlparser .util.NodeList 保存结构化的 html 内容并对其修改,最后通过它的 toHtml 方法将修改结果导出。 示例代码如下所示。

 


private String parse(String html, String charset) throws ParserException {
	Parser parser = Parser.createParser(html, charset);
	NodeList list = parser.parse(null);
	String html = recurse(list).toHtml();
	System.out.println(html);
}

private NodeList recurse(NodeList list) {
	if(list==null)
		return null;
	
	Node node = null;
	SimpleNodeIterator iterator = list.elements();
	
	while(iterator.hasMoreNodes())
	{
		node = iterator.nextNode();
		if(node==null)
			break;
		if(node instanceof Tag

)
		{
			Tag

 tag

 = (Tag

)node;
			
			// Modifying attributes or something else.

			recurse(node.getChildren());
		}
	}
	
	return null;
}

 

          好了,这次要说的就是上面的这 2 个方法,希望能对有需要的兄弟有所帮助。记得上次在问答频道里有人问过这问题。

 

作者:lzy.je
出处:http://lzy.iteye.com

分享到:
评论

相关推荐

    htmlParser2.0.jar

    html解析器, 可满足90%以上的需求,可以分析自定义的标签, 开源项目, 最近4年没有修改记录, 2011.04.24有人再次进行了提交. 最新的htmlParser

    html解析的利器,htmlParser

    如果要对HTML进行解析,提取HTML的数据或者修改HTML数据,HtmlParser是一个不错的选择,而且非常的简单好用

    htmlParser:简单的 JavaScript HTML 解析器

    特此授予任何人免费获得本软件副本和相关文档文件(“软件”)的许可,不受限制地处理本软件,包括但不限于使用、复制、修改、合并的权利、发布、分发、再许可和/或出售软件的副本,并允许向其提供软件

    react-html-parser:直接将HTML字符串转换为React组件,并提供一种简单的方法来修改和替换内容

    将标准HTML元素,属性和内联样式转换为它们的React等效项,并提供一种修改和替换内容的简单方法。 安装 npm install react-html-parser # or yarn add react-html-parser 用法 import React from 'react' ; import ...

    微信小程序-微信小程序html转wxml

    usage: ...或 引入src里面html2json.js/html2wxml.wxml/htmlparser.js/example.wxss...在example.wxss中,可自定义添加修改html标签对应的样式,以满足不同的需求,也欢迎提供全面的样式提交pull request。 标签列表:

    python批量下载图片的三种方法

    selenium则提供了Chrome,IE,FireFox等的支持,每种浏览器都有execute_script和find_element_by_xx方法,可以方便的执行js脚本(包括修改元素)和读取html里面的元素。不足是selenium只提供对python2.6和2.7的支持...

    HttpClient以及获取页面内容应用

    12. Request的输出流可以避免流中内容直接缓冲到socket服务器。 13. Response的输入流可以有效的从socket服务器直接读取相应内容。 14. 在http1.0和http1.1中利用KeepAlive保持持久连接。 15. 直接获取服务器发送的...

    代理服务器自动测试工具(含Java源码)

    用Java写的一个小工具,通过...这个小工具使用了GPL授权的HTMLParser库(htmlparser.sourceforge.net)。 这个小工具也采用GPL授权。代码以及Eclipse工程文件包含在压缩包内。欢迎修改加强,但是请遵循GPL授权与精神。

    Python编程入门经典

    1.5 用不同的方法串联字符串 11 1.6 本章小结 12 1.7 习题 13 第2章 数值与运算符 15 2.1 不同类型的数值 15 2.2 程序文件 17 2.2.1 使用不同的类型 18 2.2.2 基本算术 20 2.2.3 一些惊喜 22 2.3 使用数值 23 2.3.1 ...

    JAVA上百实例源码以及开源项目源代码

    嵌套在html中 各种EJB之间的调用示例 7个目标文件 摘要:Java源码,初学实例,EJB调用实例  各种EJB之间的调用源码示例,用远程接口的引用访问EJB、函数将被FirstEJB调用,同时它将调用secondEJB 基于JAVA的UDP...

    JAVA上百实例源码以及开源项目

    嵌套在html中 各种EJB之间的调用示例 7个目标文件 摘要:Java源码,初学实例,EJB调用实例  各种EJB之间的调用源码示例,用远程接口的引用访问EJB、函数将被FirstEJB调用,同时它将调用secondEJB 基于JAVA的UDP...

    java开源包1

    容易维护扩展(不需要修改主类就可以添加新的API支持) 注入型解释器(依据不同的返回格式注入相应的解释器) 集中管理请求参数与参数映射 以运行时异常的方式来管理错误的响应 使用泛型来做强类型编程 多协议扩展...

    java开源包11

    容易维护扩展(不需要修改主类就可以添加新的API支持) 注入型解释器(依据不同的返回格式注入相应的解释器) 集中管理请求参数与参数映射 以运行时异常的方式来管理错误的响应 使用泛型来做强类型编程 多协议扩展...

    java开源包2

    容易维护扩展(不需要修改主类就可以添加新的API支持) 注入型解释器(依据不同的返回格式注入相应的解释器) 集中管理请求参数与参数映射 以运行时异常的方式来管理错误的响应 使用泛型来做强类型编程 多协议扩展...

    java开源包3

    容易维护扩展(不需要修改主类就可以添加新的API支持) 注入型解释器(依据不同的返回格式注入相应的解释器) 集中管理请求参数与参数映射 以运行时异常的方式来管理错误的响应 使用泛型来做强类型编程 多协议扩展...

    java开源包6

    容易维护扩展(不需要修改主类就可以添加新的API支持) 注入型解释器(依据不同的返回格式注入相应的解释器) 集中管理请求参数与参数映射 以运行时异常的方式来管理错误的响应 使用泛型来做强类型编程 多协议扩展...

    java开源包5

    容易维护扩展(不需要修改主类就可以添加新的API支持) 注入型解释器(依据不同的返回格式注入相应的解释器) 集中管理请求参数与参数映射 以运行时异常的方式来管理错误的响应 使用泛型来做强类型编程 多协议扩展...

    java开源包10

    容易维护扩展(不需要修改主类就可以添加新的API支持) 注入型解释器(依据不同的返回格式注入相应的解释器) 集中管理请求参数与参数映射 以运行时异常的方式来管理错误的响应 使用泛型来做强类型编程 多协议扩展...

    java开源包4

    容易维护扩展(不需要修改主类就可以添加新的API支持) 注入型解释器(依据不同的返回格式注入相应的解释器) 集中管理请求参数与参数映射 以运行时异常的方式来管理错误的响应 使用泛型来做强类型编程 多协议扩展...

Global site tag (gtag.js) - Google Analytics