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之间的互调,支持消息发 ...
随机推荐
- C语言,如何产生随机数
1. 基本函数 在C语言中取随机数所需要的函数是: int rand(void);void srand (unsigned int n); rand()函数和srand()函数被声明在头文件stdli ...
- Oracle查看被锁的表和解锁[转]
查看被锁的表 select p.spid,a.serial#, c.object_name,b.session_id,b.oracle_username,b.os_user_name from v$p ...
- mysql学习(四)-字段类型
mysql数据类型: 数值型: 整形:int 浮点型:float double decimal:定点型 日期: date '2012-01-02' time '10:01:01' datetime ...
- C#控件、窗体置顶
//控件置于顶层和底层 panel.BringToFront();//置于顶层 panel.SendToBack();//置于底层 //窗体置顶 TopMost = true;
- Hash算法原理理解
我们有很多的小猪,每个的体重都不一样,假设体重分布比较平均(我们考虑到公斤级别),我们按照体重来分,划分成100个小猪圈. 然后把每个小猪,按照体重赶进各自的猪圈里,记录档案. 好了,如果我们要找某个 ...
- MVC入门
MVC开始是存在于桌面程序中的,M是指业务模型,V是指用户界面,C 则是控制器,使用MVC的目的是将M和V的实现代码分离,从而使同一个程序可以使用不同的表现形式.比如一批统计数据可以分别用柱状图.饼图 ...
- 查看linux版本的三种常用方法
1) 登录到服务器执行 lsb_release -a ,即可列出所有版本信息,例如: [root@3.5.5Biz-46 ~]# lsb_release -a LSB Version: 1.3 Dis ...
- Linux(CentOS6.5) 开放端口,配置防火墙
打开配置文件 [root@localhost ~]# vi /etc/sysconfig/iptables 正确的配置文件 # Firewall configuration written by sy ...
- C语言入门(4)——常量、变量与赋值
对于基本数据类型量,按其取值是否可改变又分为常量和变量两种.在程序执行过程中,其值不发生改变的量称为常量,其值可变的量称为变量.它们可与数据类型结合起来分类. 常量 常量有字符常量(Character ...
- C++死锁解决心得
一. 概述C++多线程开发中,容易出现死锁导致程序挂起的现象.关于死锁的信息,见百度百科http://baike.baidu.com/view/121723.htm. 解决步骤分为三步:1.检测死锁线 ...