Jsoupxpath使用 xpath 解析 html 的解析器
JsoupXpath 是一款純Java開發(fā)的使用xpath解析提取html數(shù)據(jù)的解析器,針對html解析完整實現(xiàn)了W3C XPATH 1.0標準語法,xpath的Lexer和Parser基于Antlr4構(gòu)建,html的DOM樹生成采用Jsoup,故命名為JsoupXpath. 為了在java里也享受xpath的強大與方便但又苦于找不到一款足夠好用的xpath解析器,故開發(fā)了JsoupXpath。JsoupXpath的實現(xiàn)邏輯清晰,擴展方便, 支持完備的W3C XPATH 1.0標準語法,W3C規(guī)范:http://www.w3.org/TR/1999/REC-xpath-19991116 ,JsoupXpath語法描述文件Xpath.g4
快速開始
maven依賴:
cn.wanghaomiao
JsoupXpath
${latest-release-version}
示例:
String xpath="http://div[@id='post_list']/div[./div/div/span[@class='article_view']/a/num()>1000]/div/h3/allText()";
String doc = "...";
JXDocument jxDocument = new JXDocument(doc);
Listrs = jxDocument.sel(xpath);
for (Object o:rs){
if (o instanceof Element){
int index = ((Element) o).siblingIndex();
System.out.println(index);
}
System.out.println(o.toString());
}
其他可以參考 org.seimicrawler.xpath.JXDocumentTest,這里有大量的測試用例
語法
支持完備的W3C XPATH 1.0標準語法,W3C規(guī)范:http://www.w3.org/TR/1999/REC-xpath-19991116
這里是JsoupXpath的基于Antlr4的語法解析樹示例,方便大家更快速的一覽JsoupXpath的語法處理能力與語法解析執(zhí)行過程
//ul[@class='subject-list']/li[./div/div/span[@class='pl']/num()>(1000+90*(2*50))][last()][1]/div/h2/allText()這個主要是一些表達式嵌套的解析示例,點擊圖片可以查看大圖//ul[@class='subject-list']/li[not(contains(self::li/div/div/span[@class='pl']//text(),'14582'))]/div/h2//text()這個是對內(nèi)置函數(shù)支持的一個解析示例,點擊圖片可以查看大圖
關(guān)于使用Xpath的一些注意事項
多數(shù)情況下是不建議直接粘貼Firefox或chrome里生成的Xpath,這些瀏覽器在渲染頁面會根據(jù)標準自動補全一些標簽,如table標簽會自動加上tbody標簽,這樣生成的Xpath路徑顯然不是最通用的,所以很可能就取不到值。所以,要使用Xpath并感受Xpath的強大以及他所帶來便捷與優(yōu)雅最好就是學(xué)習(xí)下Xpath的標準語法,這樣應(yīng)對各種問題才能游刃有余,享受Xpath的真正威力!
函數(shù)
int position()返回當前節(jié)點在其所在上下文中的位置int last()返回所在上下文的最后那個節(jié)點位置int first()返回所在上下文的的第一個節(jié)點位置string concat(string, string, string*)連接若干字符串boolean contains(string, string)判斷第一個字符串是否包含第二個int count(node-set)計算給定的節(jié)點集合中節(jié)點個數(shù)boolean starts-with(string, string)判斷第一個字符串是否以第二個開頭int string-length(string?)如果給定了字符串則返回字符串長度,如果沒有,那么則將當前節(jié)點轉(zhuǎn)為字符串并返回長度-
string substring(string, number, number?)第一個參數(shù)指定字符串,第二個指定起始位置(xpath索引都是從1開始),第三指定要截取的長度,這里要注意在xpath的語法里這,不是結(jié)束的位置。substring("12345", 1.5, 2.6) returns "234"
substring("12345", 2, 3) returns "234"
string substring-ex(string, number, number)第一個參數(shù)指定字符串,第二個指定起始位置(java里的習(xí)慣從0開始),第三個結(jié)束的位置(支持負數(shù)),這個是JsoupXpath擴展的函數(shù),方便java習(xí)慣的開發(fā)者使用。string substring-after(string, string)在第一個字符串中截取第二個字符串之后的部分string substring-before(string, string)在第一個字符串中截取第二個字符串之前的部分
開發(fā)者添加函數(shù)
以上只是Xpath1.0標準中的函數(shù),開發(fā)亦可以方便快捷的添加自定義函數(shù),只需實現(xiàn) org.seimicrawler.xpath.core.Function.java接口并且包路徑為package org.seimicrawler.xpath.core.function;即可,不需要修改語法范式,JsoupXpath運行時即可自動識別并加載(并不一定非要在一個jar中)。對于標準語法中目前JsoupXpath還未實現(xiàn)的函數(shù),歡迎大家向主倉庫提交Pull request,一起添磚添瓦。
NodeTest
allText()提取節(jié)點下全部文本,取代類似//div/h3//text()這種遞歸取文本用法html()獲取全部節(jié)點的內(nèi)部的htmlouterHtml()獲取全部節(jié)點的 包含節(jié)點本身在內(nèi)的全部htmlnum()抽取節(jié)點自有文本中全部數(shù)字,如果知道節(jié)點的自有文本(即非子代節(jié)點所包含的文本)中只存在一個數(shù)字,如閱讀數(shù),評論數(shù),價格等那么直接可以直接提取此數(shù)字出來。如果有多個數(shù)字將提取第一個匹配的連續(xù)數(shù)字。text()提取節(jié)點的自有文本node()提取所有節(jié)點
軸
AxisName: 'ancestor' //在當前上下文中節(jié)點的祖先中選擇 | 'ancestor-or-self' //在當前上下文中節(jié)點的祖先及包括自身中選擇 | 'attribute' //標記做提取節(jié)點屬性運算 | 'child' //在當前上下文中節(jié)點的子節(jié)點中選擇 這是xpath默認的軸,如 /div/li 就是 /div/child::li 的簡寫 | 'descendant' //在當前上下文中節(jié)點的后代中選擇 | 'descendant-or-self' //在當前上下文中節(jié)點的后代包括自身中選擇 | 'following' //在當前上下文中節(jié)點后面的全部節(jié)點中選擇 | 'following-sibling' //在當前上下文中節(jié)點后面的全部同胞節(jié)點中選擇 | 'parent' //在當前上下文中節(jié)點的父親節(jié)點中選擇 | 'preceding' //在當前上下文中節(jié)點前面的全部節(jié)點中選擇 | 'preceding-sibling' //在當前上下文中節(jié)點前面的全部同胞節(jié)點中選擇 | 'self' //當前上下文中選擇 | 'following-sibling-one' //在上下文中節(jié)點的前一個同胞節(jié)點中選擇(JsoupXpath擴展) | 'preceding-sibling-one' //在上下文中節(jié)點的下一個同胞節(jié)點選擇(JsoupXpath擴展) | 'sibling' //全部同胞(JsoupXpath擴展)(開發(fā)中。。。) ;
操作符
MINUS : '-'; PLUS : '+'; DOT : '.'; MUL : '*'; DIVISION : '`div`'; MODULO : '`mod`'; DOTDOT : '..'; AT : '@'; COMMA : ','; PIPE : '|'; LESS : '<'; MORE_ : '>'; LE : '='; START_WITH : '^='; // `a^=b` 字符串a(chǎn)以字符串b開頭 a startwith b (JsoupXpath擴展) END_WITH : '$='; // `a*=b` a包含b, a contains b (JsoupXpath擴展) CONTAIN_WITH : '*='; // a包含b, a contains b (JsoupXpath擴展) REGEXP_WITH : '~='; // a的內(nèi)容符合 正則表達式b (JsoupXpath擴展) REGEXP_NOT_WITH : '!~'; //a的內(nèi)容不符合 正則表達式b (JsoupXpath擴展)
