利用 Jsoup 进行高效 Web 抓取与 HTML 处理

news2024/11/27 9:15:04

Jsoup 是一款 Java 的 HTML 解析器,可直接解析某个 URL 地址、HTML 文本内容。它提供了一套非常省力的 API,可通过 DOM,CSS 以及类似于 JQuery 的操作方法来取出和操作数据。

官网:https://jsoup.org/

中文文档:Jsoup 快速入门 | JAVA-TUTORIAL

1. Jsoup相关概念

1. Document

  • 定义:Document 对象表示整个 HTML 文档。
  • 用途:用于解析 HTML 字符串或从 URL 获取 HTML 内容。

2. Element

  • 定义:Element 对象表示 HTML 中的一个标签元素。
  • 用途:用于选择和操作具体的 HTML 元素。

3. Elements

  • 定义:Elements 对象是一个 Element 对象的集合。
  • 用途:用于存储多个匹配的元素。

4. Node

  • 定义:Node 是 Element 和 Text 的基类,表示 HTML 文档中的节点。
  • 用途:用于更细粒度的操作,如处理注释、文档类型声明等。

5. TextNode

  • 定义:TextNode 表示 HTML 文档中的纯文本节点。
  • 用途:用于处理元素内的文本内容。

6. CSS 选择器

  • 定义:CSS 选择器是一种用于选择 HTML 元素的语法。
  • 用途:用于精确选择文档中的特定元素。
  • 常用选择器:
    • #id:选择具有指定 ID 的元素。
    • .class:选择具有指定类的元素。
    • tag:选择指定标签的元素。
    • tag[attr]:选择具有指定属性的元素。
    • tag[attr=value]:选择具有指定属性值的元素。

7. 连接和请求

  • 定义:Jsoup 提供了连接到 URL 并获取 HTML 文档的功能。
  • 用途:用于从远程服务器获取 HTML 内容。

2. Jsoup 的优点

1. 易用性:

  • 简洁的 API:Jsoup 提供了非常简洁和直观的 API,使得开发者可以快速上手。
  • 链式调用:支持链式调用,使代码更加简洁和可读。

2. 强大的解析能力:

  • HTML 解析:能够解析不规范的 HTML,即使 HTML 结构不完整也能正确解析。
  • CSS 选择器:支持类似于 jQuery 的 CSS 选择器,方便提取和操作 HTML 元素。

3. 网络请求:

  • HTTP 请求:内置了简单的 HTTP 客户端,可以方便地发送 GET 和 POST 请求。
  • 自动处理重定向:支持自动处理 HTTP 重定向。

4. 安全性:

  • HTML 清洗:提供了 Jsoup.clean 方法,可以清理 HTML 以防止 XSS 攻击,确保输出的安全性。

3. Jsoup 的缺点

1. 性能问题:

  • 内存消耗:在处理大文件或大量数据时,Jsoup 可能会消耗较多的内存,尤其是在解析复杂的 HTML 文档时。
  • 速度较慢:与一些低级别的解析库相比,Jsoup 的解析速度可能稍慢,特别是在高并发场景下。

2. 功能限制:

  • 有限的 HTTP 功能:虽然内置了 HTTP 客户端,但功能相对简单,对于复杂的需求(如多线程请求、高级认证等)可能需要额外的库支持。
  • 缺乏高级特性:相比于一些更专业的爬虫框架(如 Scrapy),Jsoup 缺乏一些高级特性,如分布式爬取、自动反爬机制等。

3. 依赖管理:

  • 依赖项:Jsoup 本身依赖较少,但在实际项目中可能需要引入其他库来补充其功能,增加了项目的复杂性。

4. 错误处理:

  • 异常处理:Jsoup 的异常处理机制较为简单,对于一些复杂的错误情况可能需要开发者自行处理。

4. 执行流程

4.1. 添加依赖

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

4.2. 获取 Document

Jsoup 类方法列表:

方法名称

是否静态

