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

这里可以看到,核心无疑是Node类。
Node类是一个抽象类,它代表DOM树中的一个节点,它包含:
- 父节点
parentNode以及子节点childNodes的引用 - 属性值集合
attributes - 页面的uri
baseUri,用于修正相对地址为绝对地址 - 在兄弟节点中的位置
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元素。它包含一个字段tag和classNames。classNames是"class"属性解析出来的集合,因为CSS规范里,“class"属性允许设置多个,并用空格隔开,而在用Selector选择的时候,即使只指定其中一个,也能够选中其中的元素。所以这里就把"class"属性展开了。Element还有选取元素的入口,例如select、getElementByXXX,这些都用到了select包中的内容,这个留到下篇文章select再说。
Document是代表整个文档,它也是一个特殊的Element,即根节点。Document除了Element的内容,还包括一些输出的方法。
Document还有一个属性quirksMode,大致意思是定义处理非标准HTML的几个级别,这个留到以后分析parser的时候再说。
DOM树的遍历
Node还有一些方法,例如outerHtml(),用作节点及文档HTML的输出,用到了树的遍历。在DOM树的遍历上,用到了NodeVisitor和NodeTraversor来对树的进行遍历。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相关对象的更多相关文章
- Jsoup代码解读之四-parser
Jsoup代码解读之四-parser 作为Java世界最好的HTML 解析库,Jsoup的parser实现非常具有代表性.这部分也是Jsoup最复杂的部分,需要一些数据结构.状态机乃至编译器的知识.好 ...
- Jsoup代码解读之六-防御XSS攻击
Jsoup代码解读之八-防御XSS攻击 防御XSS攻击的一般原理 cleaner是Jsoup的重要功能之一,我们常用它来进行富文本输入中的XSS防御. 我们知道,XSS攻击的一般方式是,通过在页面输入 ...
- Jsoup代码解读之三-Document的输出
Jsoup代码解读之三-Document的输出 Jsoup官方说明里,一个重要的功能就是output tidy HTML.这里我们看看Jsoup是如何输出HTML的. HTML相关知识 分析代码前 ...
- Jsoup代码解读之五-实现一个CSS Selector
Jsoup代码解读之七-实现一个CSS Selector 当当当!终于来到了Jsoup的特色:CSS Selector部分.selector也是我写的爬虫框架webmagic开发的一个重点.附上一张s ...
- Jsoup代码解读之一-概述
Jsoup代码解读之一-概述 今天看到一个用python写的抽取正文的东东,美滋滋的用Java实现了一番,放到了webmagic里,然后发现Jsoup里已经有了…觉得自己各种不靠谱啊!算了,静下心来学 ...
- mybatis源码解读(二)——构建Configuration对象
Configuration 对象保存了所有mybatis的配置信息,主要包括: ①. mybatis-configuration.xml 基础配置文件 ②. mapper.xml 映射器配置文件 1. ...
- 2017.2.7 开涛shiro教程-第六章-Realm及相关对象(二)
原博客地址:http://jinnianshilongnian.iteye.com/blog/2018398 根据下载的pdf学习. 第六章 Realm及相关对象(二) 1.Authenticatio ...
- JavaScript -- 时光流逝(十二):DOM -- Element 对象
JavaScript -- 知识点回顾篇(十二):DOM -- Element 对象 (1) element.accessKey: 设置或返回accesskey一个元素,使用 Alt + 指定快捷键 ...
- 优秀开源代码解读之JS与iOS Native Code互调的优雅实现方案
简介 本篇为大家介绍一个优秀的开源小项目:WebViewJavascriptBridge. 它优雅地实现了在使用UIWebView时JS与ios 的ObjC nativecode之间的互调,支持消息发 ...
随机推荐
- jedis入门一
一.下载Jedis的依赖包jedis-2.1.0.jar,然后将其添加到classpath下面. 1. 定义连接:Redis暂时不要设置登录密码 Jedis jedis = new Jedis(&qu ...
- C#学习日志 day7 --------------LINQ与Lamda语句的初步尝试以及XML的生成
LINQ是一种集成在计算机语言里的信息查询语句,是c#3.0中最惹人瞩目的功能. 在C#中,LINQ语句有两种写法. 第一种写法与SQL语句类似: IEnumerable<Customer> ...
- CentOS 7 下yum安装xtrabackup备份工具
第一步:安装xtrabackup的数据库 yum install https://www.percona.com/redir/downloads/percona-release/redhat/late ...
- delphi 编码速度提升技能
效率,是一个永恒的主题. 本文重点强调 delphi ide 中的编码速度技能 一.TForm 窗口重用 当您在写一个管理类软件的时候,有大量的操作窗口,这些窗口会有大量共性.窗口重用就会发挥很大的作 ...
- mysql binlog 混合模式 出现的基于sql的数据不一致,主要是now()这类函数导致
- CentOS 删除自带的OpenJDK 和 安装SunJDK
[root@WX32 local]# java -version java version "1.6.0" OpenJDK Runtime Environment (build - ...
- ntpd和ntpdate的区别
之前配置ntpd的时候搜到一句话,印象很深刻,也觉得很有标题党的效果,就借鉴为标题了:“我认为有几种人是必须不招聘/裁掉的: 1 用ntpdate代替ntpd的人”但具体原因不太懂,总觉得还是用ntp ...
- 回收进程用户空间资源 exit()函数 _exit()函数 atexit()函数 on_exit()函数
摘要:本文主要讲述进程的终止方式,以及怎样使用exit()函数来终止进程.回收进程用户空间资源:分析了exit()函数与_exit()函数,returnkeyword的差异.同一时候具体解读了怎样使用 ...
- ASP.NET listBbox控件用法
ListBox基本功能使用方法 2011-06-09 13:23:16| 分类: .NET/C# | 标签:listbox基本功能使用方法 |举报 |字号大中小 订阅 ListBox基 ...
- 2014.9.20CSS样式表
一.前景与背景 background-color: 背景色,定义背景的颜色 background-image:url() 定义背景图片 background-attachment:fixed/scro ...