本文在写作过程中参考了官方文档,传送门。

一、jsoup概述

  jsoup 是一款基于 Java 的HTML解析器,它提供了一套非常省力的API,不但能直接解析某个URL地址、HTML文本内容,而且还能通过类似于DOM、CSS或者jQuery的方法来操作数据,所以 jsoup 也可以被当做爬虫工具使用。

二、相关概念简介

三、获取文档(Document)

获得文档对象 Document 一共有4种方法,分别对应不同的获取方式。

正式开始之前,我们需要导入有关 jar 包。

<dependency>
    <groupId>org.jsoup</groupId>
    <artifactId>jsoup</artifactId>
    <version>1.15.1</version>
</dependency>

3.1)从URL中加载文档对象(常用)

使用 Jsoup.connect(String url).get()方法获取(只支持 http 和 https 协议):

Document doc = Jsoup.connect("http://csdn.com/").get();
String title = doc.title();
System.out.println(title);

connect(String url)方法创建一个新的 Connection并通过.get()或者.post()方法获得数据。如果从该URL获取HTML时发生错误,便会抛出 IOException,应适当处理。

Connection 接口还提供一个方法链来解决特殊请求,我们可以在发送请求时带上请求的头部参数,具体如下:

Document doc = Jsoup.connect("http://csdn.com")
  .data("query", "Java")
  .userAgent("Mozilla")
  .cookie("auth", "token")
  .timeout(8000)
  .post();
  System.out.println(doc);

想获得完整的响应对象和响应码?我们可以使用execute()方法:

// 获得响应对象
Connection.Response response = Jsoup.connect("http://csdn.com").execute();
int                 code     = response.statusCode();
// 输出状态码:200
System.out.println(code);

3.2)从本地文件中加载文档对象

可以使用静态的Jsoup.parse(File in, String charsetName) 方法从文件中加载文档。其中in表示路径,charsetName表示编码方式,示例代码:

File input = new File("/tmp/input.html");
Document doc = Jsoup.parse(input, "UTF-8");
System.out.println(doc);

3.3)从字符串文本中加载文档对象

使用静态的Jsoup.parse(String html) 方法可以从字符串文本中获得文档对象 Document ,示例代码:

String html = "<html><head><title>First parse</title></head>"
  + "<body><p>Parsed HTML into a doc.</p></body></html>";
Document doc = Jsoup.parse(html);
System.out.println(doc);

3.4)从<body>片断中获取文档对象

使用Jsoup.parseBodyFragment(String html)方法.

String html = "<p>Lorem ipsum.</p>";
Document doc = Jsoup.parseBodyFragment(html);
// doc 此时为:<body> <p>Lorem ipsum.</p></body>
Element body = doc.body();
System.out.println(body);

parseBodyFragment 方法创建一个新的文档,并插入解析过的HTML到body元素中。假如你使用正常的 Jsoup.parse(String html) 方法,通常也能得到相同的结果,但是明确将用户输入作为 body 片段处理是个更好的方式。

Document.body() 方法能够取得文档body元素的所有子元素,与 doc.getElementsByTag("body")相同。

四、选择元素(Element)

解析文档对象并获取数据一共有 2 种方式,分别为 DOM方式、CSS选择器方式,我们可以选择一种自己喜欢的方式去获取数据,效果一样。

4.1)DOM方式

将HTML解析成一个Document之后,就可以使用类似于DOM的方法进行操作。

// 获取csdn首页所有的链接
Document doc       = Jsoup.connect("http://csdn.com").get();
Elements elements  = doc.getElementsByTag("body");
Elements contents  = elements.first().getElementsByTag("a");
for (Element content : contents) {
    String linkHref = content.attr("href");
    String linkText = content.text();
    System.out.print(linkText+"\t");
    System.out.println(linkHref);
}

说明

Elements这个对象提供了一系列类似于DOM的方法来查找元素,抽取并处理其中的数据。具体如下:

4.1.1)查找元素

4.1.2)获取元素数据

4.1.3)操作HTML文本

4.2)CSS选择器方式

可以使用类似于CSS选择器的语法来查找和操作元素,常用的方法为select(String selector)

Document doc = Jsoup.connect("http://csdn.com").get();
// 获取带有 href 属性的 a 元素
Elements elements = doc.select("a[href]");
for (Element content : elements) {
    String linkHref = content.attr("href");
    String linkText = content.text();
    System.out.print(linkText + "\t");
    System.out.println(linkHref);
}

4.2.1)说明

select()方法在DocumentElementElements对象中都可以使用,而且是上下文相关的,因此可实现指定元素的过滤,或者采用链式访问。

select() 方法将返回一个Elements集合,并提供一组方法来抽取和处理结果。

4.2.2)select(String selector)方法参数简介

4.2.3)参数属性组合使用

4.2.4)特殊参数:伪选择器

五、获取数据(Node)

在获得文档对象并且指定查找元素后,我们就可以获取元素中的数据。
这些访问器方法都有相应的setter方法来更改数据。

String   html = "<p><a href='http://csdn.com/'><b>example</b></a> link.</p>";
Document doc  = Jsoup.parse(html);
// 查找第一个<a>元素
Element link = doc.select("a").first();
// 输出:example
String text     = link.text();
// 输出:http://csdn.com/
String href = link.attr("href");
// 输出:<b>example</b>
String aHtml     = link.outerHtml();
// 输出:<a href='http://csdn.com/'><b>example</b></a>
String aOuterHtml     = link.outerHtml();