参数

返回值

描述

parse(String html)

String html

Document

从字符串中解析 HTML 并返回一个 Document 对象。

parse(File in, String charsetName)

File in, String charsetName

Document

从文件中解析 HTML 并返回一个 Document 对象。

parse(URL url, int timeoutMillis)

URL url, int timeoutMillis

Document

从 URL 中解析 HTML 并返回一个 Document 对象。

connect(String url)

String url

Connection

创建一个新的 Connection 对象,用于发送 HTTP 请求。

 Connection 类方法列表:

方法名称

是否静态

参数

返回值

描述

method(Method method)

Method method

Connection

设置请求方法(GET、POST 等)。

url(URL url)

URL url

Connection

设置请求的 URL。

requestBody(String requestBody)

String requestBody

Connection

设置请求体内容。

data(String key, String value)

String key, String value

Connection

添加表单数据。

header(String key, String value)

String key, String value

Connection

添加请求头。

userAgent(String userAgent)

String userAgent

Connection

设置 User-Agent。

referrer(String referrer)

String referrer

Connection

设置 Referer。

timeout(int millis)

int millis

Connection

设置连接超时时间(毫秒)。

followRedirects(boolean follow)

boolean follow

Connection

设置是否自动跟随重定向。

ignoreHttpErrors(boolean ignore)

boolean ignore

Connection

设置是否忽略 HTTP 错误(如 404)。

ignoreContentType(boolean ignore)

boolean ignore

Connection

设置是否忽略内容类型检查。

maxBodySize(int maxSize)

int maxSize

Connection

设置响应体的最大大小(字节)。

cookie(String key, String value)

String key, String value

Connection

添加 Cookie。

cookies(Map<String, String> cookies)

Map<String, String> cookies

Connection

添加多个 Cookie。

execute()

Connection.Response

执行请求并返回响应对象。

get()

Document

发送 GET 请求并返回解析后的 Document 对象。

post()

Document

发送 POST 请求并返回解析后的 Document 对象。

 Connection.Response 类方法列表:

方法名称

是否静态

参数

返回值

描述

body()

String

获取响应体内容。

parse()

Document

解析响应体为 Document 对象。

statusCode()

int

获取响应状态码。

statusMessage()

String

获取响应状态消息。

url()

URL

获取最终请求的 URL(可能经过重定向)。

headers()

Map<String, List<String>>

获取响应头。

header(String key)

String key

String

获取指定响应头的值。

cookies()

Map<String, String>

获取响应中的 Cookie。

cookie(String key)

String key

String

获取指定 Cookie 的值。

 4.3. 获取Element 或 Elements 及 文本内容

Document 类方法列表:

方法名称

是否静态

参数

返回值

描述

title()

String

获取文档的标题。

select(String cssQuery)

String cssQuery

Elements

使用 CSS 选择器选择元素。

getElementsByTag(String tagName)

String tagName

Elements

获取指定标签名的所有元素。

getElementById(String id)

String id

Element

获取指定 ID 的元素。

html()

String

获取文档的 HTML 内容。

text()

String

获取文档的文本内容。

Elements 类方法列表:

方法名称

是否静态

参数

返回值

描述

first()

Element

获取第一个元素。

last()

Element

获取最后一个元素。

size()

int

获取元素的数量。

get(int index)

int index

Element

获取指定索引的元素。

eachText()

List<String>

获取所有元素的文本内容列表。

eachAttr(String attributeKey)

String attributeKey

List<String>

获取所有元素的指定属性值列表。

 Element 类方法列表:

方法名称

是否静态

参数

返回值

描述

attr(String key)

String key

String

获取元素的属性值。

removeAttr(String key)

String key

Element

移除元素的属性。

addClass(String className)

String className

Element

添加 CSS 类。

removeClass(String className)

String className

Element

移除 CSS 类。

text()

String

获取元素的文本内容。

html()

String

获取元素的 HTML 内容。

append(String html)

