Jsoup介绍
Jsoup 是一款Java 的HTML解析器,可直接解析某个URL地址、HTML文本内容。它提供了一套非常省力的API,可通过DOM,CSS以及类似于jQuery的操作方法来取出和操作数据。
jsoup是基于MIT协议发布的,可放心使用于商业项目。
能用Jsoup实现什么?
从URL,文件或字符串中刮取并解析HTML
查找和提取数据,使用DOM遍历或CSS选择器
操纵HTML元素,属性和文本
根据安全的白名单清理用户提交的内容,以防止XSS攻击
输出整洁的HTML
爬虫技术没那么难,思路就是这么的简单
得到自己想要爬取数据的url.
通过Jsoup的jar包中的方法将Html解析成Document,
使用Document中的一些列get、first、children等方法获取自己想要的数据,如图片地址、名称、时间。
将得到的数据封装成自己的实体类。
将实体中的数据在页面加载出来。
JSoup应用的主要类
虽然完整的类库中有很多类,但大多数情况下,下面给出3
个类是我们需要重点了解的。
1. org.jsoup.Jsoup类
Jsoup类是任何Jsoup程序的入口点,并将提供从各种来源加载和解析HTML文档的方法。
org.jsoup.Jsoup
类提供了连接,清理和解析HTML文档的方法。重要方法如下:
方法名称 | 描述 |
---|---|
static Connection connect(String url) |
创建并返回URL的连接。 |
static Document parse(File in, String charsetName) |
将指定的字符集文件解析成文档。 |
static Document parse(File in, String charsetName, String baseUri) |
将指定的字符集和baseUri文件解析成文档。 |
static Document parse(String html) |
将给定的html 代码解析成文档。 |
static Document parse(String html, String baseUri) |
用baseUri 将给定的html代码解析成文档。 |
static Document parse(URL url, int timeoutMillis) |
将给定的URL解析为文档。 |
static String clean(String bodyHtml, Whitelist whitelist) |
将输入HTML返回安全的HTML,通过解析输入HTML并通过允许的标签和属性的白名单进行过滤。 |
2. org.jsoup.nodes.Document类
该类表示通过Jsoup库加载HTML文档。可以使用此类执行适用于整个HTML文档的操作。
Element类的重要方法可以参见 - http://jsoup.org/apidocs/org/jsoup/nodes/Document.html 。
查找元素
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()
和classNames()
text()
获取文本内容text(String value)
设置文本内容html()
获取元素内HTMLhtml(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)
3. org.jsoup.nodes.Element类
HTML元素是由标签名称,属性和子节点组成。 使用Element类,您可以提取数据,遍历节点和操作HTML。
Element类的重要方法可参见 - http://jsoup.org/apidocs/org/jsoup/nodes/Element.html 。
jsoup elements对象支持类似于CSS (或jquery)的选择器语法,来实现非常强大和灵活的查找功能。.
这个select
方法在Document
, Element
,或Elements
对象中都可以使用。且是上下文相关的,因此可实现指定元素的过滤,或者链式选择访问。
Select方法将返回一个Elements
集合,并提供一组方法来抽取和处理结果。
Selector选择器概述
tagname
: 通过标签查找元素,比如:a
ns|tag
: 通过标签在命名空间查找元素,比如:可以用fb|name
语法来查找<fb:name>
元素#id
: 通过ID查找元素,比如:#logo
.class
: 通过class名称查找元素,比如:.masthead
[attribute]
: 利用属性查找元素,比如:[href]
[^attr]
: 利用属性名前缀来查找元素,比如:可以用[^data-]
来查找带有HTML5 Dataset属性的元素[attr=value]
: 利用属性值来查找元素,比如:[width=500]
[attr^=value]
,[attr$=value]
,[attr*=value]
: 利用匹配属性值开头、结尾或包含属性值来查找元素,比如:[href*=/path/]
[attr~=regex]
: 利用属性值匹配正则表达式来查找元素,比如:img[src~=(?i)\.(png|jpe?g)]
*
: 这个符号将匹配所有元素
Selector选择器组合使用
el#id
: 元素+ID,比如:div#logo
el.class
: 元素+class,比如:div.masthead
el[attr]
: 元素+class,比如:a[href]
任意组合,比如:
a[href].highlight
ancestor child
: 查找某个元素下子元素,比如:可以用.body p
查找在"body"元素下的所有p
元素parent > child
: 查找某个父元素下的直接子元素,比如:可以用div.content > p
查找p
元素,也可以用body > *
查找body标签下所有直接子元素siblingA + siblingB
: 查找在A元素之前第一个同级元素B,比如:div.head + div
siblingA ~ siblingX
: 查找A元素之前的同级X元素,比如:h1 ~ p
el, el, el
:多个选择器组合,查找匹配任一选择器的唯一元素,例如:div.masthead, div.logo
伪选择器selectors
:lt(n)
: 查找哪些元素的同级索引值(它的位置在DOM树中是相对于它的父节点)小于n,比如:td:lt(3)
表示小于三列的元素:gt(n)
:查找哪些元素的同级索引值大于n
,比如
:div p:gt(2)
表示哪些div中有包含2个以上的p元素:eq(n)
: 查找哪些元素的同级索引值与n
相等,比如:form input:eq(1)
表示包含一个input标签的Form元素:has(seletor)
: 查找匹配选择器包含元素的元素,比如:div:has(p)
表示哪些div包含了p元素:not(selector)
: 查找与选择器不匹配的元素,比如:div:not(.logo)
表示不包含 class=logo 元素的所有 div 列表:contains(text)
: 查找包含给定文本的元素,搜索不区分大不写,比如:p:contains(jsoup)
:containsOwn(text)
: 查找直接包含给定文本的元素:matches(regex)
: 查找哪些元素的文本匹配指定的正则表达式,比如:div:matches((?i)login)
:matchesOwn(regex)
: 查找自身包含文本匹配指定正则表达式的元素
注意:上述伪选择器索引是从0开始的,也就是说第一个元素索引值为0,第二个元素index为1等
应用实例
现在我们来看一些使用Jsoup API处理HTML文档的例子。
1. 载入文件
从URL加载文档,使用Jsoup.connect()
方法从URL加载HTML。
Document document = Jsoup.connect("http://www.yiibai.com").get(); System.out.println(document.title());
connect(String url)
方法创建一个新的 Connection
, 和 get()
取得和解析一个HTML文件。如果从该URL获取HTML时发生错误,便会抛出 IOException,应适当处理。
Connection
接口还提供一个方法链来解决特殊请求,具体如下:
Document doc = Jsoup.connect("http://example.com") .data("query", "Java") .userAgent("Mozilla") .cookie("auth", "token") .timeout(3000) .post();
这个方法只支持Web URLs (http
和https
协议); 假如你需要从一个文件加载,可以使用 parse(File in, String charsetName)
代替。
2. 从文件加载文档
使用Jsoup.parse()
方法从文件加载HTML。
Document document = Jsoup.parse( new File( "D:/temp/index.html" ) , "utf-8" ); System.out.println(document.title());
3. 从String加载文档
使用Jsoup.parse()
方法从字符串加载HTML。
String html = "<html><head><title>First parse</title></head>" + "<body><p>Parsed HTML into a doc.</p></body></html>"; Document document = Jsoup.parse(html); System.out.println(document.title());
4. 获取标题
获取URL的标题
Document doc = Jsoup.connect("http://www.yiibai.com").get(); String title = doc.title(); Java
从HTML文件获取标题
Document doc = Jsoup.parse(new File("e:\\register.html"),"utf-8");//assuming register.html file in e drive String title = doc.title();
5. 获取HTML页面的Fav图标
假设favicon
图像将是HTML文档的<head>
部分中的第一个图像,您可以使用下面的代码。
String favImage = "Not Found"; try { Document document = Jsoup.parse(new File("C:/Users/zkpkhua/Desktop/yiibai-index.html"), "utf-8"); Element element = document.head().select("link[href~=.*\\.(ico|png)]").first(); if (element == null) { element = document.head().select("meta[itemprop=image]").first(); if (element != null) { favImage = element.attr("content"); } } else { favImage = element.attr("href"); } } catch (IOException e) { e.printStackTrace(); } System.out.println(favImage);
6. 获取HTML页面中的所有链接
要获取网页中的所有链接,请使用以下代码。
Document document = Jsoup.parse(new File("C:/Users/zkpkhua/Desktop/yiibai-index.html"), "utf-8"); Elements links = document.select("a[href]"); for (Element link : links) { System.out.println("link : " + link.attr("href")); System.out.println("text : " + link.text()); }
7. 获取HTML页面中的所有图像
要获取网页中显示的所有图像,请使用以下代码。
Document document = Jsoup.parse(new File("C:/Users/zkpkhua/Desktop/yiibai-index.html"), "utf-8"); Elements images = document.select("img[src~=(?i)\\.(png|jpe?g|gif)]"); for (Element image : images) { System.out.println("src : " + image.attr("src")); System.out.println("height : " + image.attr("height")); System.out.println("width : " + image.attr("width")); System.out.println("alt : " + image.attr("alt")); }
8. 获取URL的元信息
元信息包括Google等搜索引擎用来确定网页内容的索引为目的。 它们以HTML页面的HEAD
部分中的一些标签的形式存在。 要获取有关网页的元信息,请使用下面的代码。
Document document = Jsoup.parse(new File("C:/Users/zkpkhua/Desktop/yiibai-index.html"), "utf-8"); String description = document.select("meta[name=description]").get(0).attr("content"); System.out.println("Meta description : " + description); String keywords = document.select("meta[name=keywords]").first().attr("content"); System.out.println("Meta keyword : " + keywords);
9. 在HTML页面中获取表单参数
在网页中获取表单输入元素非常简单。 使用唯一ID查找FORM元素; 然后找到该表单中存在的所有INPUT元素。
Document doc = Jsoup.parse(new File("c:/temp/yiibai-index.html"),"utf-8"); Element formElement = doc.getElementById("loginForm"); Elements inputElements = formElement.getElementsByTag("input"); for (Element inputElement : inputElements) { String key = inputElement.attr("name"); String value = inputElement.attr("value"); System.out.println("Param name: "+key+" \nParam value: "+value); }
10. 更新元素的属性/内容
只要您使用上述方法找到您想要的元素; 可以使用Jsoup API来更新这些元素的属性或innerHTML。 例如,想更新文档中存在的“rel = nofollow
”的所有链接。
Document document = Jsoup.parse(new File("C:/Users/zkpkhua/Desktop/yiibai.com.html"), "utf-8"); Elements links = document.select("a[href]"); links.attr("rel", "nofollow");
11. 消除不信任的HTML(以防止XSS)
假设在应用程序中,想显示用户提交的HTML片段。 例如 用户可以在评论框中放入HTML内容。 这可能会导致非常严重的问题,如果您允许直接显示此HTML。 用户可以在其中放入一些恶意脚本,并将用户重定向到另一个脏网站。
为了清理这个HTML,Jsoup提供Jsoup.clean()
方法。 此方法期望HTML格式的字符串,并将返回清洁的HTML。 要执行此任务,Jsoup使用白名单过滤器。 jsoup白名单过滤器通过解析输入HTML(在安全的沙盒环境中)工作,然后遍历解析树,只允许将已知安全的标签和属性(和值)通过清理后输出。
它不使用正则表达式,这对于此任务是不合适的。
清洁器不仅用于避免XSS,还限制了用户可以提供的元素的范围:您可以使用文本,强元素,但不能构造div
或表元素。
String dirtyHTML = "<p><a href='http://www.yiibai.com/' onclick='sendCookiesToMe()'>Link</a></p>"; String cleanHTML = Jsoup.clean(dirtyHTML, Whitelist.basic()); System.out.println(cleanHTML);
执行后输出结果如下 -
<p><a href="http://www.yiibai.com/" rel="nofollow">Link</a></p>
12、获取所有链接
这个示例程序将展示如何从一个URL获得一个页面。然后提取页面中的所有链接、图片和其它辅助内容。并检查URLs和文本信息。
运行下面程序需要指定一个URLs作为参数
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.tagName().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; } }
示例输入结果
Fetching http://news.ycombinator.com/... Media: (38) * img: <http://ycombinator.com/images/y18.gif> 18x18 () * img: <http://ycombinator.com/images/s.gif> 10x1 () Imports: (2) * link <http://ycombinator.com/news.css> (stylesheet) * link <http://ycombinator.com/favicon.ico> (shortcut icon) Links: (141) * a: <http://ycombinator.com> () * a: <http://news.ycombinator.com/news> (Hacker News)
Jsoup的SSL扩展
现在很多站点都是SSL对数据传输进行加密,这也让普通的HttpConnection无法正常的获取该页面的内容,而Jsoup本身也对次没有做出相应的处理,只是留下来了一个粗糙的使用证书配置什么的方法进行解决。想了一下是否可以让Jsoup可以识别所有的SSL加密过的页面,查询了一些资料,发现可以为本地HttpsURLConnection配置一个“万能证书”,其原理是就是:
重置HttpsURLConnection的DefaultHostnameVerifier,使其对任意站点进行验证时都返回true
重置httpsURLConnection的DefaultSSLSocketFactory, 使其生成随机证书
代码实现
package org.hanmeis.common.html; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; import java.io.FileWriter; import java.io.IOException; import java.net.URL; import java.util.LinkedList; import java.util.List; /** * Created by zhao.wu on 2016/12/2. * 该爬虫用于爬去奇书网的玄幻小说类表 */ public class QiShuuListSpider { //用于保存小说信息的列表 static List<NovelDir> novelDirs = new LinkedList<>(); public static void main(String[] args) throws IOException { //解析过程 URL index = new URL("http://www.qisuu.com/soft/sort02/"); parsePage(index); //将信息存档 FileWriter writer = new FileWriter("qishu.txt"); for (NovelDir novelDir : novelDirs) { writer.write(novelDir.toString()); } writer.close(); } static void parsePage(URL url){ try { //使用Jsoup的解析方法进行填装Dom Document doc = Jsoups.parse(url, 1000); //获取小说列表 Elements novelList = doc.select(".listBox li"); for (Element element : novelList) { NovelDir dir = new NovelDir(); //获取小说作者 Element authorElement = element.select(".s a").first(); if(authorElement!=null) { dir.setAuthor(authorElement.html()); } //获取小说描述 Element descriElement = element.select(".u").first(); if(descriElement!=null) { dir.setDescription(descriElement.html()); } //获取标题、目录地址和封面 Element titleElement = element.select("a").last(); if(titleElement!=null) { dir.setTitle(titleElement.html()); dir.setIndexUrl(titleElement.attr("abs:href")); Element imageElement = titleElement.select("img").first(); if(imageElement!=null) { dir.setHeadPic(imageElement.attr("src")); } } System.out.println(dir); novelDirs.add(dir); } //获取下一页的地址,并进行请求 Elements pageDiv = doc.select(".tspage a"); for (Element element : pageDiv) { if(element.html().equals("下一页")){ //使用“abs:href"获取该页面的绝对地址 String path = element.attr("abs:href"); //由于该站点做了请求频率限制,过快的请求会遭到暂时屏蔽,所以要细水长流的的慢慢请求 Thread.sleep(2000); parsePage(new URL(path)); } } } catch (IOException e) { System.out.println(url); e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } /** * 小说MATE数据对象 */ static class NovelDir{ //封面 private String headPic; //作者 private String author; //标题 private String title; //目录地址 private String indexUrl; //大概描述 private String description; //getter, setter toString } }
SSL扩展代码
package org.hanmeis.common.html; import org.jsoup.Connection; import org.jsoup.helper.HttpConnection; import org.jsoup.nodes.Document; import javax.net.ssl.*; import java.io.IOException; import java.net.URL; import java.security.SecureRandom; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; /** * Created by zhao.wu on 2016/11/29. */ public class Jsoups{ static{ try { //重置HttpsURLConnection的DefaultHostnameVerifier,使其对任意站点进行验证时都返回true HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() { public boolean verify(String hostname, SSLSession session) { return true; } }); //创建随机证书生成工厂 SSLContext context = SSLContext.getInstance("TLS"); context.init(null, new X509TrustManager[] { new X509TrustManager() { public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { } public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { } public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } } }, new SecureRandom()); //重置httpsURLConnection的DefaultSSLSocketFactory, 使其生成随机证书 HttpsURLConnection.setDefaultSSLSocketFactory(context.getSocketFactory()); } catch (Exception e) { e.printStackTrace(); } } /** * 使用ssl的方式去获取远程html的dom, * 该方法在功能上与Jsoup本身的转换工具一样, * 仅仅是用来告诉代码阅读者这个方法已经对SSL进行了扩展 * @param url 需要转换的页面地址 * @param timeoutMillis 请求超市时间 * @return 该页面的dom树 * @throws IOException 请求异常或者转换异常时抛出 */ public static Document parse(URL url, int timeoutMillis) throws IOException { Connection con = HttpConnection.connect(url); con.timeout(timeoutMillis); return con.get(); } }
参看文章
官网地址:https://jsoup.org
官网测试:https://try.jsoup.org
官网指南:https://jsoup.org/cookbook/、中文版:http://www.open-open.com/jsoup/
基于Jsoup实现的简单爬虫:https://blog.csdn.net/WuZuoDingFeng/article/details/53539402