HtmlParser
HtmlParser 基本类库使用
HtmlParser 提供了强大的类库来处理 Internet 上的网页,可以实现对网页特定内容的提取和修改。下面通过几个例子来介绍 HtmlParser 的一些使用。这些例子其中的代码,有部分用在了后面介绍的简易爬虫中。以下所有的代码和方法都在在类 HtmlParser.Test.java 里,这是笔者编写的一个用来测试 HtmlParser 用法的类。
- 迭代遍历网页所有节点
网页是一个半结构化的嵌套文本文件,有类似 XML 文件的树形嵌套结构。使用HtmlParser 可以让我们轻易的迭代遍历网页的所有节点。清单 1 展示了如何来实现这个功能。
清单 1
// 循环访问所有节点,输出包含关键字的值节点 public static void extractKeyWordText(String url, String keyword) { try { //生成一个解析器对象,用网页的 url 作为参数 Parser parser = new Parser(url); //设置网页的编码,这里只是请求了一个 gb2312 编码网页 parser.setEncoding("gb2312"); //迭代所有节点, null 表示不使用 NodeFilter NodeList list = parser.parse(null); //从初始的节点列表跌倒所有的节点 processNodeList(list, keyword); } catch (ParserException e) { e.printStackTrace(); } } private static void processNodeList(NodeList list, String keyword) { //迭代开始 SimpleNodeIterator iterator = list.elements(); while (iterator.hasMoreNodes()) { Node node = iterator.nextNode(); //得到该节点的子节点列表 NodeList childList = node.getChildren(); //孩子节点为空,说明是值节点 if (null == childList) { //得到值节点的值 String result = node.toPlainTextString(); //若包含关键字,则简单打印出来文本 if (result.indexOf(keyword) != -1) System.out.println(result); } //end if //孩子节点不为空,继续迭代该孩子节点 else { processNodeList(childList, keyword); }//end else }//end wile }
上面的中有两个方法:
- private static void processNodeList(NodeList list, String keyword)
该方法是用类似深度优先的方法来迭代遍历整个网页节点,将那些包含了某个关键字的值节点的值打印出来。
- public static void extractKeyWordText(String url, String keyword)
该方法生成针对 String 类型的 url 变量代表的某个特定网页的解析器,调用 1中的方法实现简单的遍历。
清单 1 的代码展示了如何迭代所有的网页,更多的工作可以在此基础上展开。比如找到某个特定的网页内部节点,其实就可以在遍历所有的节点基础上来判断,看被迭代的节点是否满足特定的需要。
- 使用 NodeFilter
NodeFilter 是一个接口,任何一个自定义的 Filter 都需要实现这个接口中的 boolean accept() 方法。如果希望迭代网页节点的时候保留当前节点,则在节点条件满足的情况下返回 true;否则返回 false。HtmlParse 里提供了很多实现了 NodeFilter 接口的类,下面就一些笔者所用到的,以及常用的 Filter 做一些介绍:
这些 Filter 来组合不同的 Filter,形成满足两个 Filter 逻辑关系结果的 Filter。
- 判断节点的孩子,兄弟,以及父亲节点情况的 Filter 有:HasChildFilterHasParentFilter,HasSiblingFilter。
- 判断节点本身情况的 Filter 有 HasAttributeFilter:判读节点是否有特定属性;LinkStringFilter:判断节点是否是具有特定模式 (pattern) url 的节点;
TagNameFilter:判断节点是否具有特定的名字;NodeClassFilter:判读节点是否是某个 HtmlParser 定义好的 Tag 类型。在 org.htmlparser.tags 包下有对应 Html标签的各种 Tag,例如 LinkTag,ImgeTag 等。
还有其他的一些 Filter 在这里不一一列举了,可以在 org.htmlparser.filters 下找到。
清单 2 展示了如何使用上面提到过的一些 filter 来抽取网页中的 <a> 标签里的 href属性值,<img> 标签里的 src 属性值,以及 <frame> 标签里的 src 的属性值。
清单2
// 获取一个网页上所有的链接和图片链接 public static void extracLinks(String url) { try { Parser parser = new Parser(url); parser.setEncoding("gb2312"); //过滤 <frame> 标签的 filter,用来提取 frame 标签里的 src 属性所、表示的链接 NodeFilter frameFilter = new NodeFilter() { public boolean accept(Node node) { if (node.getText().startsWith("frame src=")) { return true; } else { return false; } } }; //OrFilter 来设置过滤 <a> 标签,<img> 标签和 <frame> 标签,三个标签是 or 的关系 OrFilte rorFilter = new OrFilter(new NodeClassFilter(LinkTag.class), new NodeClassFilter(ImageTag.class)); OrFilter linkFilter = new OrFilter(orFilter, frameFilter); //得到所有经过过滤的标签 NodeList list = parser.extractAllNodesThatMatch(linkFilter); for (int i = 0; i < list.size(); i++) { Node tag = list.elementAt(i); if (tag instanceof LinkTag)//<a> 标签 { LinkTag link = (LinkTag) tag; String linkUrl = link.getLink();//url String text = link.getLinkText();//链接文字 System.out.println(linkUrl + "**********" + text); } else if (tag instanceof ImageTag)//<img> 标签 { ImageTag image = (ImageTag) list.elementAt(i); System.out.print(image.getImageURL() + "********");//图片地址 System.out.println(image.getText());//图片文字 } else//<frame> 标签 { //提取 frame 里 src 属性的链接如 <frame src="test.html"/> String frame = tag.getText(); int start = frame.indexOf("src="); frame = frame.substring(start); int end = frame.indexOf(" "); if (end == -1) end = frame.indexOf(">"); frame = frame.substring(5, end - 1); System.out.println(frame); } } } catch (ParserException e) { e.printStackTrace(); } }
- 简单强大的 StringBean
如果你想要网页中去掉所有的标签后剩下的文本,那就是用 StringBean 吧。以下简单的代码可以帮你解决这样的问题:
清单3
StringBean sb = new StringBean();
sb.setLinks(false);//设置结果中去点链接
sb.setURL(url);//设置你所需要滤掉网页标签的页面 url
System.out.println(sb.getStrings());//打印结果
HtmlParser 提供了强大的类库来处理网页。