参考文档: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比较, 没有去除空格.
当然可以自己重写一下判断逻辑,不过一般情况够用了