Jsoup代码解读之二-DOM相关对象

 

之前在文章中说到,Jsoup使用了一套自己的DOM对象体系,和Java XML API互不兼容。这样做的好处是从XML的API里解脱出来,使得代码精炼了很多。这篇文章会说明Jsoup的DOM结构,DOM的遍历方式。在下一篇文章,我会并结合这两个基础,分析一下Jsoup的HTML输出功能。

DOM结构相关类

我们先来看看nodes包的类图:

这里可以看到,核心无疑是Node类。

Node类是一个抽象类,它代表DOM树中的一个节点,它包含:

  • 父节点parentNode以及子节点childNodes的引用
  • 属性值集合attributes
  • 页面的uribaseUri,用于修正相对地址为绝对地址
  • 在兄弟节点中的位置siblingIndex,用于进行DOM操作

Node里面包含一些获取属性、父子节点、修改元素的方法,其中比较有意思的是absUrl()。我们知道,在很多html页面里,链接会使用相对地址,我们有时会需要将其转变为绝对地址。Jsoup的解决方案是在attr()的参数开始加"abs:“,例如attr(“abs:href”),而absUrl()就是其实现方式。我写的爬虫框架webmagic里也用到了类似功能,当时是自己手写的,看到Jsoup的实现,才发现自己是白费劲了,代码如下:


URL base;
try {
    try {
        base = new URL(baseUri);
    } catch (MalformedURLException e) {
        // the base is unsuitable, but the attribute may be abs on its own, so try that
        URL abs = new URL(relUrl);
        return abs.toExternalForm();
    }
    // workaround: java resolves '//path/file + ?foo' to '//path/?foo', not '//path/file?foo' as desired
    if (relUrl.startsWith("?"))
        relUrl = base.getPath() + relUrl;
    // java URL自带的相对路径解析
    URL abs = new URL(base, relUrl);
    return abs.toExternalForm();
} catch (MalformedURLException e) {
    return "";
}

Node还有一个比较值得一提的方法是abstract String nodeName(),这个相当于定义了节点的类型名(例如Document是'#Document',Element则是对应的TagName)。

Element也是一个重要的类,它代表的是一个HTML元素。它包含一个字段tagclassNames。classNames是"class"属性解析出来的集合,因为CSS规范里,“class"属性允许设置多个,并用空格隔开,而在用Selector选择的时候,即使只指定其中一个,也能够选中其中的元素。所以这里就把"class"属性展开了。Element还有选取元素的入口,例如selectgetElementByXXX,这些都用到了select包中的内容,这个留到下篇文章select再说。

Document是代表整个文档,它也是一个特殊的Element,即根节点。Document除了Element的内容,还包括一些输出的方法。

Document还有一个属性quirksMode,大致意思是定义处理非标准HTML的几个级别,这个留到以后分析parser的时候再说。

DOM树的遍历

Node还有一些方法,例如outerHtml(),用作节点及文档HTML的输出,用到了树的遍历。在DOM树的遍历上,用到了NodeVisitorNodeTraversor来对树的进行遍历。NodeVisitor在上一篇文章提到过了,head()和tail()分别是遍历开始和结束时的方法,而NodeTraversor的核心代码如下:


public void traverse(Node root) {
    Node node = root;
    int depth = 0;

    //这里对树进行后序(深度优先)遍历
    while (node != null) {
        //开始遍历node
        visitor.head(node, depth);
        if (node.childNodeSize() > 0) {
            node = node.childNode(0);
            depth++;
        } else {
            //没有下一个兄弟节点,退栈
            while (node.nextSibling() == null && depth > 0) {
                visitor.tail(node, depth);
                node = node.parent();
                depth--;
            }
            //结束遍历
            visitor.tail(node, depth);
            if (node == root)
                break;
            node = node.nextSibling();
        }
    }
}

这里使用循环+回溯来替换掉了我们常用的递归方式,从而避免了栈溢出的风险。

实际上,Jsoup的Selector机制也是基于NodeVisitor来实现的,可以说NodeVisitor是更加底层和灵活的API。

