Jsoup库详解:Java世界的HTML解析利器
一、Jsoup概述
org.jsoup.Jsoup
是一款强大的Java HTML解析库,它能够:
- 🚀 从URL/文件/字符串加载HTML文档
- 🔍 使用DOM遍历提取数据(类似jQuery语法)
- ✂️ 清理用户输入防止XSS攻击
- 🛠️ 操作HTML元素实现动态修改
- 📦 零依赖轻量级设计(仅280KB)
二、快速开始
1. 添加依赖
<!-- Maven -->
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.16.1</version>
</dependency>
2. 基础示例
// 从URL加载
Document doc = Jsoup.connect("https://example.com").get();
String title = doc.title(); // 获取页面标题
// 从字符串解析
String html = "<div><p>Hello, Jsoup!</p></div>";
Document parsed = Jsoup.parse(html);
// 从文件加载
File input = new File("page.html");
Document fileDoc = Jsoup.parse(input, "UTF-8");
三、核心功能解析
1. 元素选择器(CSS选择器语法)
选择器示例 | 说明 |
---|---|
doc.select("div") | 选择所有div元素 |
doc.select("#logo") | 选择id=logo的元素 |
doc.select(".menu") | 选择class=menu的元素 |
doc.select("a[href]") | 选择带href属性的链接 |
doc.select("div > p") | 选择div直接子元素的p标签 |
实战示例:
Elements news = doc.select(".news-item");
for (Element item : news) {
String title = item.select("h2").text();
String link = item.select("a").attr("abs:href");
System.out.println(title + " -> " + link);
}
2. 数据提取方法
方法 | 说明 | 示例 |
---|---|---|
.text() | 获取元素文本 | element.text() |
.html() | 获取元素HTML | element.html() |
.attr() | 获取属性值 | link.attr("href") |
.absUrl() | 获取绝对URL | link.absUrl("href") |
.data() | 获取data-*属性 | div.data("value") |
3. DOM修改操作
// 修改属性
element.attr("class", "highlight");
// 添加类
element.addClass("active");
// 修改HTML内容
element.html("<strong>New content</strong>");
// 移除元素
element.remove();
四、高级特性
1. 表单处理
// 获取表单数据
Connection.Response res = Jsoup.connect("https://example.com/login")
.data("username", "admin", "password", "secret")
.method(Connection.Method.POST)
.execute();
// 保持会话
Map<String, String> cookies = res.cookies();
Document dashboard = Jsoup.connect("https://example.com/dashboard")
.cookies(cookies)
.get();
2. 请求配置
Document doc = Jsoup.connect(url)
.userAgent("Mozilla/5.0") // 设置User-Agent
.referrer("https://google.com") // 设置来源
.timeout(3000) // 设置超时(ms)
.header("Accept-Language", "en-US") // 自定义头
.followRedirects(true) // 允许重定向
.ignoreHttpErrors(true) // 忽略HTTP错误
.get();
3. XSS防护(HTML清理)
String unsafe = "<p><script>alert('XSS')</script><a href='javascript:evil.com'>Link</a></p>";
// 基本清理
String safe = Jsoup.clean(unsafe, Whitelist.basic());
// 结果: <p><a rel="nofollow">Link</a></p>
// 自定义白名单
Whitelist custom = Whitelist.none()
.addTags("b", "i", "p")
.addAttributes("a", "href");
五、性能优化技巧
- 重用连接对象:
Connection conn = Jsoup.connect(url);
Document doc1 = conn.get();
Document doc2 = conn.newRequest().url(newUrl).get();
- 限制解析范围:
// 只解析body片段
String bodyHtml = doc.body().html();
Document partial = Jsoup.parseBodyFragment(bodyHtml);
- 使用高效选择器:
// 低效(遍历所有元素)
Elements allPs = doc.select("*").select("p");
// 高效(直接定位)
Elements efficientPs = doc.select("p");
- 异步处理:
CompletableFuture.supplyAsync(() -> {
try {
return Jsoup.connect(url).get();
} catch (IOException e) {
throw new RuntimeException(e);
}
}).thenAccept(doc -> {
// 处理文档
});
六、实战案例
1. 新闻爬虫实现
public class NewsCrawler {
private Set<String> visitedUrls = ConcurrentHashMap.newKeySet();
public void crawl(String startUrl, int maxDepth) {
crawlPage(startUrl, maxDepth);
}
private void crawlPage(String url, int depth) {
if (depth <= 0 || !visitedUrls.add(url)) return;
try {
Document doc = Jsoup.connect(url)
.timeout(5000)
.get();
// 提取新闻数据
extractNews(doc);
// 获取并跟踪新链接
doc.select("a[href]").parallelStream()
.map(link -> link.absUrl("href"))
.filter(nextUrl -> !nextUrl.isEmpty())
.forEach(nextUrl -> crawlPage(nextUrl, depth - 1));
} catch (IOException e) {
System.err.println("Error crawling " + url + ": " + e.getMessage());
}
}
private void extractNews(Document doc) {
Elements articles = doc.select("article");
articles.forEach(article -> {
String title = article.select("h2").text();
String content = article.select(".content").text();
System.out.println("Title: " + title);
System.out.println("Content: " + content.substring(0, 50) + "...");
});
}
}
2. 网页内容分析器
public class PageAnalyzer {
public PageStats analyze(String url) throws IOException {
Document doc = Jsoup.connect(url).get();
PageStats stats = new PageStats();
stats.setTitle(doc.title());
stats.setWordCount(doc.body().text().split("\\s+").length);
stats.setLinkCount(doc.select("a[href]").size());
stats.setImageCount(doc.select("img").size());
// 提取元描述
Element meta = doc.select("meta[name=description]").first();
if (meta != null) {
stats.setDescription(meta.attr("content"));
}
return stats;
}
}
@Data // Lombok注解
class PageStats {
private String title;
private String description;
private int wordCount;
private int linkCount;
private int imageCount;
}
七、常见问题解答
Q1: 如何处理动态加载的内容?
Jsoup 不能执行JavaScript,对于SPA或动态内容:
- 考虑使用Selenium等工具获取完整HTML
- 分析XHR请求直接获取数据接口
Q2: 如何解决SSL证书错误?
// 创建信任所有证书的SocketFactory
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[]{new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] chain, String authType) {}
public void checkServerTrusted(X509Certificate[] chain, String authType) {}
public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; }
}}, new SecureRandom());
Document doc = Jsoup.connect(httpsUrl)
.sslSocketFactory(sslContext.getSocketFactory())
.get();
Q3: 如何提高爬取速度?
- 使用连接池:
public class JsoupPool {
private static final int MAX_CONN = 10;
private static final BlockingQueue<Connection> pool = new ArrayBlockingQueue<>(MAX_CONN);
static {
for (int i = 0; i < MAX_CONN; i++) {
pool.offer(Jsoup.newSession());
}
}
public static Document get(String url) throws InterruptedException, IOException {
Connection conn = pool.take();
try {
return conn.newRequest().url(url).get();
} finally {
pool.put(conn);
}
}
}
八、与其他库对比
特性 | Jsoup | HTMLUnit | Selenium |
---|---|---|---|
JavaScript支持 | ❌ 不支持 | ✔️ 支持 | ✔️ 支持 |
执行速度 | ⚡️ 极快 | 🐢 慢 | 🐢 慢 |
内存占用 | 🪶 极低 | 🐘 高 | 🐘 高 |
使用复杂度 | ⭐️ 简单 | ⭐⭐⭐ 复杂 | ⭐⭐⭐⭐ 复杂 |
最佳场景 | 静态页面抓取 | 需要JS渲染 | 自动化测试 |
九、最佳实践建议
- 遵守robots.txt:尊重目标网站的爬取规则
- 设置合理间隔:避免高频请求导致IP被封
- 错误处理:妥善处理网络异常和解析错误
- 缓存机制:对已爬取内容进行本地缓存
- 用户代理轮换:模拟不同浏览器访问
Jsoup凭借其简洁API和出色性能,成为Java开发者处理HTML的首选工具。无论是数据抓取、内容分析还是HTML处理,它都能提供优雅的解决方案。