htmlparser 中修改 HTML 内容的方法

          htmlparser(HTML 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
本文版权归作者所有,只允许以摘要和完整全文两种形式转载,不允许对文字进行裁剪。未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 可以使用类似于这样的代码来使用xpath获取div元素的内容: ``` from lxml import html tree = html.fromstring(html_content) div_element = tree.xpath('//div[@class="class-name"]')[0] div_content = div_element.text_content() print(div_content) ``` 这里使用了`html.fromstring()`函数将HTML字符串转换为可操作的树形结构,然后使用`xpath()`函数对树进行查询,获取匹配的div元素。`text_content()`函数可以获取元素的文本内容。 这里的示例代码使用`class-name`来查找特定的div元素,可以替换成其他属性值或者属性名来获取不同的div元素 ### 回答2: HTML是一种标记语言,用于描述网页的结构和呈现方式。XPath是一种用于在XML、HTML等结构化文档进行导航和查询的语言。在HTML,我们可以通过使用XPath表达式来获取div元素的内容。 要使用XPath获取div元素的内容,我们需要以下几个步骤: 1. 首先,我们需要创建一个HTML解析器,用于解析HTML文档。可以使用一些常见的HTML解析库,如BeautifulSoup或lxml。 2. 然后,我们需要加载HTML文档,可以从文件加载,也可以从URL加载。加载HTML文档后,就可以将其传递给HTML解析器进行解析。 3. 接下来,我们需要使用XPath表达式来选择div元素。XPath表达式由一系列的节点、操作符和轴组成,可以根据标签名、属性、层级关系等条件来选择元素。 4. 通过执行XPath表达式,我们可以获取所有符合条件的div元素。可以使用解析器提供的API方法,如find_all()或xpath()来执行XPath表达式并获取结果。 5. 最后,我们可以遍历获取到的div元素列表,进一步处理其内容,如提取文本或属性值等。 以下是一个使用Python和lxml库进行XPath获取div元素内容的示例代码: ```python import requests from lxml import etree # 加载HTML文档 url = 'https://example.com' response = requests.get(url) html = response.text # 创建HTML解析器 parser = etree.HTMLParser() # 解析HTML文档 tree = etree.HTML(html, parser=parser) # 使用XPath表达式选择div元素 div_elements = tree.xpath('//div') # 遍历div元素列表 for div in div_elements: # 处理div元素内容 content = div.text print(content) ``` 以上就是使用XPath获取div元素内容的简单示例。根据实际需求,可以根据div的层级关系、属性等条件来进一步筛选和处理元素内容。 ### 回答3: HTML是一种标记语言,用于描述网页的结构和元素。XPath是一种查询语言,用于在XML或HTML文档定位和选择元素。 要使用XPath获取div元素的内容,可以按照以下步骤进行操作: 1. 首先,需要将HTML文档加载到一个解析器,例如Python的lxml库。 2. 然后,使用XPath表达式来选择所需的元素。在这种情况下,XPath表达式应该是"//div",表示选择文档的所有div元素。 3. 使用XPath解析器的findall或find方法,将XPath表达式应用到HTML文档,并返回匹配的div元素列表。 4. 对于每个匹配的div元素,可以使用元素的text属性获取其内容。 以下是使用Python和lxml库来获取div元素的内容的示例代码: ```python import requests from lxml import etree url = "https://example.com" response = requests.get(url) html = etree.HTML(response.content) divs = html.xpath("//div") for div in divs: content = div.text print(content) ``` 注意,这只是一个简单的示例,实际的实现可能需要根据具体的情况进行调整和修改。同时,XPath表达式可以根据实际需要进行更加复杂的定位和选择。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值