String html

Element

在元素末尾追加 HTML。

prepend(String html)

String html

Element

在元素开头插入 HTML。

select(String cssQuery)

String cssQuery

Elements

使用 CSS 选择器选择子元素。

5. CSS 选择器

5.1.  基本选择器

1. 标签选择器

  • 选择所有 <div> 标签:div
  • 选择所有 <a> 标签:a

2. 类选择器

  • 选择所有带有 class="example" 的元素:.example

3. ID 选择器

  • 选择 ID 为 example 的元素:#example

4. 属性选择器

  • 选择所有带有 href 属性的 <a> 标签:a[href]
  • 选择所有 href 属性值为 http://example.com 的 <a> 标签:a[href="http://example.com"]
  • 选择所有 href 属性值包含 example 的 <a> 标签:a[href*="example"]
  • 选择所有 href 属性值以 http 开头的 <a> 标签:a[href^="http"]
  • 选择所有 href 属性值以 .html 结尾的 <a> 标签:a[href$=".html"]
  • 选择所有 src 属性值匹配正则表达式的 <img> 标签:img[src~=(?i)(png|jpe?g)]

5. 命名空间选择器

  • 选择所有在 fb 命名空间中的 name 标签:fb|name

6. 通配符选择器

  • 选择所有元素:*

5.2. 组合选择器

1. 后代选择器

  • 选择所有在 <div> 内部的 <p> 标签:div p

2. 子选择器

  • 选择所有直接在 <div> 内部的 <p> 标签:div > p

3. 相邻兄弟选择器

  • 选择所有紧接在 <h1> 后面的 <p> 标签:h1 + p

4. 通用兄弟选择器

  • 选择所有在 <h1> 后面的 <p> 标签:h1 ~ p

5. 元素+ID

  • 选择所有带有 ID 为 logo 的 <div> 标签:div#logo

6. 元素+类

  • 选择所有带有 class="title" 的 <div> 标签:div.title

7. 元素+属性

  • 选择所有带有 href 属性的 <a> 标签:a[href]

8. 多个类选择器

  • 选择所有同时带有 class="info" 和 class="active" 的元素:.info.active

9. 多个选择器组合

  •  选择所有带有 class="highlight" 且带有 href 属性的 <a> 标签:a[href].highlight

5.3. 伪类选择器

1. 索引选择器

  • 选择索引值小于 3 的 <td> 标签:td:lt(3)
  • 选择索引值大于 2 的 <p> 标签:div p:gt(2)
  • 选择索引值等于 1 的 <input> 标签:form input:eq(1)

2. 包含选择器

  • 选择包含 <p> 标签的 <div> 标签:div:has(p)
  • 选择不包含 class="logo" 的所有 <div> 标签:div:not(.logo)

3. 文本匹配选择器

  • 选择包含文本 jsoup 的 <p> 标签:p:contains(jsoup)
  • 选择直接包含文本 jsoup 的 <p> 标签:p:containsOwn(jsoup)

4. 正则表达式匹配选择器

  • 选择文本匹配正则表达式的 <div> 标签:div:matches((?i)login)
  • 选择自身包含文本匹配正则表达式的 <div> 标签:div:matchesOwn((?i)login)

6. 实战示例

以爬取 https://ssr3.scrape.center/ 这个网站为例:

1. 获取所有电影信息。

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.HttpHeaders;

import java.io.IOException;

