news 2026/1/29 16:33:06

使用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/1/28 9:29:30

阿里云渠道商:GPU 服务器 5 大高频故障排查指南

一、故障 1&#xff1a;GPU 驱动崩溃典型报错&#xff1a; NVIDIA-SMI has failed | Xid errors 排查步骤&#xff1a; 执行诊断命令&#xff1a;dmesg | grep NVRM # 检查内核日志 nvidia-bug-report.sh # 生成完整诊断报告 检查驱动兼容性&#xff1a;确认驱动版本与 CUD…

作者头像 李华
网站建设 2026/1/28 5:10:15

Java一年半经验面试会问哪些问题?

前几天收到一位粉丝私信&#xff0c;说的是他才一年半经验&#xff0c;去面试却被各种问到分布式&#xff0c;高并发&#xff0c;多线程之间的问题。基础层面上的是可以答上来&#xff0c;但是面试官深问的话就不会了&#xff01;被问得都怀疑现在Java招聘初级岗位到底招的是初…

作者头像 李华
网站建设 2026/1/28 18:23:47

从零到上线:6个关键阶段完成Open-AutoGLM私有化部署

第一章&#xff1a;从零开始理解Open-AutoGLM架构与私有化部署价值Open-AutoGLM 是一个面向企业级应用的开源自动化大语言模型框架&#xff0c;融合了自然语言理解、任务编排与私有化部署能力。其核心架构基于模块化解耦设计&#xff0c;支持灵活扩展和深度定制&#xff0c;适用…

作者头像 李华
网站建设 2026/1/29 12:02:12

Open-AutoGLM部署终极方案(支持GPU/CPU双模式快速上手)

第一章&#xff1a;Open-AutoGLM开源如何部署部署 Open-AutoGLM 开源项目需要准备基础环境、克隆代码库并配置运行依赖。该项目基于 Python 构建&#xff0c;支持本地和容器化两种部署方式&#xff0c;适用于多种硬件平台。环境准备 在开始部署前&#xff0c;请确保系统已安装以…

作者头像 李华
网站建设 2026/1/24 10:23:21

MT8870A无线测试仪架设与软件安装指南

MT8870A无线测试仪架设与软件安装指南 在现代无线通信产品量产测试中&#xff0c;面对5G、Wi-Fi 6、蓝牙LE Audio等多标准共存的复杂需求&#xff0c;传统分站式测试方案已难以满足高吞吐量和低综合成本的要求。安立推出的MT8870A模块化无线测试平台&#xff0c;正是为应对这一…

作者头像 李华