噹噹噹!終於來到了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