六、修改数据

在解析了一个Document对象之后,你可能想修改其中的某些属性值,并把它输出到前台页面或保存到其他地方,jsoup对此提供了一套非常简便的接口(支持链式写法)。

6.1)设置属性的值

当以下方法针对Element对象操作时,只有一个元素会受到影响。当针对Elements对象进行操作时,可能会影响到多个元素。

Document doc = Jsoup.connect("http://csdn.com").get();
// 复数,Elements
Elements elements = doc.getElementsByClass("text");
// 单数,Element
Element element = elements.first();
// 复数对象,所有 class="text" 的元素都将受到影响
elements.attr("name","goods");
// 单数对象,只有一个元素会受到影响(链式写法)
element.attr("name","shop")
        .addClass("red");

6.2)修改元素的HTML内容

可以使用Element中的HTML设置方法具体如下:

Document doc = Jsoup.connect("http://csdn.com").get();
Element div = doc.select("div").first();
div.html("<p>csdn</p>");
div.prepend("<p>a</p>");
div.append("<p>good</p>");
// 输出:<div"> <p>a</p> <p>csdn</p> <p>good</p> </div>
Element span = doc.select("span").first();
span.wrap("<li><a href='...'></a></li>");
// 输出: <li><a href="..."> <span>csdn</span> </a></li>

6.3)修改元素的文本内容

对于传入的文本,如果含有像 <, > 等这样的字符,将以文本处理,而非HTML。

 // <div></div>
Element div = doc.select("div").first();
div.text(" one "); 
div.prepend(" two ");
div.append(" three ");
// 输出: <div> two one three </div>

七、其他功能

6.1)相对路径转绝对路径

问题描述
  你有一个包含相对URLs路径的HTML文档,现在需要将这些相对路径转换成绝对路径的URLs。

解决方式

  1. 确保在你解析文档时有指定base URI路径。
  2. 然后使用 abs: 属性前缀来取得包含base URI的绝对路径。代码如下:
Document doc = Jsoup.connect("http://www.open-open.com").get();
Element link = doc.select("a").first();
// 输出:/
String relHref = link.attr("href");
// 输出:http://www.open-open.com/
String absHref = link.attr("abs:href");

说明

在HTML元素中,URLs经常写成相对于文档位置的相对路径,如:<a href="/download">...</a>。当你使用 .attr(String key) 方法来取得a元素的href属性时,它将直接返回在HTML源码中指定的值。

假如你需要取得一个绝对路径,需要在属性名前加 abs: 前缀,这样就可以返回包含根路径的URL地址attr("abs:href")。因此在解析HTML文档时,定义base URI非常重要。

如果你不想使用abs: 前缀,还有一个方法能够实现同样的功能 .absUrl(String key)

6.2)消除不受信任的HTML (防止XSS攻击)

问题描述

  在某些网站中经常会提供用户评论的功能,但是有些不怀好意的用户,会搞一些脚本到评论内容中,而这些脚本可能会破坏整个页面的行为,更严重的是获取一些机要信息,此时需要清理该HTML,以避免跨站脚本攻击(XSS)。

解决方式
  使用clean()方法清除恶意代码,但需要指定一个配置的 Safelist(旧版本中是Whitelist),通常使用Safelist.basic()即可。Safelist的工作原理是将输入的 HTML 内容单独隔离解析,然后遍历解析树,只允许已知的安全标签和属性输出。

String unsafe =
        "<p><a href='http://csdn.com/' οnclick='attack()'>Link</a></p>";
// 输出: <p><a href="http://csdn.com/" >Link</a></p>
String safe = Jsoup.clean(unsafe, Safelist.basic());
System.out.println(safe);

说明

  jsoup的Safelist不仅能够在服务器端对用户输入的HTML进行过滤,只输出一些安全的标签和属性,还可以限制用户可以输入的标签范围。

6.2)消除不受信任的HTML (防止XSS攻击)

问题描述

  在某些网站中经常会提供用户评论的功能,但是有些不怀好意的用户,会搞一些脚本到评论内容中,而这些脚本可能会破坏整个页面的行为,更严重的是获取一些机要信息,此时需要清理该HTML,以避免跨站脚本攻击(XSS)。

解决方式
  使用clean()方法清除恶意代码,但需要指定一个配置的 Safelist(旧版本中是Whitelist),通常使用Safelist.basic()即可。Safelist的工作原理是将输入的 HTML 内容单独隔离解析,然后遍历解析树,只允许已知的安全标签和属性输出。

String unsafe =
        "<p><a href='http://csdn.com/' οnclick='attack()'>Link</a></p>";
// 输出: <p><a href="http://csdn.com/" >Link</a></p>
String safe = Jsoup.clean(unsafe, Safelist.basic());
System.out.println(safe);

说明

  jsoup的Safelist不仅能够在服务器端对用户输入的HTML进行过滤,只输出一些安全的标签和属性,还可以限制用户可以输入的标签范围。

6.3)jsoup使用代理

Connection.Response execute = Jsoup.connect("http://csdn.net/")
					               .proxy("12.12.12.12", 1080)	// 使用代理
					               .execute();

如果你读完觉得有收获,不妨点个赞~

发表回复