Jsoup使用
Jsoup 是什么
Jsoup是一款基于java语言的html解析器,可以直接对网页URL,文件,字符串文本进行解析,并且可对生成的DOM结构进行增删改查操作,官方包提供了dom及css 选择器的方式来进行解析,API十分简洁方便,对新手用户极好,并且由于Jsoup对网页请求,DOM解析有着很好的支持,常常是爬虫的不二首选
开始使用
使用之前需要先引用下maven依赖或者jar包,截至目前为止,最新的版本是 1.13.1 ,这里我们使用比较稳定的1.11.3版本
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.11.3</version>
</dependency>
需要下载jar包的小伙伴也可以直接打开页面 mvnrepository.com/artifact/or…
点击jar 下载即可
加载文档
Jsoup提供了一个静态基础类Jsoup,可以通过这个类来进行文档加载,目前主要支持如下几种方式
HTML字符串
public static Document parse(String html)
@Test
public void html() throws IOException {
Document document = Jsoup.parse("<html><body><p><span>111</span></p></body></html>");
Elements p = document.select("p span");
System.out.println(p.text());
}
111
URL
public static Connection connect(String url)
@Test
public void test() throws IOException {
Document document = Jsoup.connect("https://gitee.com/").get();
String title = document.title();
System.out.println(title);
}
码云 Gitee — 基于 Git 的代码托管和研发协作平台
文件
public static Document parse(File in, String charsetName) throws IOException
@Test
public void file() throws IOException {
Document document = Jsoup.parse(new File("D:\\a.txt"),"utf-8");
Elements p = document.select("p span");
System.out.println(p.text());
}
111
Jsoup为何如此强大
Jsoup为何如此强大,关键在于Jsoup对常用的api做了很好的封装,并且通俗易懂,小白上手也很快,下面就主要介绍下常用的对象及API,
网络请求
jsoup封装了http请求所涉及的几乎所有api,在Jsoup.connect()方法返回的对象Connection对象中,封装了http请求的常见操作。
cookie
Connection cookie(String k, String v);
Connection cookies(Map<String, String> cookieMap);
支持添加kv形式的单个cookie,同时支持多个cookie的Map的添加,简单直接
get post execute method
Document get() throws IOException;
Document post() throws IOException;
Connection.Response execute() throws IOException;
Connection method(Method method);
封装了常见的请求方式,execute请求默认为get请求
URL
Connection url(URL var1);
Connection url(String var1);
支持多种url请求
Proxy
Connection proxy(Proxy var1);
Connection proxy(String var1, int var2);
支持代理请求
data requestBody
Connection data(String var1, String var2);
Connection data(String var1, String var2, InputStream var3);
Connection data(String var1, String var2, InputStream var3, String var4);
Connection data(Collection<Connection.KeyVal> var1);
Connection data(Map<String, String> var1);
Connection data(String... var1);
Connection requestBody(String body);
支持各种各样的请求data入参
followRedirects
Connection followRedirects(boolean var1);
当当前请求返回状态码为302需要重定向时,是否自动重定向,默认为true即默认进行重定向请求
userAgent
Connection userAgent(String userAgent);
添加请求头部的UserAgent客户端信息
ignoreContentType
Connection ignoreContentType(boolean ignoreContentType);
是否忽略请求返回的数据类型,默认为false,当请求类型不支持时会抛出异常(Unhandled content type. Must be text/, application/xml, or application/+xml),设置为true则忽略
ignoreHttpErrors
Connection ignoreHttpErrors(boolean ignoreHttpErrors);
header headers
Connection header(String name, String value);
Connection headers(Map<String,String> headers);
添加请求头部
referrer
Connection referrer(String referrer);
设置请求头referrer来源
response request
Response response();
Request request();
获取当前请求的request response对象
Document对象
在上面加载文档后会生成一个Document对象,这个对象继承自Element对象和Node对象,
Node
Node是节点的抽象模型并且实现了Cloneable接口,抽象了节点的通用模型,对应的Document,Element,TextNode等为Node节点的具体实例
Element
Element是一个节点和节点属性及其子类集合的描述,最直观的表达就是他的内部属性表示,Tag表示当前HTML节点的标签描述,比如p,a,span,div等等这些标签,childNodes表示其内部子节点,Attributes表示当前HTML节点的属性合集 ,同时内部封装了常见的DOM增删改查操作,其中包括最常见的select方法,通过select选择器进行属性筛选。
private static final List<Node> EMPTY_NODES = Collections.emptyList();
private static final Pattern classSplit = Pattern.compile("\\s+");
private static final String baseUriKey = Attributes.internalKey("baseUri");
private Tag tag;
private WeakReference<List<Element>> shadowChildrenRef; // points to child elements shadowed from node children
List<Node> childNodes;
private Attributes attributes;
Document
Document继承自Element,Document是一个特殊的Element,因为他表示整个DOM的根节点,上面加载文档返回的类型就是Document对象,Docuement中有一个很重要的对象parser,表示解析当前DOM树的Parse解析器
以下是document常用方法,更多方法可以参考Jsoup中文文档 www.open-open.com/jsoup/
- body(),获取body元素节点
- select(),强大的select选择器,支持通过css获取对应的节点元素信息
- getElementById(),通过ID获取元素节点
- attr(String),获取当前节点对应的属性信息
- attr(String,String),设置当前节点属性信息
- text(),获取当前节点内容信息
- text(String),设置当前节点内容信息
- outerHtml(),获取整个html节点信息
- children(),获取当前节点下的所有子节点
- className(),获取当前节点class名称
- ....
Jsoup实战
确认目标
这里我们就以腾讯阅读为例子 http://book.qq.com/,获取他的云起排行榜书籍及相关信息
获取css selector
分析它的dom结构,一顿F12操作后发现,主要信息是在一个class为rankListWrap的div 的 ul下面,class为rankList rankHover numList tabList
直接右键 ul ,选择copy -> copy selector,获取当前UL的dom css 选择器
我这边儿复制出来的是
body > div.pageCenter > div:nth-child(6) > div.rightBox.fr > div.rankListWrap > ul:nth-child(1)
编写代码
我们直接使用Jsoup解析url的方法connect进行请求,获取整体的dom信息
Document document = Jsoup.connect("http://book.qq.com/").get();
获取页面标题
String title = document.title();
System.out.println("title : "+ title);
title : QQ阅读 - 腾讯文学 - 文字之美,感动心灵
获取整个页面的所有图片连接
图片连接都是基于img标签来展示的,我们可以直接通过css选择器来获取所有的img元素
Elements imgs = document.select("img");
获取到所有的img元素后,我们需要获取每个img元素的src的值也就是最终的图片连接
List<String> srcs = imgs.stream().map(element -> element.attr("src")).collect(Collectors.toList());
最终打印我们获取的所有图片连接
srcs.forEach(System.out::println);
http://img1.chuangshi.qq.com/book/p1/txwxLogo.jpg
http://img1.chuangshi.qq.com/chuangshi/p1/code03.png
//bossaudioandcomic-1252317822.image.myqcloud.com/activity/document/758e5004aa42f91d22f191122b6ae5ec.jpg
//bossaudioandcomic-1252317822.image.myqcloud.com/activity/document/b3a3520112e70678e4a638a0257c0ea4.jpg
//bossaudioandcomic-1252317822.image.myqcloud.com/activity/document/862f865fa574e0bc492332ea145e869d.jpg
//bossaudioandcomic-1252317822.image.myqcloud.com/activity/document/235937bdfc1c35f62fce27e9367a05f3.jpg
//bossaudioandcomic-1252317822.image.myqcloud.com/activity/document/a534924729a16f91053d94730e9cbe07.jpg
//bossaudioandcomic-1252317822.image.myqcloud.com/activity/document/950076aefee4a1d5e3897eab8ae22110.jpg
//wfqqreader-1252317822.image.myqcloud.com/cover/485/24180485/t5_24180485.jpg
//wfqqreader-1252317822.image.myqcloud.com/cover/787/23451787/t5_23451787.jpg
//wfqqreader-1252317822.image.myqcloud.com/cover/746/21526746/t5_21526746.jpg
//wfqqreader-1252317822.image.myqcloud.com/cover/661/421661/t5_421661.jpg
//wfqqreader-1252317822.image.myqcloud.com/cover/22/20307022/s_20307022.jpg
//wfqqreader-1252317822.image.myqcloud.com/cover/412/23240412/s_23240412.jpg
//wfqqreader-1252317822.image.myqcloud.com/cover/963/648963/s_648963.jpg
....
...
..
.
http://img1.chuangshi.qq.com/book/p1/footer_img6.png
打印出来的部分图片信息的url是不完整的,缺少http协议,我们可以换一种写法
List<String> srcs = imgs.stream().map(element -> element.attr("abs:src")).collect(Collectors.toList());
较上面的写法,我们多了一个abs表示获取绝对的连接信息,这样获取到的连接就正常了
整体代码如下
Document document = Jsoup.connect("http://book.qq.com/").get();
Elements imgs = document.select("img");
List<String> srcs = imgs.stream().map(element -> element.attr("abs:src")).collect(Collectors.toList());
srcs.forEach(System.out::println);
简单几句就获取到了所有图片的连接信息,Jsoup还是很强大的
同样的如果我们需要获取到整个页面的a标签的href信息,可以直接修改如下即可
Document document = Jsoup.connect("http://book.qq.com/").get();
Elements imgs = document.select("a");
List<String> links = imgs.stream().map(element -> element.attr("abs:href")).collect(Collectors.toList());
links.forEach(System.out::println);
获取排行榜列表
回到正题,我们是要获取到人气榜单书籍及相关信息,第二步的时候我们已经获取到了他的css 选择器
body > div.pageCenter > div:nth-child(6) > div.rightBox.fr > div.rankListWrap > ul:nth-child(1)
上面的css选择器是获取到了对应ul,我们需要获取的其实是ul下面的li标签里面的东西,所以在上面的css选择器的后面 加一个li标签,也就是
body > div.pageCenter > div:nth-child(6) > div.rightBox.fr > div.rankListWrap > ul:nth-child(1) li
我们需要获取 排行,书名,作者,分类,人气量,这几个关键指标
- 排名,在class为detailWrap的div下面span标签的内容,css选择器为
div.detailWrap span - 书名,h4标签下面的a标签内容,css选择器为
h4 > a,其中>表示直接下级元素 - 作者,p标签下的a标签内容,css选择器为
p > a - 分类,p标签下的a标签内容,css选择器为
p > a,作者和分类是同一个css标签,所以可以公用 - 人气量,em标签内容,css选择器为
em
整体代码如下, 其中text方法是获取对应元素内的文本信息,select方法主要是用来获取对应元素
@Test
public void read() throws IOException {
Document document = Jsoup.connect("http://book.qq.com/").get();
Elements lis = document.select("body > div.pageCenter > div:nth-child(6) > div.rightBox.fr > div.rankListWrap > ul:nth-child(1) li");
for (Element li : lis) {
String order = li.select("div.detailWrap span").text();
System.out.print(order+"\t");
String bookName= li.select("h4 > a").text();
System.out.print(bookName+"\t");
Elements nodes = li.select("p > a");
String author = nodes.get(0).text();
String type = nodes.get(1).text();
System.out.print(author+"\t"+type+"\t");
String em = li.select("em").text();
System.out.println(em);
}
}
结果
1 邪王追妻 苏小暖 穿越奇情 7837331
2 神医弃女 MS芙子 东方玄幻 2819006
3 这个大佬画风不对 墨泠 时空穿梭 1071361
4 神医凰后 苏小暖 穿越奇情 1012406
5 快穿之炮灰女配逆袭记 很是矫情 时空穿梭 757821
6 电竞大神暗恋我 战七少 婚恋情缘 708821
7 云家小九超皮哒 水清竹 东方玄幻 660780
8 医妃惊世 顾染锦 穿越奇情 591343
9 锦绣农女种田忙 巅峰小雨 经商种田 589633
10 娱乐圈之女王在上 迷路的龙 娱乐明星 535095