@SpringBootTest
public class JsoupTests {
    @Test
    public void testJsoup() throws IOException {
        String url = "https://ssr3.scrape.center/";
        Document document = Jsoup.connect(url)
                .header(HttpHeaders.AUTHORIZATION, "Basic YWRtaW46YWRtaW4=")
                .get();
        // 解析电影信息
        Elements movieItems = document.select(".el-card__body");
        for (Element item : movieItems) {
            // 提取电影名称和链接
            Element nameLink = item.select("a.name").first();
            if (nameLink != null) {
                String movieName = nameLink.select("h2").text();
                String movieUrl = nameLink.attr("href");

                // 提取电影封面URL
                Element coverImage = item.select("img.cover").first();
                String coverImageUrl = coverImage != null ? coverImage.attr("src") : "N/A";

                // 提取电影类别
                String category = item.select(".el-button.category").text();

                // 提取国家和片长
                Elements infoElements = item.select(".info");
                String countryAndDuration = infoElements.get(0).text();
                String[] parts = countryAndDuration.split(" / ");
                String country = parts[0];
                String duration = parts[1];

                // 提取上映日期
                String releaseDate = infoElements.get(1).text();

                // 提取评分
                String score = item.select(".score").text();

                // 提取星级评分
                String starRating = item.select(".el-rate").attr("aria-valuenow");

                // 打印提取的信息
                System.out.println("电影名称: " + movieName);
                System.out.println("电影链接: " + movieUrl);
                System.out.println("电影封面URL: " + coverImageUrl);
                System.out.println("电影类别: " + category);
                System.out.println("国家: " + country);
                System.out.println("片长: " + duration);
                System.out.println("上映日期: " + releaseDate);
                System.out.println("评分: " + score);
                System.out.println("星级评分: " + starRating);
                System.out.println("----------------------------");
            }
        }
    }
}

 测试结果为:

 2. 打印所有电影的电影类别、国家和片长、上映日期、评分、星级评分、总条数及页面链接

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.HttpHeaders;

import java.io.IOException;

