在日常开发中,URL 内容抓取与解析是 Java 开发者常见的需求之一,例如获取网页数据、提取关键信息等场景。本文将以实际场景为例,详细讲解如何使用 Java 的
HttpClient发送 HTTP 请求,结合
Jsoup解析 HTML 内容,并通过完整代码演示从请求建立到数据提取的全流程,帮助开发者快速掌握相关技术要点。
本次实战选用的技术栈均为 Java 生态中成熟、稳定的工具库,具体如下:
HttpClient 5.x:Apache 官方提供的 HTTP 客户端工具,相比传统
HttpURLConnection,支持更多 HTTP 协议特性,连接池管理更高效,适合高并发场景下的请求发送。Jsoup 1.17.x:轻量级 HTML 解析器,支持 DOM 选择器、CSS 选择器语法,能快速从 HTML 文档中提取指定标签或属性内容,简化解析逻辑。SLF4J+Logback:日志框架,用于记录请求过程中的关键信息(如请求状态、异常信息),便于问题排查。
在开始编码前,需先在 Maven 项目的
pom.xml中引入相关依赖,确保依赖版本兼容:
xml
<dependencies>
<!-- Apache HttpClient 5.x -->
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<version>5.3.1</version>
</dependency>
<!-- Jsoup HTML解析 -->
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.17.2</version>
</dependency>
<!-- 日志框架 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.9</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.4.8</version>
</dependency>
</dependencies>
本次实战的核心需求是:发送 HTTP GET 请求到指定 URL,获取网页 HTML 内容后,提取页面中的文本信息并格式化输出。下面将分模块讲解代码实现逻辑,所有代码均经过测试可直接运行。
首先封装一个通用的 HTTP GET 请求工具类,负责建立连接、发送请求、处理响应,并处理常见的异常(如连接超时、请求失败等)。该工具类支持配置超时时间,确保请求不会因网络问题长期阻塞。
java
运行
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
/**
* HTTP请求工具类,封装HttpClient发送GET请求的逻辑
*/
public class HttpUtils {
private static final Logger LOGGER = LoggerFactory.getLogger(HttpUtils.class);
// 连接超时时间(毫秒)
private static final int CONNECT_TIMEOUT = 5000;
// 响应超时时间(毫秒)
private static final int RESPONSE_TIMEOUT = 5000;
/**
* 发送HTTP GET请求,获取响应内容
* @param url 请求URL
* @return 响应内容(字符串)
* @throws IOException 网络异常或请求失败时抛出
*/
public static String sendGetRequest(String url) throws IOException {
if (url == null || url.trim().isEmpty()) {
throw new IllegalArgumentException("请求URL不能为空");
}
// 创建CloseableHttpClient实例(try-with-resources自动关闭资源)
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
// 构建HttpGet请求对象
HttpGet httpGet = new HttpGet(url);
// 设置请求头,模拟浏览器访问(避免部分网站拒绝爬虫请求)
httpGet.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36");
httpGet.setHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
LOGGER.info("发送GET请求:{}", url);
// 发送请求并获取响应(try-with-resources自动关闭响应)
try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
// 检查响应状态码(200表示请求成功)
int statusCode = response.getCode();
if (statusCode != 200) {
LOGGER.error("请求失败,状态码:{},URL:{}", statusCode, url);
throw new IOException("HTTP请求失败,状态码:" + statusCode);
}
// 提取响应实体内容,转换为字符串(指定UTF-8编码避免乱码)
String responseContent = EntityUtils.toString(
response.getEntity(),
StandardCharsets.UTF_8
);
LOGGER.info("请求成功,响应内容长度:{} 字符", responseContent.length());
return responseContent;
}
} catch (IOException e) {
LOGGER.error("发送GET请求异常,URL:{}", url, e);
throw e; // 抛出异常由调用方处理
}
}
}
获取网页 HTML 内容后,需要解析其中的有效信息。本工具类使用 Jsoup 的 CSS 选择器语法,提取页面中的
<div>标签文本(可根据实际需求调整选择器),并过滤空字符串,返回格式化后的结果列表。
java
运行
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* HTML内容解析工具类,基于Jsoup提取网页中的有效信息
*/
public class HtmlParseUtils {
private static final Logger LOGGER = LoggerFactory.getLogger(HtmlParseUtils.class);
/**
* 解析HTML内容,提取指定标签的文本信息
* @param htmlContent HTML字符串
* @param cssSelector CSS选择器(如"div.content")
* @return 提取的文本列表(去重、去空)
*/
public static List<String> parseHtmlContent(String htmlContent, String cssSelector) {
if (htmlContent == null || htmlContent.trim().isEmpty()) {
LOGGER.warn("HTML内容为空,无法解析");
return new ArrayList<>();
}
if (cssSelector == null || cssSelector.trim().isEmpty()) {
throw new IllegalArgumentException("CSS选择器不能为空");
}
// 将HTML字符串解析为Document对象
Document document = Jsoup.parse(htmlContent);
// 使用CSS选择器获取指定元素
Elements elements = document.select(cssSelector);
LOGGER.info("通过选择器「{}」匹配到 {} 个元素", cssSelector, elements.size());
// 提取元素文本,过滤空字符串并去重
return elements.stream()
.map(Element::text) // 提取元素文本
.map(String::trim) // 去除前后空格
.filter(text -> !text.isEmpty()) // 过滤空文本
.distinct() // 去重
.collect(Collectors.toList());
}
}
主程序类负责串联 HTTP 请求与 HTML 解析的流程,先调用
HttpUtils获取指定 URL 的内容,再通过
HtmlParseUtils提取关键信息,最后将结果打印输出。其中,URL 的请求与解析逻辑完全解耦,便于后续扩展(如增加 POST 请求、调整解析规则等)。
java
运行
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.List;
/**
* 主程序:URL内容抓取与解析演示
*/
public class UrlContentCrawler {
private static final Logger LOGGER = LoggerFactory.getLogger(UrlContentCrawler.class);
public static void main(String[] args) {
// 1. 定义目标URL和CSS选择器
String targetUrl = "https://zhizhangren.cn/pd/54.html"; // 目标URL
String cssSelector = "div"; // 解析所有div标签的文本(可根据实际页面结构调整)
try {
// 2. 发送GET请求,获取HTML内容
LOGGER.info("开始抓取URL:{}", targetUrl);
String htmlContent = HttpUtils.sendGetRequest(targetUrl);
// 3. 解析HTML内容,提取关键信息
LOGGER.info("开始解析HTML内容,使用选择器:{}", cssSelector);
List<String> parsedTextList = HtmlParseUtils.parseHtmlContent(htmlContent, cssSelector);
// 4. 输出解析结果
LOGGER.info("解析完成,共提取 {} 条有效文本:", parsedTextList.size());
for (int i = 0; i < parsedTextList.size(); i++) {
System.out.printf("[%d] %s%n", i + 1, parsedTextList.get(i));
}
} catch (IllegalArgumentException e) {
LOGGER.error("参数异常:{}", e.getMessage());
} catch (IOException e) {
LOGGER.error("网络请求或解析异常:{}", e.getMessage());
} catch (Exception e) {
LOGGER.error("程序运行异常:", e);
}
}
}
HttpUtils中配置代理信息)。
System.out.println(htmlContent)打印完整 HTML,调整
cssSelector(如改为
"body"或具体类名,需结合页面 DOM 结构);若出现乱码,检查
EntityUtils.toString的编码是否为
StandardCharsets.UTF_8,部分网站可能使用
GBK编码,需对应调整。
问题:请求时抛出
ConnectTimeoutException。解决方案:
HttpUtils中的
CONNECT_TIMEOUT(如延长至 10000 毫秒);若目标网站有反爬机制,可增加请求间隔、更换
User-Agent(模拟不同浏览器)。
问题:
parseHtmlContent返回空列表。解决方案:
问题:运行程序后无日志打印。解决方案:
确认
logback-classic依赖已正确引入;在
src/main/resources目录下添加
logback.xml配置文件,指定日志输出级别(如
INFO)和输出方式(控制台 / 文件)。
PoolingHttpClientConnectionManager配置连接池,复用连接减少资源消耗;支持 HTTPS:HttpClient 5.x 默认支持 HTTPS,若遇到证书问题,需自定义
SSLContext忽略证书验证(仅用于测试环境,生产环境需配置合法证书);数据持久化:将解析后的文本信息存入 MySQL、Redis 等数据库,便于后续分析或展示;定时任务:结合
Quartz或 Spring 的
@Scheduled注解,实现定时抓取 URL 内容,实时更新数据。
通过本文的教程,开发者可掌握 Java 中 URL 内容抓取与解析的核心技术,代码逻辑清晰、可扩展性强,可根据实际需求调整请求参数、解析规则或扩展功能。同时,本文提供的实战案例覆盖了开发中的常见场景与问题,帮助开发者避开技术坑,提升开发效率。