参考文档:http://www.cnblogs.com/loveyakamoz/archive/2011/07/27/2118937.html
最近学了maven还是蛮方便,但是jar包冲突之类的比较恶心.
为什么用htmlparser, 不用dom4j,因为dom4j只对标准格式的xml文件有用,html也是xml的一种.但是如果出现如同这样的:
<img src="xxxx">
这样的dom4j就解析有问题.htmlparser就没有这个问题
htmlparser导入jar:
dependency> <groupId>org.htmlparser</groupId> <artifactId>htmlparser</artifactId> <version>2.1</version> </dependency>
然后开始学习.
html可以通过网址或者直接传入一段内容
// 传入一段已经抓取的内容(随便从网上copy一份源码过来) Parser parser = new Parser(content); NodeIterator i = parser.elements(); while (i.hasMoreNodes()) { Node node = i.nextNode(); System.out.println(node); } /* for (NodeIterator i = parser.elements(); i.hasMoreNodes(); ) { Node node = i.nextNode(); System.out.println("getText:"+node.getText()); System.out.println("getPlainText:"+node.toPlainTextString()); System.out.println("toHtml:"+node.toHtml()); System.out.println("toHtml(true):"+node.toHtml(true)); System.out.println("toHtml(false):"+node.toHtml(false)); System.out.println("toString:"+node.toString()); System.out.println("================================================="); } */
打印出了很多内容,把回车换行也打印出来了.虽然看起来比较乱,
另外这里要注意,循环parser只能循环一次, 再次循环没有数据的!!!可以打开第二段注释掉的打印一次试试.没有内容.
htmlparser提供了filter和visitor来解析内容,这里记录一下使用filter的(visitor差不多)
比如,我要从下面的文本中找到
id="js_content" 的元素
<html> ...... <div class="rich_media_content " id="js_content"><p>测试</p></div> <div>啊哈哈</div> ...... </html>
代码:
// 将内容解析 Parser parser = new Parser(content); // 取出所有的div节点 NodeFilter divfilter = new TagNameFilter("div"); // 取出所有的p节点 NodeFilter pfilter = new TagNameFilter("p"); // 取出所有id=js_content的节点 *** 这里注意值不能有空格-后面解释 NodeFilter idfilter = new HasAttributeFilter( "id", "js_content" ); // 过滤器组合 NodeFilter filter = new AndFilter(divfilter, idfilter); // 精确定位,这里使用idfilter,上面只是列出了那些filter可以使用,具体的可以看参考资料 NodeList nodes = parser.extractAllNodesThatMatch(idfilter).extractAllNodesThatMatch(pfilter,true); for (int i = 0; i < nodes.size(); i++) { Node node = nodes.elementAt(i); System.out.println(i + ":---:" + node.toHtml()); }
注意到我上面用的方法:
NodeList nodes = parser.extractAllNodesThatMatch(idfilter).extractAllNodesThatMatch(pfilter,true);
可以这样写:
//得到第一层获取的节点 NodeList nodes = parser.extractAllNodesThatMatch(idfilter); //然后再次获取想要的节点, 使用pfilter,这里的第二个(是否递归参数)必须是true才能取得子节点,不然是空的!!!!! NodeList tmp = nodes.extractAllNodesThatMatch(pfilter,true);
看看源码是这样写的(递归查询出子节点然后返回):
/* 第一个参数:过滤器, 第二个参数:是否递归子节点 */ public NodeList extractAllNodesThatMatch (NodeFilter filter, boolean recursive){ Node node;NodeList children;NodeList ret;ret = new NodeList (); for (int i = 0; i < size; i++){ node = nodeData[i]; if (filter.accept (node)) ret.add (node); // 这一段判定是否将子节点加入到list中返回,默认是false的 if (recursive){ children = node.getChildren (); if (null != children) ret.add (children.extractAllNodesThatMatch (filter, recursive)); } } return (ret); }
可以这么理解:
第一次递归的是
NodeList nodes = parser.extractAllNodesThatMatch(idfilter)
这个返回了所有符合要求的内容,也就是
<div class="rich_media_content " id="js_content"><p>测试</p></div>
而第二段代码,如果不加入true的递归参数:
NodeList tmp = nodes.extractAllNodesThatMatch(pfilter);
其实相当于在div的同级去找p节点,当然没有了.
(以上是我自己的理解,不知道是否正确,如果有错误请大家下面指出....)
另外需要注意一点:
NodeFilter idfilter = new HasAttributeFilter( "id", "js_content" );
对具体的属性进行匹配的时候,要完全匹配(属性名称大小写忽略),如果是这样的(注意到下面的class="rich_mdeia_content "是带有空格的):
NodeFilter idfilter = new HasAttributeFilter( "class", "rich_media_content" );
<html> <div class="rich_media_content " id="js_content"><p>测试</p></div> <div>啊哈哈</div> </html>
是取不到结果的,因为源码中是用的equal比较, 没有去除空格.
当然可以自己重写一下判断逻辑,不过一般情况够用了