代碼結構
先介紹以下parser包裏的主要類:
- Parser
Jsoup parser的入口facade,封裝了常用的parse靜態方法。可以設置maxErrors,用於收集錯誤記錄,默認是0,即不收集。與之相關的類有ParseError,ParseErrorList。基於這個功能,我寫了一個PageErrorChecker來對頁面做語法檢查,並輸出語法錯誤。
- Token
保存單個的詞法分析結果。Token是一個抽象類,它的實現有Doctype,StartTag,EndTag,Comment,Character,EOF6種,對應6種詞法類型。
- Tokeniser
保存詞法分析過程的狀態及結果。比較重要的兩個字段是state和emitPending,前者保存狀態,後者保存輸出。其次還有tagPending/doctypePending/commentPending,保存還沒有填充完整的Token。
- CharacterReader
對讀取字符的邏輯的封裝,用於Tokenize時候的字符輸入。CharacterReader包含了類似NIO裏ByteBuffer的consume()、unconsume()、mark()、rewindToMark(),還有高級的consumeTo()這樣的用法。
- TokeniserState
用枚舉實現的詞法分析狀態機。
- HtmlTreeBuilder
語法分析,通過token構建DOM樹的類。
- HtmlTreeBuilderState
語法分析狀態機。
- TokenQueue
雖然披了個Token的馬甲,其實是在query的時候用到,留到select部分再講。
詞法分析狀態機
現在我們來講講HTML的詞法分析過程。這裏借用一下http://ued.ctrip.com/blog/?p=3295裏的圖,圖中描述了一個Tag標籤的狀態轉移過程,
這裏忽略了HTML註釋、實體以及屬性,只保留基本的開始/結束標籤,例如下面的HTML:
<div>test</div>
Jsoup裏詞法分析比較複雜,我從裏面抽取出了對應的部分,就成了我們的miniSoupLexer(這裏省略了部分代碼,完整代碼可以看這裏MiniSoupTokeniserState):
參考這個程序,可以看到Jsoup的詞法分析的大致思路。分析器本身的編寫是比較繁瑣的過程,涉及屬性值(區分單雙引號)、DocType、註釋、HTML實體,以及一些錯誤情況。不過了解了其思路,代碼實現也是按部就班的過程。