HtmlParser边学边记录

参考文档: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比较, 没有去除空格.

当然可以自己重写一下判断逻辑,不过一般情况够用了

你可能感兴趣的:(HtmlParser边学边记录)