目录
一.什么是jsoup
二.解析和遍历文档
三.从字符串解析文档
四.解析body部分
五.从URL加载文档
六.从文件加载文档
七.使用DOM方法浏览文档
八.使用选择器语法查找元素
九.从元素中提取属性,文本和HTML
十.将相对路径的URL解析为绝对URL
十一.设置属性值
十二.设置元素的HTML
十三. 设置元素的文本内容
十四.清理HTML
一.什么是jsoup
jsoup是一个用于实际处理HTML的Java库。它使用HTML最佳DOM方法和css选择器,为获取URL以及提取和处理数据提供了非常方便的API。
简单地说,他是一个Java爬虫给的api工具方法。
他的优点:
WHATWG HTML5规范,并将HTML解析为与现代浏览器相同的DOM.
URL,文件或字符串中抓取并解析HTML
DOM遍历或css选择器查找和提取数据
HTML元素,属性和文本
安全的白名单清除用户提交的内容,以防止XSS攻击
整洁的HTML
构建DOM树的方式,对树上的元素标签就行处理
二.解析和遍历文档
//1.测试 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创建干净的完整的dom树
未关闭的标签(例如<p>hello 解析为<p>hello</p>)
隐式标签 (例如,将裸体<td>table data</td> 包裹<table><tr><td>)
可靠得创建文档结构 (html包含head和body,并且头部中仅包含适当的元素)
文档的对象模型
文档由 Elements和TestNode组成
继承链 Document扩展Element延申Node。TextNode延申Node
一个元素包含一个子节点列表。并具有一个父元素,他们仅提供子元素的过滤列表
三.从字符串解析文档
问题:
java字符串中又HTML,并且想要解析该HTML以获得其内容,或者确保其格式正确,或者对其进行修改。该字符串可能来自用户输入,文件或者来自网络
解决方案:
Jsoup.parse(String html)方法或者 Jsoup .parse(String html,String baseUri)如果网页来自网络,并且你想要获取绝对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包含几个p标签;而不是完整的HTML文档)。可能是由用户提交或评论在CMS中编辑页面的正文提供的。
解决方案
Jsoup.parsebodyFragment(String html);
String html="<div><p>Lorem ipsum.</p>";
Document doc=Jsoup.parseBodyFragment(html);
Element element =doc.body();
描述
parseBodyFragment方法创建一个空的shell文档,并将已解析的HTML插入body元素中,如果使用常规Jsoup.parse(String html)方法,通常会得到相同的结果,但是将输入作为主体片段进行显示处理可确保将用户提供的所有HTML都解析为body元素
Documnet.boy()方法检索文档body元素的子元素,相当于doc.getElementsByTag("body");
五.从URL加载文档
获取并解析HTML文档,并在其中查找数据
解决方案
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) 方法创建一个new Connect,并get()获取解析一个HTML文件。如果提取网址时发生错误,就会报一个IOException,您将适当的处理它
Connect接口旨在用于方法链接以构建特定的请求
六.从文件加载文档
问题
从磁盘中读取html文件,您想要加载和解析该文件,然后可以操纵或提取其中的数据
解决方案
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文件。如果在加载文件时发生错误,它将抛出一个IOException
,您应该适当地处理它。
baseUri
解析器使用该参数在<base href>
找到元素之前解析文档中的相对URL 。如果您不担心此问题,可以改为传递一个空字符串。
有一种姐妹方法parse(File in, String charsetName)
,该方法使用文件的位置作为baseUri
。如果您在本地文件系统站点上工作,并且指向该站点的相对链接也在文件系统上,则此功能很有用。
七.使用DOM方法浏览文档
问题
您有一个要从中提取数据的HTML文档。通常你知道HTML文档的结构
解决问题的思路
将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的方法来查找元素,以及调取和处理数据。DOM获取器是上下文的;在父文档需要调用的时候,他在文档下找到匹配的元素
在子元素上调用后,他们在该子元素下找到了元素,这样,您就可以在所需的数据上进行筛选
寻找元素
-
getElementById(String id)
-
getElementsByTag(String tag)
-
getElementsByClass(String className)
-
getElementsByAttribute(String key)
(和相关方法) - 元素的兄弟姐妹:
siblingElements()
,firstElementSibling()
,lastElementSibling()
,nextElementSibling()
,previousElementSibling()
- 图:
parent()
,children()
,child(int index)
元素数据
-
attr(String key)
获取和attr(String key, String value)
设置属性 -
attributes()
获取所有属性 -
id()
,className()
和classNames()
-
text()
获取并text(String value)
设置文本内容 -
html()
获取并html(String value)
设置内部HTML内容 -
outerHtml()
获得外部HTML值 -
data()
获取数据内容(例如script
和style
标签) -
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:按标签查找元素,例如 a
ns|tag:通过名称空间中的标签fb|name查找<fb:name>元素,例如,查找元素
#id:按ID查找元素,例如 #logo
.class:按类名称查找元素,例如 .masthead
[attribute]:具有属性的元素,例如 [href]
[^attr]:具有属性名称前缀的[^data-]元素,例如查找具有HTML5数据集属性的元素
[attr=value]:具有属性值的元素,例如[width=500](也可以引用,如[data-name='launch sequence'])
[attr^=value],[attr$=value],[attr*=value]:与属性的元素,与开始,结束与,或包含所述的值,例如[href*=/path/]
[attr~=regex]:具有与正则表达式匹配的属性值的元素;例如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:直接从父级派生的子元素,例如div.content > pfindp元素;并body > *找到身体标签的直接子代
siblingA + siblingB:查找紧随同级A的同级B元素,例如 div.head + div
siblingA ~ siblingX:查找同级X元素后跟同级A,例如 h1 ~ p
el, el, el:将多个选择器组合在一起,找到与任何选择器匹配的唯一元素;例如div.masthead, div.logo
伪选择器
:lt(n):查找其兄弟索引(即其在DOM树中相对于其父级的位置)小于的元素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):查找包含给定文本的元素。搜索不区分大小写;例如p:contains(jsoup)
:containsOwn(text):查找直接包含给定文本的元素
:matches(regex):查找文本与指定正则表达式匹配的元素;例如div:matches((?i)login)
:matchesOwn(regex):查找其自身文本与指定的正则表达式匹配的元素
请注意,上面索引的伪选择器基于0,即第一个元素位于索引0,第二个元素位于1,依此类推。
九.从元素中提取属性,文本和HTML
问题
解析文档并找到一些元素以后你需要获取这些元素里的数据
解决方案
Node.attr(String key)方法
对于元素(及其组合的子元素)上的文本,请使用Element.text();
对于使用Element.html()
,或Node.outerHtml()
酌情使用
例子:
// 要获取属性的值,请使用Node.attr(String key)方法
// 对于元素(及其组合的子元素)上的文本,请使用 Element.text()
// 对于HTML,请使用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);
上面的方法是元素数据访问方法的核心。还有其他一些:
Element.id()
Element.tagName()
Element.className() 和 Element.hasClass(String className)
所有这些访问器方法都有相应的设置器方法来更改数据。
十.将相对路径的URL解析为绝对URL
解决方案
确保base URI在解析文档时指定一个(从URL加载时是隐式的)
使用abs:属性前缀可以从属性解析绝对URL:
Document doc = Jsoup.connect("http://jsoup.org").get();
Element link = doc.select("a").first();
String relHref = link.attr("href"); // == "/"
String absHref = link.attr("abs:href"); // "http://jsoup.org/"
描述
在HTML元素中,通常是相对于文档的位置写URL <a href="/download">...</a>。当使用该Node.attr(String key)方法获取href属性时,将按源HTML中指定的方式返回该属性。
如果要获取绝对URL,则有一个属性键前缀abs:,该属性键前缀将导致该属性值根据文档的基本URI(原始位置)进行解析:attr("abs:href")
对于此用例,在解析文档时指定基本URI非常重要。
如果您不想使用abs:前缀,那么还有一种方法Node.absUrl(String key)可以执行相同的操作,但是可以通过自然属性键进行访问。
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));
else
print(" * %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) + ".";
else
return s;
}
}
十一.设置属性值
问题
您有一个解析后的文档,在将其保存到磁盘或作为HTTP响应发送之前,您想更新属性值。
解决方案
Element.attr(String key, String value)和Elements.attr(String key, String value)。
Element.addClass(String className)和Element.removeClass(String className)方法。
rel="nofollow"属性添加到adiv中的每个元素:
doc.select("div.comments a").attr("rel", "nofollow");
描述
像中的其他方法一样Element
,这些attr
方法返回当前值Element
(或Elements
在处理来自select的集合时)。这允许方便的方法链接:
doc.select("div.masthead").attr("title", "jsoup").addClass("round-box");
十二.设置元素的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>
讨论区
Element.html(String html) 清除元素中所有现有的内部HTML,并将其替换为已解析的HTML。
Element.prepend(String first)和Element.append(String last)分别添加HTML到元素的内部HTML的开始或结束,
Element.wrap(String around)将HTML包装在元素的外部HTML周围。
您还可以使用Element.prependElement(String tag)和Element.appendElement(String tag)方法创建新元素,并将它们作为子元素插入到文档流中。
十三. 设置元素的文本内容
问题
修改HTML文档的文本内容。
解决方案
使用以下文本设置器方法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>
讨论区
文本设置器方法反映了HTML设置器方法:
Element.text(String text) 清除元素中所有现有的内部HTML,并将其替换为提供的文本。
Element.prepend(String first)和Element.append(String last)添加文本节点到开始或一个元素的内部HTML的结束
文本应以未编码的形式提供:<,>等字符将被视为文字,而不是HTML。
十四.清理HTML
问题
您要允许不受信任的用户提供HTML以在您的网站上输出(例如,作为评论提交)。您需要清除此HTML,以避免跨站点脚本(XSS)攻击。
解决方案
将jsoup HTMLCleaner用于由指定的配置Whitelist。
String unsafe =
"<p><a href='http://example.com/' onclick='stealCookies()'>Link</a></p>";
String safe = Jsoup.clean(unsafe, Whitelist.basic());
// now: <p><a href="http://example.com/" rel="nofollow">Link</a></p>
讨论区
针对您的网站的跨站点脚本攻击可能真的毁了您的一天,更不用说用户了。许多站点通过不允许用户提交的内容中包含HTML来避免XSS攻击:它们仅强制执行纯文本,或使用替代标记语法(如Wiki-text或Markdown)。这些对用户来说很少是最佳解决方案,因为它们降低了表达能力,并迫使用户学习新语法。
更好的解决方案可能是使用富文本格式的所见即所得编辑器(例如CKEditor或TinyMCE)。这些输出HTML,并允许用户进行可视化工作。但是,它们的验证是在客户端完成的:您需要应用服务器端的验证来清理输入并确保HTML可以安全地放置在您的站点上。否则,攻击者可以避免客户端Java脚本验证并将不安全的HMTL直接注入您的站点中
jsoup白名单清理程序的工作原理是:解析输入的HTML(在安全的沙盒环境中),然后遍历解析树,仅允许将已知安全的标记和属性(和值)传递到清除的输出中。
它不使用正则表达式,这不适用于此任务。
jsoup提供了一系列Whitelist配置以满足大多数需求;如有必要,可以对其进行修改,但要小心。
清理程序不仅对避免XSS有用,而且在限制用户可以提供的元素范围方面也很有用:您可能会同意text a,strong元素,但对结构div或table元素不是很满意。