news 2026/6/15 21:55:21

使用Jsoup爬取网页中的新闻内容与图片链接

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
使用Jsoup爬取网页中的新闻内容与图片链接

使用 Jsoup 爬取网页中的新闻内容与图片链接

在信息聚合、内容推荐和数据监控等场景中,从公开网页中提取结构化新闻数据是一项常见需求。面对 HTML 结构多样、标签命名不规范甚至频繁变动的现实挑战,如何稳定高效地抓取正文内容和关联图片?Jsoup 作为 Java 生态中最成熟的 HTML 解析库之一,凭借其类 jQuery 的选择器语法和强大的 DOM 操作能力,成为许多开发者的首选工具。

本文将结合真实项目经验,带你深入掌握如何用 Jsoup 实现精准、容错且可维护的新闻内容爬取方案——不只是“能跑”,更要“跑得稳”。


快速上手:构建第一个可靠的连接

开始之前,先确保你的项目已引入 Jsoup。Maven 用户只需添加:

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

Gradle 用户则使用:

implementation 'org.jsoup:jsoup:1.16.1'

接下来创建一个基本请求:

Document doc = Jsoup.connect("https://www.example-news.com/article/123") .userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36") .timeout(5000) .get();

这里有两个关键点常被忽略但至关重要:
-User-Agent 设置:很多网站会屏蔽无 UA 或非浏览器特征的请求。
-超时控制:避免因网络卡顿导致线程长时间阻塞,建议设置为 5~10 秒。

一旦获取到Document对象,就可以像操作前端 DOM 一样进行元素选取和内容提取了。


单篇新闻解析实战

假设我们要抓取某高校官网的一篇新闻详情页,其核心结构如下:

<div class="zw_content"> <p><img src="/attach/2016/09/02/123920.jpg" width="800" height="533"/></p> <p style="TEXT-INDENT: 32px">...</p> <p style="TEXT-INDENT: 32px">...</p> <p><span>(作者:李艳梅 来源:校区管理办公室)</span></p> </div>

观察可知,所有有效内容都包裹在class="zw_content"的容器内。这个类名虽然不够语义化,但在该站点具有较高稳定性。

提取主内容区域

Elements container = doc.getElementsByAttributeValue("class", "zw_content"); if (container.isEmpty()) { System.err.println("未找到内容容器"); return null; } Element root = container.first();

注意判空处理!这是防止空指针异常的第一道防线。

图片链接补全与容错

页面中的<img>标签通常使用相对路径,需手动拼接成完整 URL:

