HtmlParser 简单应用

HtmlParser 简介

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


HtmlParser 基本类库使用
HtmlParser 提供了强大的类库来处理 Internet 上的网页,可以实现对网页特定内容的提取和修改。下面通过几个例子来介绍 HtmlParser 的一些使用。这些例子其中的代码,有部分用在了后面介绍的简易爬虫中。以下所有的代码和方法都在在类 HtmlParser.Test.java 里,这是笔者编写的一个用来测试 HtmlParser 用法的类。
迭代遍历网页所有节点
网页是一个半结构化的嵌套文本文件,有类似 XML 文件的树形嵌套结构。使用HtmlParser 可以让我们轻易的迭代遍历网页的所有节点。清单 3 展示了如何来实现这个功能。

清单 3

[java]  view plain copy
  1. // 循环访问所有节点,输出包含关键字的值节点  
  2.     public static void extractKeyWordText(String url, String keyword) {  
  3.         try {  
  4.             //生成一个解析器对象,用网页的 url 作为参数  
  5.             Parser parser = new Parser(url);  
  6.             //设置网页的编码,这里只是请求了一个 gb2312 编码网页  
  7.             parser.setEncoding("gb2312");  
  8.             //迭代所有节点, null 表示不使用 NodeFilter  
  9.             NodeList list = parser.parse(null);  
  10.             //从初始的节点列表跌倒所有的节点  
  11.             processNodeList(list, keyword);  
  12.         } catch (ParserException e) {  
  13.             e.printStackTrace();  
  14.         }  
  15.     }  
  16.   
  17.     private static void processNodeList(NodeList list, String keyword) {  
  18.         //迭代开始  
  19.         SimpleNodeIterator iterator = list.elements();  
  20.         while (iterator.hasMoreNodes()) {  
  21.             Node node = iterator.nextNode();  
  22.             //得到该节点的子节点列表  
  23.             NodeList childList = node.getChildren();  
  24.             //孩子节点为空,说明是值节点  
  25.             if (null == childList)  
  26.             {  
  27.                 //得到值节点的值  
  28.                 String result = node.toPlainTextString();  
  29.                 //若包含关键字,则简单打印出来文本  
  30.                 if (result.indexOf(keyword) != -1)  
  31.                     System.out.println(result);  
  32.             } //end if  
  33.             //孩子节点不为空,继续迭代该孩子节点  
  34.             else   
  35.             {  
  36.                 processNodeList(childList, keyword);  
  37.             }//end else  
  38.         }//end wile  
  39.     }  

上面的中有两个方法:
private static void processNodeList(NodeList list, String keyword)
该方法是用类似深度优先的方法来迭代遍历整个网页节点,将那些包含了某个关键字的值节点的值打印出来。
public static void extractKeyWordText(String url, String keyword)
该方法生成针对 String 类型的 url 变量代表的某个特定网页的解析器,调用 1中的方法实现简单的遍历。
清单 3 的代码展示了如何迭代所有的网页,更多的工作可以在此基础上展开。比如找到某个特定的网页内部节点,其实就可以在遍历所有的节点基础上来判断,看被迭代的节点是否满足特定的需要。


使用 NodeFilter
NodeFilter 是一个接口,任何一个自定义的 Filter 都需要实现这个接口中的 boolean accept() 方法。如果希望迭代网页节点的时候保留当前节点,则在节点条件满足的情况下返回 true;否则返回 false。HtmlParse 里提供了很多实现了 NodeFilter 接口的类,下面就一些笔者所用到的,以及常用的 Filter 做一些介绍:
对 Filter 做逻辑操作的 Fitler 有:AndFilter,NotFilter ,OrFilter,XorFilter。
这些 Filter 来组合不同的 Filter,形成满足两个 Filter 逻辑关系结果的 Filter。
判断节点的孩子,兄弟,以及父亲节点情况的 Filter 有:HasChildFilter HasParentFilter,HasSiblingFilter。
判断节点本身情况的 Filter 有 HasAttributeFilter:判读节点是否有特定属性;LinkStringFilter:判断节点是否是具有特定模式 (pattern) url 的节点;
TagNameFilter:判断节点是否具有特定的名字;NodeClassFilter:判读节点是否是某个 HtmlParser 定义好的 Tag 类型。在 org.htmlparser.tags 包下有对应 Html标签的各种 Tag,例如 LinkTag,ImgeTag 等。
还有其他的一些 Filter 在这里不一一列举了,可以在 org.htmlparser.filters 下找到。
清单 4 展示了如何使用上面提到过的一些 filter 来抽取网页中的 <a> 标签里的 href属性值,<img> 标签里的 src 属性值,以及 <frame> 标签里的 src 的属性值。

