一、目錄jsoup概述
使用場景
DOM解析
CSS選擇器
HTML過濾
邏輯分析
總結
二、jsoup概述
官方解釋:
jsoup是一個用于處理真實HTML的Java庫。它提供了一個非常友善的API,用于提取和操作資料,使用最好的DOM,CSS和類似jquery的方法。
個人接觸到jsoup是在用java寫爬蟲時,苦惱于大量使用正則比對不僅降低了代碼的可讀性,相對也比較費時費力。這時候,一款爬蟲架構突然引入眼簾,那就是jsoup。作為一款輕量,功能強大的爬蟲架構,jsoup讓簡單抓取網頁資訊變得優雅,便捷。
雖然是一個java庫,但是它的使用邏輯卻無比接近于jQuery,以至于隻要是熟悉或是了解jQuery的人可以輕而易舉地上手這款架構。
三、使用場景
DOM解析
jsoup的dom解析異常簡單嗎,隻需要new一個ducumnet對象即可實作擷取這個網頁元素,接下來以解析一個網頁為例。可以看到,将網頁轉化成ducument類,之後的Element類以及其子類都可以看成是一個個節點,通過調用相關方法實作整個檔案節點的周遊。同時,Element類的getElementByTag讓人很容易聯想到js中的相關方法,是以隻要有點JS基礎和java基礎的人看這段代碼都不會覺得陌生。
這裡以查詢學生成績資訊為例:
public class jsoupTest {
public void getGrade(String stu_num, String id_num) throws IOException {
String testURL = "http://jwc.cqupt.edu.cn/showS tuQmcj.php"; //目标網頁 Connection con = Jsoup.connect(testURL); //擷取連接配接 con.data("xh", stu_num); //填寫參數 con.data("sfzh", id_num);
Document document = con.post(); //選擇發送方式,擷取整個網頁資訊,存在documnet類裡
Element pTable = document.body().getElementsByClass("pTable").get(0); //通過class屬性 ,擷取子類元素 ;
Elements trs = pTable.getElementsByTag("tbody").get(0).children();
trs.forEach(tr -> { //周遊
标簽
if (!tr.children().isEmpty()) {
Element element = tr.getElementsByTag("td").get(0);
if (!element.text().equals("課程類型")) {
GradeInfo gradeInfo = new GradeInfo();
gradeInfo.setProperty(tr.getElementsByTag("td").get(0).text());
String term = tr.getElementsByTag("td").get(1).text();
System.out.println(term);
System.out.println(tr.getElementsByTag("td").get(2).text());
System.out.println(tr.getElementsByTag("td").get(5).text());
System.out.println(tr.getElementsByTag("td").get(6).text());
System.out.println(tr.getElementsByTag("td").get(7).text());
}
}
});
return null;
}
public static void main(String[] args) {
jsoupTest jsouptest = new jsoupTest();
try {
jsouptest.getGrade("2017210246", "010033");
} catch (IOException e) {
e.printStackTrace();
}
}
}
結果:
隻需要幾行就可以完成對html的基本解析,而且所有的操作都可以用js的邏輯解釋。或多或少比原生正則比對要實用地多。
CSS選擇器
jsoup決心是想向前端靠齊了,除了基本的DOM解析操作外,它同時加入了CSS選擇器,這個操作乍一看似乎沒什麼用處,但是當你真正去學習如何使用後你的就會發現這是多少好用。在針對較複雜地語句比對時,使用選擇器可以輕而易舉地篩選出你想要的元素,可以幫你節省大量代碼。
使用方法:可以用Element.select(String selector)和Element.select(String selector)實作.
public void getBySelect() throws IOException {
String testURL = "" +
"
"+
"
"+" 成績"+" 課程"+" 姓名"+" 學号"+
"85"+"國文"+"小明"+"201721001"+
"80"+"數學"+"小明"+"2017210001"+
""; //利用字元串拼接出HTML标簽 //擷取連接配接
Document document = Jsoup.parse(testURL); //将HTML轉化成可周遊地document類 Elements elements = document.select("span:matchesOwn(^8)");
for (Element element:
elements) {
System.out.println(elements.text());
}
}
結果:
CSS選擇器類似于JQuery和CSS中使用的選擇器,可以通過特定的選擇器文法将對指定元素進行篩選
HTML過濾
這個功能也是偶然看見的,不過現在想來也理所當然,過濾網頁資訊本身就是Jsoup分内的事。當時正在看XSS攻擊方面的知識,突然發現jsoup在安全方面已早有考慮,已本身優秀的HTML解析為基礎,抵禦XSS攻擊自然也是十分優秀。
XSS注入本質就是在HTML中插入特定的标簽改變原來标簽的含義,是以防止XSS攻擊的本質是能分辨并及時過濾掉多餘或是無效的HTML标簽。對此,jsoup有一個白名單機制,通過clean方法可以一步通過白名單設定的過濾規則清理所有的标簽,同時也會保留适當标簽和禁止圖檔顯示的功能。
public class JsoupUtil {
private static final Whitelist whitelist = Whitelist.basicWithImages();
private static final Document.OutputSettings outputSettings = new Document.OutputSettings().prettyPrint(false);
static {
// 富文本編輯時一些樣式是使用style來進行實作的 // 比如紅色字型 style="color:red;" // 是以需要給所有标簽添加style屬性 whitelist.addAttributes(":all", "style");
}
public static String clean(String content) {
return Jsoup.clean(content, "", whitelist, outputSettings);
}
public static void main(String[] args) throws FileNotFoundException, IOException {
String text = "ssssss";
System.out.println(clean(text));
}
}
四、邏輯分析
jsoup作為一款輕便的爬蟲架構,全部由Jonathan Hedley獨立寫出,是以代碼相比其他一些笨重的架構要簡潔很多,我通過網上一些解析jsoup源碼的部落格,加深對jsoup的了解。廢話不多說,讓我們看看jsoup的魅力吧!node
借用别人部落格裡整理的圖檔,可以看到,讓java能像js那樣使用類似标簽的嵌套存儲的方法就是在這裡就是利用自定義的node抽象類,将屬性存儲在類似樹狀的結構中這,這樣做不僅有利于之後的DOM樹解析,也容易周遊,有利于性能的提高。
我們再看看CSS選擇器的實作邏輯。這是selector的源碼清單
jsoup在關于selector的實作大緻是利用Evaluator抽象類,Selector選擇的表達式都會通過QueryParser最終編譯到對應的Evaluator類上,然後此類又有很多派生子類,進而分别實作不同功能。邏輯思路還算簡單,但是具體代碼我還不曾仔細研讀,是以在此也不再贅述。不過其中進行嵌套實作對象的思路還是值得借鑒的。
在HTML過濾方面,jsoup防止XSS攻擊的大緻政策是将HTMl字元串解析成document對象,這樣保證了無法通過注入一段無用的腳本和字元串拼接導緻網頁的功能發生了改變
将一些高頻出現的危險系數較高的标簽加入白名單進行提前過濾
總結
jsoup在操作便捷度上已經展現了它的實力,但是在性能上,考慮到它的底層還是通過正則進行比對,是以對于一些一些簡單的HTML解析或許直接正則的最快的;但是,當HTML頁面比較複雜,這便是jsoup大顯身手的時候了。
但是jsoup還是有很多不足,例如隻能處理靜态頁面,對于動态顯示或者後端渲染後後的頁面無法正常進行爬取,這時就需要利用其他的工具例如httpunit進行模拟的ajax請求。
jsoup總歸還隻是個人項目,在後期的維護方面還是存在一定的不确定性,如果需要用應用在一些大型的長久性的項目中還需三思。
jsoup的底層實作還是正則比對,盡管jsoup本身夠輕量,但它依然需要解析整個HTML,再進行進一步的搜尋,是以在一些簡單的網頁解析中,肯定還是直接上正則來的直接來的友善。但是如果網頁的結構足夠複雜,使用正則的的代碼量巨大,那麼jsoup不失為一個不錯的選擇。
總而言之,jsoup作為一款輕量的爬蟲架構,在HTML解析方面的表現還是很不錯的,如果平時希望偷點懶,節省點時間和代碼量,還是很值得推薦的。