Jsoup代码解读之二-DOM相关对象的更多相关文章

  1. Jsoup代码解读之四-parser

    Jsoup代码解读之四-parser 作为Java世界最好的HTML 解析库,Jsoup的parser实现非常具有代表性.这部分也是Jsoup最复杂的部分,需要一些数据结构.状态机乃至编译器的知识.好 ...

  2. Jsoup代码解读之六-防御XSS攻击

    Jsoup代码解读之八-防御XSS攻击 防御XSS攻击的一般原理 cleaner是Jsoup的重要功能之一,我们常用它来进行富文本输入中的XSS防御. 我们知道,XSS攻击的一般方式是,通过在页面输入 ...

  3. Jsoup代码解读之三-Document的输出

    Jsoup代码解读之三-Document的输出   Jsoup官方说明里,一个重要的功能就是output tidy HTML.这里我们看看Jsoup是如何输出HTML的. HTML相关知识 分析代码前 ...

  4. Jsoup代码解读之五-实现一个CSS Selector

    Jsoup代码解读之七-实现一个CSS Selector 当当当!终于来到了Jsoup的特色:CSS Selector部分.selector也是我写的爬虫框架webmagic开发的一个重点.附上一张s ...

  5. Jsoup代码解读之一-概述

    Jsoup代码解读之一-概述 今天看到一个用python写的抽取正文的东东,美滋滋的用Java实现了一番,放到了webmagic里,然后发现Jsoup里已经有了…觉得自己各种不靠谱啊!算了,静下心来学 ...

  6. mybatis源码解读(二)——构建Configuration对象

    Configuration 对象保存了所有mybatis的配置信息,主要包括: ①. mybatis-configuration.xml 基础配置文件 ②. mapper.xml 映射器配置文件 1. ...

  7. 2017.2.7 开涛shiro教程-第六章-Realm及相关对象(二)

    原博客地址:http://jinnianshilongnian.iteye.com/blog/2018398 根据下载的pdf学习. 第六章 Realm及相关对象(二) 1.Authenticatio ...

  8. JavaScript -- 时光流逝(十二):DOM -- Element 对象

    JavaScript -- 知识点回顾篇(十二):DOM -- Element 对象 (1) element.accessKey: 设置或返回accesskey一个元素,使用 Alt + 指定快捷键 ...

  9. 优秀开源代码解读之JS与iOS Native Code互调的优雅实现方案

    简介 本篇为大家介绍一个优秀的开源小项目:WebViewJavascriptBridge. 它优雅地实现了在使用UIWebView时JS与ios 的ObjC nativecode之间的互调,支持消息发 ...

随机推荐

  1. 为什么getline()后要两次回车????(将输入的字符串按单词倒序输出)

    #include<iostream>#include<string>#include<algorithm>using namespace std;int main( ...

  2. Js把URL中的参数解析为一个对象

    <!DOCTYPE HTML> <html> <head> <meta charset="utf-8" /> <title&g ...

  3. shell命令实战详解

    1.解析路径获取文件名和目录名. 获取文件名      #awk解法:用“/”做分隔符,然后打印出最后的那一部分. resFile=`echo /tmp/csdn/zhengyi/test/adb.l ...

  4. SilverLight搭建WCF聊天室详细过程

    收藏SL双工通信例子教程 SilverLight 4正式版发布给开发人员带来了更多功能,并且4已经支持NET.TCP协议,配合WCF开发高效率的交互应用程序已经不再是难事,本系列文章主要针对已经完成的 ...

  5. android jar 第三方包

    工程交叉了,做相互引用 1.单纯的代码jar  不引用res http://terryblog.blog.51cto.com/1764499/564558 1.2.连带源码一起打包出来 http:// ...

  6. GDAL python教程(1)——用OGR读写矢量数据

    本教程的讲义和源码都是取自Utah State University的openGIS课程 相关资料,包括讲义.源码.数据样例,请从此处下载http://www.gis.usu.edu/~chrisg/ ...

  7. uestc Palindromic String

    字符串hash因为如果一个字符串是回文串,那么正着做哈希和反着做哈希结果应该一样.于是我们先正反各做一边哈希.如果判断出来一个字符串是回文穿那么这个字符串的前半部分和后半部分的重数一定相同,于是当前位 ...

  8. CodeForces - 61E Enemy is weak

    Description The Romans have attacked again. This time they are much more than the Persians but Shapu ...

  9. 像web一样使用python

    使用传统的web开发技术,也就是html+js,然后搭配一个后端语言,已经成为当今web开发的固定模式了,为此也形成了众多的toolkit,譬如ror,django,各种js图形库更是玲琅满目,从非常 ...

  10. elmah - Error Logging Modules and Handlers for ASP.NET - 1 : 初体验

    elmah(英文):https://code.google.com/p/elmah/   写作思路:先看结果,然后再说原理   elmah文章基本内容如下   1.安装 2.基本使用 3.详细配置讲解 ...