@SpringBootTest
public class JsoupTests {
    public static void main(String[] args) {
        String url = "https://ssr3.scrape.center/";

        try {
            // 连接并获取文档
            Document document = Jsoup.connect(url)
                    .header("Authorization", "Basic YWRtaW46YWRtaW4=")
                    .get();

            // 提取电影类别
            Elements categoryButtons = document.select(".el-button.category");
            for (Element button : categoryButtons) {
                System.out.println("电影类别: " + button.text());
            }

            // 提取国家和片长
            Elements infoDivs = document.select(".info");
            for (Element div : infoDivs) {
                System.out.println("国家和片长: " + div.text());
            }

            // 提取上映日期
            Elements releaseDateDivs = document.select(".info:contains(上映)");
            for (Element div : releaseDateDivs) {
                System.out.println("上映日期: " + div.text());
            }

            // 提取评分
            Elements scoreElements = document.select(".score");
            for (Element score : scoreElements) {
                System.out.println("评分: " + score.text());
            }

            // 提取星级评分
            Elements rateElements = document.select(".el-rate");
            for (Element rate : rateElements) {
                int fullStars = rate.select(".el-rate__icon.el-icon-star-on").size();
                int halfStar = rate.select(".el-rate__decimal.el-icon-star-on").size();
                double rating = fullStars + (halfStar > 0 ? 0.5 : 0);
                System.out.println("星级评分: " + rating);
            }

            // 提取分页信息
            Element pagination = document.select(".el-pagination").first();
            if (pagination != null) {
                String totalItems = pagination.select(".el-pagination__total").text();
                System.out.println("总条数: " + totalItems);

                Elements pageLinks = pagination.select(".el-pager li.number a");
                for (Element link : pageLinks) {
                    System.out.println("页面链接: " + link.attr("href"));
                }
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

打印结果:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2248323.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【c语言】文件操作详解 - 从打开到关闭

文章目录 1. 为什么使用文件&#xff1f;2. 什么是文件&#xff1f;3. 如何标识文件&#xff1f;4. 二进制文件和文本文件&#xff1f;5. 文件的打开和关闭5.1 流和标准流5.1.1 流5.1.2 标准流 5.2 文件指针5.3 文件的打开和关闭 6. 文件的读写顺序6.1 顺序读写函数6.2 对比一组…

004 逻辑变量与运算

当0和1表示逻辑状态时&#xff0c;两个二进制数码按照某种特定的因果关系进行的运算——就叫&#xff1a;逻辑运算 1.二值逻辑变量与基本逻辑运算 逻辑代数: 与普通代数不同,逻辑代数中的变量只有0和1两个可取值&#xff0c;它们分别用来表示完全两个对立的逻辑状态 逻辑运…

Deepnote、JupyterLab、Google Colab、Amazon SageMaker、VS Code对比

功能比较 平台语言支持扩展性数据连接可视化能力DeepnotePython、R、SQL中等&#xff0c;依赖云端支持主要云平台&#xff08;BigQuery、Snowflake等&#xff09;内置仪表盘与交互图表JupyterLab多种语言&#xff0c;插件支持广泛极高&#xff0c;完全可自定义使用库&#xff…

网络安全中的数据科学如何重新定义安全实践?

组织每天处理大量数据&#xff0c;这些数据由各个团队和部门管理。这使得全面了解潜在威胁变得非常困难&#xff0c;常常导致疏忽。以前&#xff0c;公司依靠 FUD 方法&#xff08;恐惧、不确定性和怀疑&#xff09;来识别潜在攻击。然而&#xff0c;将数据科学集成到网络安全中…

C语言数据结构与算法--简单实现队列的入队和出队

&#xff08;一&#xff09;队列的基本概念 和栈相反&#xff0c;队列(Queue)是一种先进先出&#xff08;First In First Out&#xff09;的线性表。只 允许在表的一端进行插入&#xff0c;而在另一端删除元素&#xff0c;如日常生活中的排队现象。队列中 允许插入的一端叫队尾…

快速理解微服务中Sentinel怎么实现限流

Sentinel是通过动态管理限流规则&#xff0c;根据定义的规则对请求进行限流控制。 一.实现步骤 1.定义资源&#xff1a;在Sentinel中&#xff0c;资源可以是URL、方法等&#xff0c;用于标识需要进行限流的请求&#xff1b;(在Sentinel中&#xff0c;需要我们去告诉Sentinel哪些…

matlab根据excel表头筛选表格数据

有如下表格需要筛选&#xff1a; 如果要筛选style中的A&#xff0c;color中的F2&#xff0c;num中的3。 代码如下&#xff1a; clear;clc; file_Pathstrcat(F:\csdn\,test1.xlsx); %表格路径、文件名 E1readtable(file_Path,Sheet,1); %读取表格中的字母和数字,1代表第一个…

学习日志016--python实现双向循环列表与链栈

python中一些复合数据结构通过类的封装来实现的。双向循环链表与链栈也在其中。 双向循环链表 双向循环链表是一种特殊类型的链表&#xff0c;它结合了双向链表和循环链表的特点。在双向循环链表中&#xff0c;每个节点不仅包含数据&#xff0c;还持有指向前一个和后一个节点的…

【Docker】常用命令汇总

Docker 是1个开源的应用容器引擎&#xff0c;基于Go 语言并遵从 Apache2.0 协议开源。 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中&#xff0c;然后发布到任何流行的 Linux 机器上&#xff0c;也可以实现虚拟化。 容器是完全使用沙箱机制&#xff0c;相…

QT QRadioButton控件 全面详解

本系列文章全面的介绍了QT中的57种控件的使用方法以及示例,包括 Button(PushButton、toolButton、radioButton、checkBox、commandLinkButton、buttonBox)、Layouts(verticalLayout、horizontalLayout、gridLayout、formLayout)、Spacers(verticalSpacer、horizontalSpacer)、…

Docker部署mysql:8.0.31+dbsyncer

Docker部署mysql8.0.31 创建本地mysql配置文件 mkdir -p /opt/mysql/log mkdir -p /opt/mysql/data mkdir -p /opt/mysql/conf cd /opt/mysql/conf touch my.config [mysql] #设置mysql客户端默认字符集 default-character-setUTF8MB4 [mysqld] #设置3306端口 port33…

[SUCTF 2019]EasySQL--详细解析

信息搜集 进入界面是一个搜索框&#xff1a; 查看一下源代码&#xff0c;显示是POST传参&#xff1a; 随便上传个数字1&#xff1a; 抓包测试一下闭合&#xff0c;发现以双引号闭合会回显nonono,单引号闭合则无回显。 由于没有报错信息&#xff0c;所以我们不能确定具体的闭…

警钟长鸣,防微杜渐,遨游防爆手机如何护航安全生产?

近年来&#xff0c;携非防爆手机进入危险作业区引发爆炸的新闻屡见报端。2019年山西某化工公司火灾&#xff0c;2018年延安某煤业瓦斯爆炸&#xff0c;均因工人未用防爆手机产生静电打火引发。涉爆行业领域企业量大面广&#xff0c;相当一部分企业作业场所人员密集&#xff0c;…

【智能流体力学】RAG大模型方法:解决固体力学和流体动力学问题

【使用 AutoGen + GPT-4o + Chainlit UI 进行工程仿真的对话式多智能体 AI 聊天机器人】 本项目构建了一个由多个AI代理组成的系统,这些代理通过使用Microsoft AutoGen进行对话交互,能够自主地创建和仿真固体力学(FEA)和流体动力学(CFD)问题。每个AI代理都擅长规划、问题…

Redis与MySQL如何保证数据一致性

Redis与MySQL如何保证数据一致性 简单来说 该场景主要发生在读写并发进行时&#xff0c;才会发生数据不一致。 主要流程就是要么先操作缓存&#xff0c;要么先操作Redis&#xff0c;操作也分修改和删除。 一般修改要执行一系列业务代码&#xff0c;所以一般直接删除成本较低…

Java项目实战II基于微信小程序的校运会管理系统(开发文档+数据库+源码)

目录 一、前言 二、技术介绍 三、系统实现 四、核心代码 五、源码获取 全栈码农以及毕业设计实战开发&#xff0c;CSDN平台Java领域新星创作者&#xff0c;专注于大学生项目实战开发、讲解和毕业答疑辅导 一、前言 在充满活力与激情的校园生活中&#xff0c;校运会不仅是…

【西瓜书】神经网络-MP神经元、感知机和多层网络

神经网络&#xff08;neural networks&#xff09;的定义&#xff1a;神经网络是由具有适应性的简单单元组成的广泛并行互联的网络&#xff0c;它的组织能够模拟生物神经系统对真实世界物体所作出的交互反应。&#xff08;T. Kohonen 1988年在Neural Networks创刊号上给出的定义…

《安富莱嵌入式周报》第346期:开源2GHz带宽,12bit分辨率,3.2Gsps采样率示波,开源固件安全分析器, 开源口袋电源,开源健康测量,FreeCAD

周报汇总地址&#xff1a;嵌入式周报 - uCOS & uCGUI & emWin & embOS & TouchGFX & ThreadX - 硬汉嵌入式论坛 - Powered by Discuz! 视频&#xff1a; https://www.bilibili.com/video/BV1TYBhYKECK/ 《安富莱嵌入式周报》第346期&#xff1a;开源2GHz带…

介绍一下atoi(arr);(c基础)

hi , I am 36 适合对象c语言初学者 atoi(arr)&#xff1b;是返回整数(int型)&#xff0c;整数是arr数组中字符中数字 格式 #include<stdio.h> atoi(arr); 返回值arr数组中的数字 未改变arr数组 #include<stdlib.h>//atoi(arr); 返 <stdlib> int main(…

Docker: 教程07 - ( 如何对 Docker 进行降级和升级)

如果我们使用 docker 来管理容器&#xff0c;那么保持 docker 引擎的更新将会是十分重要的&#xff0c;这一篇文章我们将会讨论如何对Docker 进行降级和升级。 准备工作 - docker 环境 我们需要拥有一个安装好 docker 的运行环境。 如果你需要了解如何安装 docker 可以通过如…