JAVA爬虫的利器 - Jsoup学习使用

1,607 阅读8分钟

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