当当当!终于来到了Jsoup的特点:CSS Selector部分。selector也是我写的爬虫框架webmagic开发的一个重点。附上一张street fighter的图,但愿之后webmagic也能挑战Jsoup!html
Jsoup的select包里,类结构以下:java
在最开始介绍Jsoup的时候,就已经说过NodeVisitor
和Selector
了。Selector
是select部分的对外facade,而NodeVisitor
则是遍历树的底层API,CSS Selector也是根据NodeVisitor
实现的遍历。git
Jsoup的select核心是Evaluator
。Selector所传递的表达式,会通过QueryParser
,最终编译成一个Evaluator
。Evaluator
是一个抽象类,它只有一个方法:github
<!-- lang: java --> public abstract boolean matches(Element root, Element element);
注意这里传入了root,是为了某些状况下对树进行遍历时用的。web
Evaluator的设计简洁明了,全部的Selector表达式单词都会编译到对应的Evaluator。例如#xx
对应Id
,.xx
对应Class
,[]
对应Attribute
。这里补充一下w3c的CSS Selector规范:http://www.w3.org/TR/CSS2/selector.html框架
固然,只靠这几个还不够,Jsoup还定义了CombiningEvaluator
(对Evaluator进行And/Or组合),StructuralEvaluator
(结合DOM树结构进行筛选)。this
这里咱们可能最关心的是,“div ul li”这样的父子结构是如何实现的。这个的实现方式在StructuralEvaluator.Parent
中,贴一下代码了:lua
<!-- lang: java --> static class Parent extends StructuralEvaluator { public Parent(Evaluator evaluator) { this.evaluator = evaluator; } public boolean matches(Element root, Element element) { if (root == element) return false; Element parent = element.parent(); while (parent != root) { if (evaluator.matches(root, parent)) return true; parent = parent.parent(); } return false; } }
这里Parent包含了一个evaluator
属性,会根据这个evaluator去验证全部父节点。注意Parent是能够嵌套的,因此这个表达式"div ul li"最终会编译成And(Parent(And(Parent(Tag("div")),Tag("ul")),Tag("li")))
这样的Evaluator组合。设计
select部分比想象的要简单,代码可读性也很高。通过了parser部分的研究,这部分应该算是得心应手了。code
webmagic是一个爬虫框架,它的Selector是用于抓取HTML中指定的文本,其机制和Jsoup的Evaluator很是像,只不过webmagic暂时是将Selector封装成较简单的API,而Evaluator直接上了表达式。以前也考虑过本身定制DSL来写一个HTML,如今看了Jsoup的源码,实现能力算是有了,可是引入DSL,实现只是一小部分,如何让DSL易写易懂才是难点。
其实看了Jsoup的源码,精细程度上比webmagic要好得多了,基本每一个类都对应一个真实的概念抽象,可能之后会在这方面下点工夫。
下篇文章将讲最后一部分:白名单及HTML过滤机制。
最后依然附上这系列文章和代码的github地址:https://github.com/code4craft/jsoup-learning