一、 JSOUP簡介
在以往用java來處理解析HTML文檔或者片段時,我們通常會採用htmlparser(http://htmlparser.sourceforge.net/)這個開源類庫。現在我們有了JSOUP,以後的處理HTML的內容只需要使用JSOUP就已經足夠了,JSOUP有更快的更新,更方便的API等。
jsoup 是一款 Java 的HTML 解析器,可直接解析某個URL地址、HTML常值內容。它提供了一套非常省力的API,可通過DOM,CSS以及類似於jQuery的操作方法來取出和操作資料,可以看作是java版的jQuery。
jsoup的主要功能如下:
- 從一個URL,檔案或字串中解析HTML;
- 使用DOM或CSS選取器來尋找、取出資料;
- 可操作HTML元素、屬性、文本;
jsoup是基於MIT協議發布的,可放心使用於商業項目。官方網站:http://jsoup.org/
二、 解析遍曆HTML文檔
Jsoup處理HTML檔案是,是將使用者輸入的HTML文檔,解析轉換成一個Document對象進行處理。Jsoup一般支援以下幾種來源內容的轉換。
- 解析一個html字串
- 解析一個body片段
- 根據一個url地址載入Document對象
- 根據一個檔案載入Document對象
(一)解析一個html字串
在處理一個html字串。我們可能需要對其進行解析,並提取其內容,或校正其格式是否完整,或者想修改它。Jsoup可以協助我們輕鬆的解決這些問題。
在Jsoup中有一個這樣的靜態方法Jsoup.parse(String html),可以將我們的html片段轉換成Document對象。樣本如下:
| 12 |
String html = "<div><p align=\"center\"><img alt=\"\" width=\"660\" height=\"852\" src=\"/erp/UserFiles/Image/51.jpg\" />這是P元素的內容</p>";Document document = Jsoup.parse(html); |
使用上面的方法,就可以將html字串,轉換成Document對象,一旦有了Document對象,我們就可以使用其中適當的方法根據需求處理問題。我們可以看到這裡轉換的html片段並不是一個合法的html片段,裡面的div標籤沒有閉合。這對於Jsoup來說不是問題,它可以很好的處理這類問題。
(二) 解析body片段
假如我們現在有一個HTML片斷 (比如. 一個 div 包含一對 p 標籤; 一個不完整的HTML文檔) 想對它進行解析。這個HTML片斷可以是使用者提交的一條評論或在一個CMS頁面中編輯body部分。我們可以使用使用Jsoup.parseBodyFragment(String html)方法。
樣本如下:
| 12 |
String html = "<div><p align=\"center\"><img alt=\"\" width=\"660\" height=\"852\" src=\"/erp/UserFiles/Image/51.jpg\" />這是P元素的內容</p>";Document document = Jsoup.parseBodyFragment(html); |
看到這裡可能會有疑問,這個和上面的html片段是一樣的嘛。沒錯是一樣的,parseBodyFragment 方法建立一個空殼的文檔,並插入解析過的HTML到body元素中。假如使用正常的 Jsoup.parse(String html) 方法,通常也可以得到相同的結果,但是明確將使用者輸入作為 body片段處理,以確保使用者所提供的任何糟糕的HTML都將被解析成body元素。
Document.body() 方法能夠取得文檔body元素的所有子項目,與doc.getElementsByTag("body")相同。
(三) 根據一個URL地址載入Document對象
有時候我們可能希望通過一個url地址,然後提取裡面的內容,轉換成document對象。我們以前可能是使用http client等類比一個請求,然後取得返回內容等,使用Jsoup方便簡單的解決這個問題。樣本如下:
| 123 |
Document document = Jsoup.connect("http://www.baidu.com").get();String title = document.title();String text = document.text(); |
connect(String url) 方法建立一個新的 Connection, 和 get() 取得和解析一個HTML檔案。如果從該URL擷取HTML時發生錯誤,便會拋出 IOException,應適當處理。
Connection 介面還提供一個方法鏈來解決特殊請求,具體如下:
| 1 |
Document doc = Jsoup.connect("http://test.com").data("query", "Java").userAgent("Mozilla").cookie("auth", "token").timeout(3000).post(); |
可以向連結地址post參數,設定userAgent,cookie,timeout等,而且這裡是採用的連結操作很方便(熟悉jQuery的應該很熟悉這樣的連結操作)。
(四)根據檔案載入document
有時候我們要處理的html內容,可能是存在硬碟上的某個檔案裡面,我們需要從中提取或者解析某些內容出來,我們可以通過Jsoup來這樣處理。範例程式碼如下:
| 12 |
File input = new File("d:/input.html");Document doc = Jsoup.parse(input, "UTF-8", "http://test.com/"); |
看到這裡可能有一個疑問,第一個參數是檔案,第二是編碼,第三個是什麼呢?第三個參數是baseUrl,使用他我們可以方便的處理相對路徑問題,如果不需要可以不傳,這是一個多態方法,在前面的三個部分裡面,都可以再加一個這樣的baseUrl,後面會詳細講述。
三、 資料幫浦(一) 使用Dom方法遍曆文檔
通過第二章我們可以擷取一個document的對象,我們可以通過這個對象來遍曆文檔,如:
| 1234567 |
Document doc = Jsoup.parse(input, "UTF-8", "http://test.com/");Element content = doc.getElementById("content");Elements links = content.getElementsByTag("a");for(Element link : links) {String linkHref = link.attr("href");String linkText = link.text();} |
這裡我們可以方便的使用Doument對象的方法來擷取內容。常用方法如下:
尋找元素
- getElementById(String id)
- getElementsByTag(String tag)
- getElementsByClass(String className)
- getElementsByAttribute(String key) (and related methods)
- Element siblings: siblingElements(), firstElementSibling(), lastElementSibling();nextElementSibling(), previousElementSibling()
- Graph: parent(), children(), child(int index)
元素資料
- attr(String key)擷取屬性attr(String key, String value)設定屬性
- attributes()擷取所有屬性
- id(), className() and classNames()
- text()擷取常值內容text(String value) 設定常值內容
- html()擷取元素內HTMLhtml(String value)設定元素內的HTML內容
- outerHtml()擷取元素外HTML內容
- data()擷取資料內容(例如:script和style標籤)
- tag() and tagName()
操作HTML和文本
- append(String html), prepend(String html)
- appendText(String text), prependText(String text)
- appendElement(String tagName), prependElement(String tagName)
- html(String value)
(二) 使用選取器來尋找元素
使用jQuery時,我們無不為其強大的選取器歎服,jsoup有同樣的強大的選取器,可以方便我們的對文檔進行處理。範例程式碼如下:
| 123456 |
Elements links = doc.select("a[href]"); //帶有<span style="text-decoration: underline;">href</span>屬性的a元素Elements pngs = doc.select("img[src$=.png]");//副檔名為.<span style="text-decoration: underline;">png</span>的圖片Element masthead = doc.select("div.masthead").first();//class等於<span style="text-decoration: underline;">masthead</span>的<span style="text-decoration: underline;">div</span>標籤Elements resultLinks = doc.select("h3.r > a"); //在h3元素之後的a元素 |
jsoup elements對象支援類似於CSS (或jquery)的選取器文法,來實現非常強大和靈活的尋找功能。.
這個select 方法在Document, Element,或Elements對象中都可以使用。且是上下文相關的,因此可實現指定元素的過濾,或者鏈式選擇訪問。
Select方法將返回一個Elements集合,並提供一組方法來抽取和處理結果。
(三) 從元素中抽取屬性和文檔
使用Jsoup抽取屬性,一般方法如下:
- 要取得一個屬性的值,可以使用Node.attr(String key) 方法
- 對於一個元素中的文本,可以使用Element.text()方法
- 對於要取得元素或屬性中的HTML內容,可以使用Element.html(), 或 Node.outerHtml()方法
樣本如下:
| 1234567891011 |
String html = "<p>An <a href='http://example.com/'><b>example</b></a> link.</p>";Document doc = Jsoup.parse(html);//解析HTML字串返回一個Document實現Element link = doc.select("a").first();//尋找第一個a元素</pre> String text = doc.body().text(); // "An example link"//取得字串中的文本String linkHref = link.attr("href"); // "http://example.com/"//取得連結地址String linkText = link.text(); // "example""//取得連結地址中的文本</pre> String linkOuterH = link.outerHtml();// "<a href="http://example.com"><b>example</b></a>"String linkInnerH = link.html(); // "<b>example</b>"//取得連結內的html內容 |
(四) URL處理
我們在處理HTML內容時,可能經常會遇到這種問題,需要將html頁面裡面的連結地址從相對位址轉換成絕對位址,jsoup有一個方法用來解決此問題。我們前面對到的baseurl,就是用來解決此問題的。範例程式碼如下:
| 12345 |
Document doc = Jsoup.connect("http://www.baidu.com/").get();Element link = doc.select("a").first();String relHref = link.attr("href"); // == "/"String absHref = link.attr("abs:href");// "http://www.baidu.com/gaoji/preferences.html" |
在HTML元素中,URLs經常寫成相對於文檔位置的相對路徑: <a href="/download">...</a>. 當你使用 Node.attr(String key) 方法來取得a元素的href屬性時,它將直接返回在HTML源碼中指定定的值。
假如你需要取得一個絕對路徑,需要在屬性名稱前加 abs: 首碼。這樣就可以返回包含根路徑的URL地址attr("abs:href")
因此,在解析HTML文檔時,定義base URI非常重要。如果你不想使用abs: 首碼,還有一個方法能夠實現同樣的功能 Node.absUrl(String key)。
四、 資料修改(一) 設定屬性值
在處理html時,我們有時候可能需要修改裡面的屬性值,片地址、class名稱等各種屬性。
可以使用屬性設定方法 Element.attr(String key, String value), 和 Elements.attr(String key, String value).
假如你需要修改一個元素的 class 屬性,可以使用 Element.addClass(String className) 和Element.removeClass(String className) 方法。
Elements 提供了大量操作元素屬性和class的方法,比如:要為div中的每一個a元素都添加一個rel="nofollow"
可以使用如下方法:
| 1 |
doc.select("div.comments a").attr("rel", "nofollow"); |
這裡的jsoup方法同樣支援連結操作,如下:
| 1 |
doc.select("div.masthead").attr("title", "jsoup").addClass("round-box"); |
(二) 設定元素的html內容
我們需要向html裡面添加html片段等內容時可以如下操作:
| 123456789 |
Element div = doc.select("div").first(); // <div></div>div.html("<p>lorem ipsum</p>"); // <div><p>lorem ipsum</p></div>div.prepend("<p>First</p>");//在div前添加html內容div.append("<p>Last</p>");//在div之後添加html內容// 添完後的結果: <div><p>First</p><p>lorem ipsum</p><p>Last</p></div>Element span = doc.select("span").first(); // <span>One</span>span.wrap("<li><a href='http://example.com/'></a></li>");//對元素包裹一個外部HTML內容添完後的結果://<li><a href="http://example.com"><span>One</span></a></li> |
(三) 設定元素的常值內容
如果我們需要修改元素內的常值內容,可以如下操作:
| 12345 |
Element div = doc.select("div").first(); // <div></div>div.text("five > four"); // <div>five > four</div>div.prepend("First ");div.append(" Last");// now: <div>First five > four Last</div> |
說明
文本設定方法與 HTML setter 方法一樣:
Element.text(String text) 將清除一個元素中的內部HTML內容,然後提供的文本進行代替
Element.prepend(String first) 和 Element.append(String last) 將分別在元素的內部html前後添加文本節點。
對於傳入的文本如果含有像 <, > 等這樣的字元,將以文本處理,而非HTML。