清单4

[java]  view plain copy
  1. // 获取一个网页上所有的链接和图片链接  
  2.     public static void extracLinks(String url) {  
  3.         try {  
  4.             Parser parser = new Parser(url);  
  5.             parser.setEncoding("gb2312");  
  6. //过滤 <frame> 标签的 filter,用来提取 frame 标签里的 src 属性所、表示的链接  
  7.             NodeFilter frameFilter = new NodeFilter() {  
  8.                 public boolean accept(Node node) {  
  9.                     if (node.getText().startsWith("frame src=")) {  
  10.                         return true;  
  11.                     } else {  
  12.                         return false;  
  13.                     }  
  14.                 }  
  15.             };  
  16. //OrFilter 来设置过滤 <a> 标签,<img> 标签和 <frame> 标签,三个标签是 or 的关系  
  17.      OrFilte rorFilter = new OrFilter(new NodeClassFilter(LinkTag.class), new   
  18. NodeClassFilter(ImageTag.class));  
  19.      OrFilter linkFilter = new OrFilter(orFilter, frameFilter);  
  20.     //得到所有经过过滤的标签  
  21.     NodeList list = parser.extractAllNodesThatMatch(linkFilter);  
  22.     for (int i = 0; i < list.size(); i++) {  
  23.         Node tag = list.elementAt(i);  
  24.         if (tag instanceof LinkTag)//<a> 标签   
  25.         {  
  26.             LinkTag link = (LinkTag) tag;  
  27.             String linkUrl = link.getLink();//url  
  28.             String text = link.getLinkText();//链接文字  
  29.             System.out.println(linkUrl + "**********" + text);  
  30.         }  
  31.         else if (tag instanceof ImageTag)//<img> 标签  
  32.         {  
  33.             ImageTag image = (ImageTag) list.elementAt(i);  
  34.             System.out.print(image.getImageURL() + "********");//图片地址  
  35.             System.out.println(image.getText());//图片文字  
  36.         }  
  37.         else//<frame> 标签  
  38.         {  
  39. //提取 frame 里 src 属性的链接如 <frame src="test.html"/>  
  40.             String frame = tag.getText();  
  41.             int start = frame.indexOf("src=");  
  42.             frame = frame.substring(start);  
  43.             int end = frame.indexOf(" ");  
  44.             if (end == -1)  
  45.                 end = frame.indexOf(">");  
  46.             frame = frame.substring(5, end - 1);  
  47.             System.out.println(frame);  
  48.         }  
  49.     }  
  50. catch (ParserException e) {  
  51.             e.printStackTrace();  
  52. }  
  53. }  

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


清单5

[java]  view plain copy
  1. StringBean sb = new StringBean();  
  2. sb.setLinks(false);//设置结果中去点链接  
  3. sb.setURL(url);//设置你所需要滤掉网页标签的页面 url  
  4. System.out.println(sb.getStrings());//打印结果  

HtmlParser 提供了强大的类库来处理网页,由于本文旨在简单的介绍,因此只是将与笔者后续爬虫部分有关的关键类库进行了示例说明。感兴趣的读者可以专门来研究一下 HtmlParser 更为强大的类库。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值