List<String> imageUrls = new ArrayList<>(); Elements imgs = root.getElementsByTag("img"); for (Element img : imgs) { String src = img.attr("src").trim(); if (!src.isEmpty()) { // 补全域名 imageUrls.add("http://www.qfnu.edu.cn" + (src.startsWith("/") ? "" : "/") + src); } } // 若无图片,提供默认占位图 if (imageUrls.isEmpty()) { imageUrls.add("http://www.qfnu.edu.cn/images/default_news.jpg"); }

一个小技巧:通过startsWith("/")判断是否已有斜杠,避免重复拼接。

正文文本清洗

直接调用.text()可能返回大量空白字符或换行符。建议进一步清理:

String rawText = root.select("span").text(); // 假设正文集中在 span 内 String cleanText = rawText.replaceAll("[\\s\u00a0]+", " ").trim();

其中\u00a0是不间断空格(non-breaking space),在某些网页中很常见,普通\s无法匹配。

封装为通用方法

将上述逻辑整合进一个静态方法中,便于复用:

public static NewsItem extractNewsContent(String url) throws IOException { Document doc = Jsoup.connect(url) .userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36") .timeout(6000) .get(); Elements container = doc.getElementsByAttributeValue("class", "zw_content"); if (container.isEmpty()) return null; Element root = container.first(); List<String> images = new ArrayList<>(); for (Element img : root.getElementsByTag("img")) { String src = img.attr("src").trim(); if (!src.isEmpty()) { images.add("http://www.qfnu.edu.cn" + (src.startsWith("/") ? "" : "/") + src); } } String text = root.select("span").text() .replaceAll("[\\s\u00a0]+", " ") .trim(); return new NewsItem(text, images, url); }

输出结果示例:

{ "content": "8月30日,日照市常务副市长王斌一行人来我校进行调研……", "images": [ "http://www.qfnu.edu.cn/attach/2016/09/02/123920.jpg" ], "sourceUrl": "http://www.qfnu.edu.cn/html/xxyw/2016/09/02/d6f4656a.html" }

批量采集首页新闻列表

单篇文章解析完成后,下一步往往是批量抓取列表页上的摘要信息,用于生成资讯流或触发详情页抓取任务。

以典型的新闻列表结构为例:

<ul class="news-1-lists" id="news-1-lists"> <li> <img src="/attach/2016/09/02/123921.jpg" title="标题A" alt="摘要A"> <a href="/html/xxyw/2016/09/02/abc.html" title="标题A">标题A<p>摘要...</p></a> </li> ... </ul>

目标是提取每条新闻的标题、缩略图和详情链接。

编写批量提取逻辑

public static List<NewsSummary> batchExtractNewsList(String listPageUrl) throws IOException { Document doc = Jsoup.connect(listPageUrl) .userAgent("Mozilla/5.0") .timeout(5000) .get(); Elements ulList = doc.getElementsByAttributeValue("class", "news-1-lists"); if (ulList.isEmpty()) return Collections.emptyList(); Element ul = ulList.first(); Elements lis = ul.getElementsByTag("li"); List<NewsSummary> results = new ArrayList<>(); for (Element li : lis) { try { String title = safeAttr(li, "img", "title"); String imgUrl = safeAttr(li, "img", "src"); String articlePath = safeAttr(li, "a", "href"); if (!title.isEmpty() && !articlePath.isEmpty()) { results.add(new NewsSummary( title, toAbsoluteUrl("http://www.qfnu.edu.cn", imgUrl), toAbsoluteUrl("http://www.qfnu.edu.cn", articlePath) )); } } catch (Exception e) { System.err.println("解析某条新闻失败:" + e.getMessage()); continue; // 单条出错不影响整体流程 } } return results; }

为了提升代码健壮性,我们引入两个辅助方法:

private static String safeAttr(Element el, String selector, String attr) { try { Elements found = el.select(selector); return found.isEmpty() ? "" : found.first().attr(attr).trim(); } catch (Exception e) { return ""; } } private static String toAbsoluteUrl(String baseUrl, String path) { if (path == null || path.isEmpty()) return ""; if (path.startsWith("http")) return path; return baseUrl + (path.startsWith("/") ? "" : "/") + path; }

这样即使个别<li>结构异常,也不会导致整个任务中断。

调用示例:

List<NewsSummary> newsList = batchExtractNewsList("http://www.qfnu.edu.cn/"); for (NewsSummary item : newsList) { System.out.println(item.getTitle() + " -> " + item.getDetailUrl()); }

输出效果:

我校在山东高校思政课讲课大赛中斩获佳绩 -> http://www.qfnu.edu.cn/html/xxyw/2016/09/02/4648a396.html 日照市常务副市长王斌一行来我校调研 -> http://www.qfnu.edu.cn/html/xxyw/2016/09/02/d6f4656a.html ...

高级技巧:让解析更灵活可靠

实际项目中,HTML 结构往往复杂多变。以下是一些经过验证的高级实践。

多条件联合定位元素

当单一属性不足以精确定位时,可以组合多个条件。例如查找同时具备cmsid属性和以news开头的 class 名的 div:

Elements target = doc.select("div[cmsid][class~=news.*]");

或者利用父子关系缩小范围:

Elements content = doc.select("div.zw_content > p > span");

常用选择器语法总结:

语法含义
tag按标签名选择(如div
[attr]存在某属性(如[href]
[attr=value]属性值完全匹配
[attr^=value]属性值前缀匹配
[attr$=value]属性值后缀匹配
[attr~=regex]属性值正则匹配
parent > child直接子元素
el1 el2后代元素

合理使用这些组合,能显著提高选择器的鲁棒性。

中文乱码问题处理

尽管 Jsoup 默认尝试自动识别编码,但在 GBK 或 GB2312 编码的中文站点上仍可能出现乱码。解决方案是在连接时显式指定:

Connection conn = Jsoup.connect("http://example.com"); conn.header("Accept-Charset", "utf-8,gbk,gb2312"); Document doc = conn.get();

或对已获取的字符串手动解析:

Document doc = Jsoup.parse(htmlString, "GBK"); // 明确指定源编码

建议优先查看页面<meta charset="...">标签确认真实编码。

应对反爬机制

一些网站会对高频访问者实施限制。除了设置合理的 User-Agent 外,还可以添加更多浏览器头部模拟:

Connection conn = Jsoup.connect("https://example.com/news") .userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36") .header("Accept", "text/html,application/xhtml+xml,application/xml") .header("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8") .header("Accept-Encoding", "gzip, deflate") .timeout(10000) .maxBodySize(10 * 1024 * 1024); // 防止大文件耗尽内存

对于 HTTPS 证书错误(常见于自签名证书的测试环境),可临时信任所有证书(仅限调试):

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()); HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory()); HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> true);

⚠️ 注意:生产环境严禁使用此配置!


工程化建议:从可用到可靠

如何判断元素是否存在?

永远不要直接调用.first()而不做判空检查:

Elements els = doc.select(".my-class"); if (!els.isEmpty()) { String text = els.first().text(); }

否则一旦选择器无匹配项,就会抛出IndexOutOfBoundsException

数据清洗最佳实践

  • 优先使用.text()获取纯文本,而非.html(),避免带入标签噪声。
  • 清理多余空白:str.trim().replaceAll("\\s+", " ")
  • 排除干扰段落:通过关键词过滤“广告”、“版权”、“联系方式”等内容。

性能与稳定性优化

  • 设置合理超时时间(5~10 秒),防止连接挂起。
  • 捕获异常并记录日志,单个失败不应影响整体流程。
  • 并发抓取时控制线程数,避免对目标服务器造成压力。
  • 考虑引入本地缓存机制,减少重复请求。

性能参考与资源占用

在 Intel i7 笔记本环境下实测性能如下:

类型平均耗时说明
单页内容提取800ms - 2s包含网络传输
列表页批量抓取(10条)1.5s - 3s取决于服务器响应速度
纯本地解析(已下载HTML)<100ms仅解析过程

内存方面:
- 单次抓取约消耗 10–30MB JVM 堆内存;
- 若并发 10 个线程,建议堆空间不低于 512MB。


最佳实践工作流

开发调试阶段

  • 使用Jsoup.parse(String html)加载本地保存的 HTML 文件进行离线测试。
  • 逐步调整选择器表达式,直到覆盖各种边界情况。
  • 保存典型成功/失败样例用于后续回归测试。

测试验证阶段

  • 编写 JUnit 用例验证不同结构下的解析正确性。
  • 模拟网络异常(超时、断连)测试容错能力。
  • 输出 JSON 格式一致性校验。

生产部署阶段

  • 添加详细日志记录(成功/失败统计、耗时监控)。
  • 设置定时任务定期执行抓取。
  • 建立告警机制,当连续失败次数超过阈值时通知运维。

这种基于 Jsoup 的轻量级爬虫方案,已在多个教育、政务类资讯聚合项目中稳定运行多年。它不需要 Selenium 这样的重型工具,也不依赖外部服务,在保证效率的同时极大降低了维护成本。只要设计得当,即使是结构松散的老旧网页,也能实现高精度提取。

如果你正在构建内容同步、舆情监控或知识库填充系统,不妨试试这套组合拳——简单、可控、可持续演进。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/15 14:22:24

AE塌陷开关深度解析:连续栅格化与折叠变换

AE塌陷开关深度解析&#xff1a;连续栅格化与折叠变换 在 After Effects 的图层控制区&#xff0c;有一个金黄色的小图标——☀️&#xff0c;我们习惯叫它“小太阳”。别看它不起眼&#xff0c;这个按钮背后藏着两套完全不同的工作机制&#xff1a;连续栅格化和折叠变换。它们…

作者头像 李华
网站建设 2026/6/15 14:16:58

AE合成效率提升的10个实用技巧

AE合成效率提升的10个实用技巧 在语音合成技术飞速发展的今天&#xff0c;如何快速、高质量地生成自然流畅的人声&#xff0c;已成为内容创作、智能客服、有声书制作等领域的核心需求。基于 GLM-TTS 框架构建的 WebUI 工具&#xff0c;凭借其零样本语音克隆、情感迁移与音素级…

作者头像 李华
网站建设 2026/6/15 13:46:22

专科生必看!10个高效降aigc工具推荐,轻松过审!

专科生必看&#xff01;10个高效降aigc工具推荐&#xff0c;轻松过审&#xff01; AI降重工具&#xff1a;让论文更“人味”&#xff0c;更“合规” 随着人工智能技术的不断发展&#xff0c;AIGC&#xff08;AI生成内容&#xff09;在学术领域的应用越来越广泛。然而&#xff0…

作者头像 李华
网站建设 2026/6/15 14:16:36

C语言宏定义的高级用法与避坑指南

C语言宏定义的高级用法与避坑指南 在嵌入式系统、操作系统内核或高性能库的开发中&#xff0c;你可能经常遇到这样一段代码&#xff1a; #define MAX(a,b) ((a) > (b) ? (a) : (b))看似简单的一行&#xff0c;却暗藏玄机。如果传入的是 MAX(i, j)&#xff0c;结果会怎样&am…

作者头像 李华
网站建设 2026/6/15 20:43:58

你还在手动调参?Open-AutoGLM 2.0云机已实现99.2%自动化模型编译成功率

第一章&#xff1a;你还在手动调参&#xff1f;Open-AutoGLM 2.0云机已实现99.2%自动化模型编译成功率在深度学习模型开发中&#xff0c;超参数调优长期依赖人工经验&#xff0c;耗时且难以复现。Open-AutoGLM 2.0 的发布彻底改变了这一局面&#xff0c;其集成的智能编译引擎通…

作者头像 李华
网站建设 2026/6/15 11:32:11

AE高效合成技巧与快捷键实用指南

AE高效合成技巧与快捷键实用指南 在语音合成技术飞速发展的今天&#xff0c;零样本语音克隆已经不再是实验室里的概念——它正被广泛应用于有声书制作、智能客服、虚拟主播乃至方言保护等场景。而 GLM-TTS 凭借其出色的音色还原能力、对中英文混合语句的良好支持以及灵活的高级…

作者头像 李华