jsoup学习笔记简明教程
目錄
一.什么是jsoup
?二.解析和遍歷文檔
三.從字符串解析文檔
四.解析body部分
五.從URL加載文檔
六.從文件加載文檔
七.使用DOM方法瀏覽文檔??????
八.使用選擇器語法查找元素
九.從元素中提取屬性,文本和HTML
十.將相對(duì)路徑的URL解析為絕對(duì)URL
十一.設(shè)置屬性值
十二.設(shè)置元素的HTML????
十三. 設(shè)置元素的文本內(nèi)容????
十四.清理HTML
?
一.什么是jsoup
? ? ? ? ? ? ? jsoup是一個(gè)用于實(shí)際處理HTML的Java庫。它使用HTML最佳DOM方法和css選擇器,為獲取URL以及提取和處理數(shù)據(jù)提供了非常方便的API。
? ? ? ? ? ? ? 簡單地說,他是一個(gè)Java爬蟲給的api工具方法。
? ? ? ? ?他的優(yōu)點(diǎn):
? ? ? ? ? ? ? ? ? ? ? ?jsoup 實(shí)現(xiàn)WHATWG HTML5規(guī)范,并將HTML解析為與現(xiàn)代瀏覽器相同的DOM.
? ? ? ? ? ? ? ? ? ? ? ?1.從URL,文件或字符串中抓取并解析HTML
? ? ? ? ? ? ? ? ? ? ? ?2.使用DOM遍歷或css選擇器查找和提取數(shù)據(jù)
? ? ? ? ? ? ? ? ? ? ? 3.處理HTML元素,屬性和文本
? ? ? ? ? ? ? ? ? ? ? 4.根據(jù)安全的白名單清除用戶提交的內(nèi)容,以防止XSS攻擊
? ? ? ? ? ? ? ? ? ? ? 5.輸出整潔的HTML
? ? ? ? ? 個(gè)人理解:jsoup是構(gòu)建DOM樹的方式,對(duì)樹上的元素標(biāo)簽就行處理
?二.解析和遍歷文檔
? ? ? ? ??
//1.測(cè)試 Jsoup.parse 的用法String html = "<html><head><title>First parse</title></head>"+ "<body><p>Parsed HTML into a doc.</p></body></html>";Document doc = Jsoup.parse(html);解析器將盡一切努力從您提供的HTML創(chuàng)建干凈的完整的dom樹
? ? ? ?未關(guān)閉的標(biāo)簽(例如<p>hello 解析為<p>hello</p>)
? ? ? ?隱式標(biāo)簽? (例如,將裸體<td>table data</td> 包裹<table><tr><td>)? ? ? ? ?
? ? ? 可靠得創(chuàng)建文檔結(jié)構(gòu) (html包含head和body,并且頭部中僅包含適當(dāng)?shù)脑?#xff09;
? ? ? ?文檔的對(duì)象模型
? ? ? 文檔由 Elements和TestNode組成
? ? ??繼承鏈 Document擴(kuò)展Element延申Node。TextNode延申Node
? ? ?一個(gè)元素包含一個(gè)子節(jié)點(diǎn)列表。并具有一個(gè)父元素,他們僅提供子元素的過濾列表
? ? ??
三.從字符串解析文檔
? ?問題:
? ? ? ? 你在java字符串中又HTML,并且想要解析該HTML以獲得其內(nèi)容,或者確保其格式正確,或者對(duì)其進(jìn)行修改。該字符串可能來自用戶輸入,文件或者來自網(wǎng)絡(luò)
? ?解決方案:
? ? ? ?使用靜態(tài)的Jsoup.parse(String html)方法或者 Jsoup .parse(String html,String baseUri)如果網(wǎng)頁來自網(wǎng)絡(luò),并且你想要獲取絕對(duì)URL
? ? ??
String html = "<html><head><title>First parse</title></head>"+ "<body><p>Parsed HTML into a doc.</p></body></html>";Document doc = Jsoup.parse(html);四.解析body部分
? ? 問題
? ? ? ? ? ?你要解析正文的HTML片段(例如,div包含幾個(gè)p標(biāo)簽;而不是完整的HTML文檔)??赡苁怯捎脩籼峤换蛟u(píng)論在CMS中編輯頁面的正文提供的。
? ? ?解決方案
? ? ? ? ? ? ?Jsoup.parsebodyFragment(String html);
String html="<div><p>Lorem ipsum.</p>";Document doc=Jsoup.parseBodyFragment(html);Element element =doc.body();? ? ? ? ??
描述?
? ? ? ? ?該parseBodyFragment方法創(chuàng)建一個(gè)空的shell文檔,并將已解析的HTML插入body元素中,如果使用常規(guī)Jsoup.parse(String html)方法,通常會(huì)得到相同的結(jié)果,但是將輸入作為主體片段進(jìn)行顯示處理可確保將用戶提供的所有HTML都解析為body元素
? ? ? 該Documnet.boy()方法檢索文檔body元素的子元素,相當(dāng)于doc.getElementsByTag("body");
?
五.從URL加載文檔
? ? ? 您需要從網(wǎng)上獲取并解析HTML文檔,并在其中查找數(shù)據(jù)
? ? ? 解決方案
? ? ? ? ? ? ?使用Jsoup.connect(String URL);
Document doc3 = Jsoup.connect("http://www.baidu.com/").get();String title = doc3.title();Document doc4 = Jsoup.connect("http://www.baidu.com").data("query", "Java").userAgent("Mozilla").cookie("auth", "token").timeout(3000).post();String tile=doc4.title();? ?
?
?
?描述
? ? ? ?該connect(String url) 方法創(chuàng)建一個(gè)new Connect,并get()獲取解析一個(gè)HTML文件。如果提取網(wǎng)址時(shí)發(fā)生錯(cuò)誤,就會(huì)報(bào)一個(gè)IOException,您將適當(dāng)?shù)奶幚硭?/p>
? ? ? ?該Connect接口旨在用于方法鏈接以構(gòu)建特定的請(qǐng)求
六.從文件加載文檔
? ? ? 問題
? ? ? ? 從磁盤中讀取html文件,您想要加載和解析該文件,然后可以操縱或提取其中的數(shù)據(jù)
? ? ? 解決方案
? ? ? ? ? ?使用靜態(tài) JSoup.parse(File in,String charsetName,String baseUri);
File input = new File("/tmp/input.html"); Document doc = Jsoup.parse(input, "UTF-8", "http://example.com/");描述
? ? ? ? ?該parse(File in, String charsetName, String baseUri)方法加載并解析HTML文件。如果在加載文件時(shí)發(fā)生錯(cuò)誤,它將拋出一個(gè)IOException,您應(yīng)該適當(dāng)?shù)靥幚硭?/span>
? ?baseUri解析器使用該參數(shù)在<base href>找到元素之前解析文檔中的相對(duì)URL?。如果您不擔(dān)心此問題,可以改為傳遞一個(gè)空字符串。
? ? ? ? 有一種姐妹方法parse(File in, String charsetName),該方法使用文件的位置作為baseUri。如果您在本地文件系統(tǒng)站點(diǎn)上工作,并且指向該站點(diǎn)的相對(duì)鏈接也在文件系統(tǒng)上,則此功能很有用。
?
七.使用DOM方法瀏覽文檔
??????
?
? ? ? ? 問題
? ? ? ? ? ? ? 您有一個(gè)要從中提取數(shù)據(jù)的HTML文檔。通常你知道HTML文檔的結(jié)構(gòu)? ?
? ? ?解決問題的思路
??????????????將HTML解析之后,使用DOM的方法Document
? ? ??
5.使用Dom 方法瀏覽文檔Document doc5 = Jsoup.connect("https://www.baidu.com/").get();Elements s_map =doc5.getElementsByTag("map");s_map.forEach((value)->{Elements hrefs=value.getElementsByTag("area");hrefs.forEach((href)->{try {String URL=href.getElementsByAttribute("href").get(0).attr("href");System.out.println("URL = " + URL);} catch (Exception e) {e.printStackTrace();}});});? ? ? 描述?
? ? ? ? ? ?元素提供了一系列類似于DOM的方法來查找元素,以及調(diào)取和處理數(shù)據(jù)。DOM獲取器是上下文的;在父文檔需要調(diào)用的時(shí)候,他在文檔下找到匹配的元素
? ?在子元素上調(diào)用后,他們?cè)谠撟釉叵抡业搅嗽?#xff0c;這樣,您就可以在所需的數(shù)據(jù)上進(jìn)行篩選
?
? ? ? ? ? 尋找元素? ? ? ?
- getElementById(String id)
- getElementsByTag(String tag)
- getElementsByClass(String className)
- getElementsByAttribute(String key)?(和相關(guān)方法)
- 元素的兄弟姐妹:siblingElements(),firstElementSibling(),lastElementSibling(),nextElementSibling(),previousElementSibling()
- 圖:parent(),children(),child(int index)
? ? ? ?
元素?cái)?shù)據(jù)
- attr(String key)獲取和attr(String key, String value)設(shè)置屬性
- attributes()?獲取所有屬性
- id(),className()和classNames()
- text()獲取并text(String value)設(shè)置文本內(nèi)容
- html()獲取并html(String value)設(shè)置內(nèi)部HTML內(nèi)容
- outerHtml()?獲得外部HTML值
- data()獲取數(shù)據(jù)內(nèi)容(例如script和style標(biāo)簽)
- tag()?和?tagName()
處理HTML和文本
- append(String html),?prepend(String html)
- appendText(String text),?prependText(String text)
- appendElement(String tagName),?prependElement(String tagName)
- html(String value)
八.使用選擇器語法查找元素
問題
?????您想使用css或類似jquery的選擇器語法查找或操作元素
解決方案
??????Element.select(String selector)和Elements.select(String selector)方法:
?
使用選擇器語法查找元素 CSS或類似jquery的選擇器語法查找或操作元素。使用Element.select(String selector)和Elements.select(String selector)方法:tagname:按標(biāo)簽查找元素,例如 ans|tag:通過名稱空間中的標(biāo)簽fb|name查找<fb:name>元素,例如,查找元素#id:按ID查找元素,例如 #logo.class:按類名稱查找元素,例如 .masthead[attribute]:具有屬性的元素,例如 [href][^attr]:具有屬性名稱前綴的[^data-]元素,例如查找具有HTML5數(shù)據(jù)集屬性的元素[attr=value]:具有屬性值的元素,例如[width=500](也可以引用,如[data-name='launch sequence'])[attr^=value],[attr$=value],[attr*=value]:與屬性的元素,與開始,結(jié)束與,或包含所述的值,例如[href*=/path/][attr~=regex]:具有與正則表達(dá)式匹配的屬性值的元素;例如img[src~=(?i)\.(png|jpe?g)]*:所有元素,例如 *選擇器組合 el#id:具有ID的元素,例如 div#logo el.class:具有類的元素,例如 div.masthead el[attr]:具有屬性的元素,例如 a[href] 任何組合,例如 a[href].highlight ancestor child:源自祖先的子元素,例如,在“ body”類的塊下的任何位置.body p找到p元素 parent > child:直接從父級(jí)派生的子元素,例如div.content > pfindp元素;并body > *找到身體標(biāo)簽的直接子代 siblingA + siblingB:查找緊隨同級(jí)A的同級(jí)B元素,例如 div.head + div siblingA ~ siblingX:查找同級(jí)X元素后跟同級(jí)A,例如 h1 ~ p el, el, el:將多個(gè)選擇器組合在一起,找到與任何選擇器匹配的唯一元素;例如div.masthead, div.logo偽選擇器:lt(n):查找其兄弟索引(即其在DOM樹中相對(duì)于其父級(jí)的位置)小于的元素n;例如td:lt(3):gt(n):查找兄弟索引大于的元素n; 例如div p:gt(2):eq(n):查找兄弟索引等于的元素n; 例如form input:eq(1):has(selector):查找包含與選擇器匹配的元素的元素;例如div:has(p):not(selector):查找與選擇器不匹配的元素;例如div:not(.logo):contains(text):查找包含給定文本的元素。搜索不區(qū)分大小寫;例如p:contains(jsoup):containsOwn(text):查找直接包含給定文本的元素:matches(regex):查找文本與指定正則表達(dá)式匹配的元素;例如div:matches((?i)login):matchesOwn(regex):查找其自身文本與指定的正則表達(dá)式匹配的元素請(qǐng)注意,上面索引的偽選擇器基于0,即第一個(gè)元素位于索引0,第二個(gè)元素位于1,依此類推。? ? ? ? ? ? ?
九.從元素中提取屬性,文本和HTML
? 問題
? ?解析文檔并找到一些元素以后你需要獲取這些元素里的數(shù)據(jù)
解決方案
? ?要獲取屬性的值,請(qǐng)使用Node.attr(String key)方法
? ?對(duì)于元素(及其組合的子元素)上的文本,請(qǐng)使用Element.text();
? ?對(duì)于使用Element.html(),或Node.outerHtml()酌情使用
?
例子:
// 要獲取屬性的值,請(qǐng)使用Node.attr(String key)方法 // 對(duì)于元素(及其組合的子元素)上的文本,請(qǐng)使用 Element.text() // 對(duì)于HTML,請(qǐng)使用Element.html(),或Node.outerHtml()酌情使用 // // 例子:String html = "<p>An <a href='http://example.com/'><b>example</b></a> link.</p>";Document document=Jsoup.parse(html);String href=document.select("a[href]").attr("href");System.out.println("href = " + href);String text = document.body().text(); // "An example link"System.out.println("text = " + text);String ahref= document.select("a[href]").text();System.out.println("ahref = " + ahref);String outhtml=document.select("a").outerHtml();System.out.println("outhtml = " + outhtml);String htmlHref =document.select("a").html();System.out.println("htmlHref = " + htmlHref);上面的方法是元素?cái)?shù)據(jù)訪問方法的核心。還有其他一些:
Element.id()
Element.tagName()
Element.className() 和 Element.hasClass(String className)
所有這些訪問器方法都有相應(yīng)的設(shè)置器方法來更改數(shù)據(jù)。
?
十.將相對(duì)路徑的URL解析為絕對(duì)URL
解決方案
確保base URI在解析文檔時(shí)指定一個(gè)(從URL加載時(shí)是隱式的)
使用abs:屬性前綴可以從屬性解析絕對(duì)URL:
?
描述
在HTML元素中,通常是相對(duì)于文檔的位置寫URL <a href="/download">...</a>。當(dāng)使用該Node.attr(String key)方法獲取href屬性時(shí),將按源HTML中指定的方式返回該屬性。
如果要獲取絕對(duì)URL,則有一個(gè)屬性鍵前綴abs:,該屬性鍵前綴將導(dǎo)致該屬性值根據(jù)文檔的基本URI(原始位置)進(jìn)行解析:attr("abs:href")
對(duì)于此用例,在解析文檔時(shí)指定基本URI非常重要。
如果您不想使用abs:前綴,那么還有一種方法Node.absUrl(String key)可以執(zhí)行相同的操作,但是可以通過自然屬性鍵進(jìn)行訪問。
package org.jsoup.examples;import org.jsoup.Jsoup; import org.jsoup.helper.Validate; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements;import java.io.IOException;/*** Example program to list links from a URL.*/ public class ListLinks {public static void main(String[] args) throws IOException {Validate.isTrue(args.length == 1, "usage: supply url to fetch");String url = args[0];print("Fetching %s...", url);Document doc = Jsoup.connect(url).get();Elements links = doc.select("a[href]");Elements media = doc.select("[src]");Elements imports = doc.select("link[href]");print("\nMedia: (%d)", media.size());for (Element src : media) {if (src.normalName().equals("img"))print(" * %s: <%s> %sx%s (%s)",src.tagName(), src.attr("abs:src"), src.attr("width"), src.attr("height"),trim(src.attr("alt"), 20));elseprint(" * %s: <%s>", src.tagName(), src.attr("abs:src"));}print("\nImports: (%d)", imports.size());for (Element link : imports) {print(" * %s <%s> (%s)", link.tagName(),link.attr("abs:href"), link.attr("rel"));}print("\nLinks: (%d)", links.size());for (Element link : links) {print(" * a: <%s> (%s)", link.attr("abs:href"), trim(link.text(), 35));}}private static void print(String msg, Object... args) {System.out.println(String.format(msg, args));}private static String trim(String s, int width) {if (s.length() > width)return s.substring(0, width-1) + ".";elsereturn s;} }十一.設(shè)置屬性值
問題
您有一個(gè)解析后的文檔,在將其保存到磁盤或作為HTTP響應(yīng)發(fā)送之前,您想更新屬性值。
解決方案
??????
????????使用屬性設(shè)置器方法Element.attr(String key, String value)和Elements.attr(String key, String value)。
????????如果需要修改class元素的屬性,請(qǐng)使用Element.addClass(String className)和Element.removeClass(String className)方法。
?????????該Elements集合具有批量屬性和類方法。例如,要將rel="nofollow"屬性添加到adiv中的每個(gè)元素:
doc.select("div.comments a").attr("rel", "nofollow");?
? ? 描述
? ? ? 像中的其他方法一樣Element,這些attr方法返回當(dāng)前值Element(或Elements在處理來自select的集合時(shí))。這允許方便的方法鏈接:
doc.select("div.masthead").attr("title", "jsoup").addClass("round-box");? ? ?
十二.設(shè)置元素的HTML
????
問題
????修改元素的HTML。
解決方案
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.append("<p>Last</p>"); // now: <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>"); // now: <li><a href="http://example.com"><span>One</span></a></li>討論區(qū)
Element.html(String html)?清除元素中所有現(xiàn)有的內(nèi)部HTML,并將其替換為已解析的HTML。
Element.prepend(String first)和Element.append(String last)分別添加HTML到元素的內(nèi)部HTML的開始或結(jié)束,
Element.wrap(String around)將HTML包裝在元素的外部HTML周圍。
您還可以使用Element.prependElement(String tag)和Element.appendElement(String tag)方法創(chuàng)建新元素,并將它們作為子元素插入到文檔流中。
?
?
十三. 設(shè)置元素的文本內(nèi)容
????
問題
?????????????
? ? ? ? ? ?修改HTML文檔的文本內(nèi)容。
解決方案
???????????使用以下文本設(shè)置器方法Element:
? ? ? ? ? ?
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>?
討論區(qū)
文本設(shè)置器方法反映了HTML設(shè)置器方法:
Element.text(String text)?清除元素中所有現(xiàn)有的內(nèi)部HTML,并將其替換為提供的文本。
Element.prepend(String first)和Element.append(String last)添加文本節(jié)點(diǎn)到開始或一個(gè)元素的內(nèi)部HTML的結(jié)束
文本應(yīng)以未編碼的形式提供:<,>等字符將被視為文字,而不是HTML。
??
?
十四.清理HTML
?
問題
您要允許不受信任的用戶提供HTML以在您的網(wǎng)站上輸出(例如,作為評(píng)論提交)。您需要清除此HTML,以避免跨站點(diǎn)腳本(XSS)攻擊。
?
解決方案
??????將jsoup HTMLCleaner用于由指定的配置Whitelist。
討論區(qū)
針對(duì)您的網(wǎng)站的跨站點(diǎn)腳本攻擊可能真的毀了您的一天,更不用說用戶了。許多站點(diǎn)通過不允許用戶提交的內(nèi)容中包含HTML來避免XSS攻擊:它們僅強(qiáng)制執(zhí)行純文本,或使用替代標(biāo)記語法(如Wiki-text或Markdown)。這些對(duì)用戶來說很少是最佳解決方案,因?yàn)樗鼈兘档土吮磉_(dá)能力,并迫使用戶學(xué)習(xí)新語法。
更好的解決方案可能是使用富文本格式的所見即所得編輯器(例如CKEditor或TinyMCE)。這些輸出HTML,并允許用戶進(jìn)行可視化工作。但是,它們的驗(yàn)證是在客戶端完成的:您需要應(yīng)用服務(wù)器端的驗(yàn)證來清理輸入并確保HTML可以安全地放置在您的站點(diǎn)上。否則,攻擊者可以避免客戶端Java腳本驗(yàn)證并將不安全的HMTL直接注入您的站點(diǎn)中
jsoup白名單清理程序的工作原理是:解析輸入的HTML(在安全的沙盒環(huán)境中),然后遍歷解析樹,僅允許將已知安全的標(biāo)記和屬性(和值)傳遞到清除的輸出中。
它不使用正則表達(dá)式,這不適用于此任務(wù)。
jsoup提供了一系列Whitelist配置以滿足大多數(shù)需求;如有必要,可以對(duì)其進(jìn)行修改,但要小心。
清理程序不僅對(duì)避免XSS有用,而且在限制用戶可以提供的元素范圍方面也很有用:您可能會(huì)同意text?a,strong元素,但對(duì)結(jié)構(gòu)div或table元素不是很滿意。
?
? ? ?
?
?
總結(jié)
以上是生活随笔為你收集整理的jsoup学习笔记简明教程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 转载:改进的双向启发式搜索算法及其在车载
- 下一篇: 终于弄明白了ThreadLocal