池州市网站建设_网站建设公司_网站备案_seo优化
2025/12/26 16:17:48 网站建设 项目流程

Jsoup爬取网页图片与新闻内容实战

在当今信息爆炸的时代,从海量网页中高效提取结构化数据已成为许多应用场景的基础需求——无论是构建资讯聚合平台、监控竞品动态,还是做舆情分析。而Java生态中的Jsoup,正是这样一个简洁却强大的HTML解析利器。

它不像Selenium那样“笨重”,也不需要复杂的配置,几行代码就能完成一次精准的网页内容抓取。本文将带你深入实战,用真实案例演示如何利用Jsoup稳定、安全地提取新闻正文与配图,并规避常见陷阱。


快速上手:第一个爬虫任务

要开始使用Jsoup,首先确保你的项目已引入依赖。推荐通过Maven管理:

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

如果无法使用Maven,也可以手动下载jsoup-1.16.1.jar并添加到项目的构建路径中。

接下来,尝试运行一段最基础的代码:

Document doc = Jsoup.connect("http://www.example.com/news.html").get(); System.out.println(doc.title());

这段代码会发起一个HTTP GET请求,获取目标页面的完整HTML文档,并自动处理编码、重定向等问题,最终返回一个可操作的Document对象。

⚠️ 注意:所有网络请求都可能抛出IOException,务必做好异常捕获和日志记录。


精准提取:定位与清洗内容

真正的挑战从来不是“能不能拿到HTML”,而是“能否准确提取所需内容”。我们以某高校官网的一篇新闻为例(如qfnu.edu.cn),展示完整的提取流程。

第一步:连接并加载文档

Document doc; try { doc = Jsoup.connect("http://www.qfnu.edu.cn/html/xxyw/...html") .userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36") .timeout(10000) .get(); } catch (IOException e) { System.err.println("页面加载失败:" + e.getMessage()); return; }

这里加入了User-Agent和超时设置,提升成功率,避免被服务器识别为爬虫而拦截。

第二步:定位主体内容区域

打开浏览器开发者工具,观察新闻正文所在的容器。通常这类站点会用类似.zw_content这样的class命名:

Elements contentDivs = doc.getElementsByAttributeValue("class", "zw_content"); if (contentDivs.isEmpty()) { System.err.println("未找到内容区域!"); return; } Element contentDiv = contentDivs.first(); // 取第一个匹配块

也可以使用更现代的CSS选择器写法:

Element contentDiv = doc.selectFirst(".zw_content");

第三步:提取图片链接(含路径转换)

网页中的图片常使用相对路径(如/attach/123.jpg),直接保存会导致链接失效。Jsoup提供了便捷方式自动转为绝对URL:

Elements imgs = contentDiv.getElementsByTag("img"); String firstImg = imgs.size() > 0 ? imgs.get(0).attr("abs:src") : ""; String secondImg = imgs.size() > 1 ? imgs.get(1).attr("abs:src") : "";

关键在于使用abs:src而非src属性,Jsoup会根据原始请求URL自动补全域名。

第四步:提取并清洗文本内容

直接调用.text()可以提取所有子节点的文字,但往往包含干扰信息,比如“责任编辑:张三”、“作者:李四”等。

我们可以先过滤掉这些段落:

// 删除含有“责编”或“作者”的p标签 Elements creditParagraphs = contentDiv.select("p:contains(责编), p:contains(作者)"); creditParagraphs.remove(); String cleanText = contentDiv.text().replaceAll("\\s+", " ").trim();

这样得到的内容更加干净,适合后续入库或展示。

完整提取示例输出

正文:8月30日,日照市常务副市长王斌一行人来我校进行调研。校长戚万学... 图1:http://www.qfnu.edu.cn/attach/2016/09/02/123920.jpg 图2:

批量采集:首页资讯聚合抓取

单篇文章提取只是起点。实际业务中更多是批量抓取列表页上的多条新闻。

假设首页HTML结构如下:

<ul class="news-1-lists"> <li> <img src="/attach/xxx.jpg" title="新闻标题"> <a href="/html/xxyw/...html">新闻标题</a> <p>摘要内容...</p> </li> </ul>

我们的目标是从这个列表中提取每一条新闻的缩略图、标题和跳转链接。

实现代码

public static void batchExtractNews() { try { Document doc = Jsoup.connect("http://www.qfnu.edu.cn/") .userAgent("Mozilla/5.0 ...") .timeout(10000) .get(); Elements items = doc.select(".news-1-lists li"); // 直接选中每个条目 List<Map<String, String>> newsList = new ArrayList<>(); for (Element item : items) { String imgUrl = item.selectFirst("img").absUrl("src"); String title = item.selectFirst("img").attr("title"); String link = item.selectFirst("a").absUrl("href"); Map<String, String> newsItem = new HashMap<>(); newsItem.put("title", title); newsItem.put("image_url", imgUrl); newsItem.put("article_url", link); newsItem.put("timestamp", LocalDate.now().toString()); newsList.add(newsItem); System.out.printf("📌 %s\n🖼️ %s\n🔗 %s\n\n", title, imgUrl, link); } // 保存为JSON文件或其他存储形式 saveToJson(newsList, "data/news_data.json"); } catch (IOException e) { e.printStackTrace(); } }

存储建议目录结构

data/ ├── raw_html/ # 原始HTML备份 │ └── index_20251212.html ├── images/ # 下载的图片 │ └── 123920.jpg └── news_data.json # 结构化数据

对于动态加载的内容(如AJAX请求返回的JSON),可通过抓包分析接口地址,配合ignoreContentType(true)来解析非HTML响应:

String json = Jsoup.connect("https://api.example.com/news") .ignoreContentType(true) .execute() .body();

高阶技巧:健壮性与效率优化

1. 安全访问元素:防止越界异常

由于不同新闻的图片数量不一致,盲目调用get(1)极易引发IndexOutOfBoundsException。应封装安全获取方法:

private static String safeGetSrc(Elements imgs, int index) { return imgs.size() > index ? imgs.get(index).absUrl("src") : null; }

或者结合Optional风格处理:

Optional<Element> secondImgOpt = imgs.size() > 1 ? Optional.of(imgs.get(1)) : Optional.empty(); String secondUrl = secondImgOpt.map(img -> img.absUrl("src")).orElse(null);

2. CSS选择器进阶用法

语法含义示例
div.zw_contentclass为zw_content的divdoc.select("div.zw_content")
img[src]具有src属性的img标签doc.select("img[src]")
p spanp内的所有span子孙元素doc.select("p span")
li.news-onclass包含news-on的lidoc.select("li.news-on")

推荐优先使用类名选择器,性能高且稳定性好:

Elements paragraphs = doc.select(".zw_content p"); Elements images = doc.select(".zw_content img[src]");

3. 模拟浏览器行为防屏蔽

很多网站会对无头请求进行限制。合理设置请求头能显著提高成功率:

Connection conn = Jsoup.connect("http://www.qfnu.edu.cn/"); conn.userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36"); conn.header("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); conn.header("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8"); conn.header("Referer", "http://www.qfnu.edu.cn/"); conn.timeout(10000); Document doc = conn.get();

必要时还可携带Cookie维持会话状态(需自行提取)。

4. 数据清洗增强策略

除了移除“责编”信息外,还可以进一步清理:

String text = element.text(); // 移除括号内的无关信息(如[阅读次数:123]) text = text.replaceAll("\\[.*?\\]", "").trim(); // 替换全角空格、换行符为普通空格 text = text.replaceAll("[\\s\\u3000]+", " "); // 截断过长内容(适用于摘要生成) if (text.length() > 500) { text = text.substring(0, 500) + "..."; }

实战建议:如何设计可靠的爬虫逻辑

如何选择目标网页?

推荐场景
- 页面为静态HTML渲染,查看源码即可看到内容
- DOM结构清晰,有明确的class/id标识
- 使用UTF-8编码,中文显示正常
- 图文分离良好,便于独立提取

应避免的情况
- 完全依赖JavaScript动态渲染的单页应用(SPA)
- 需登录才能访问的内容(除非你能模拟登录)
- 启用验证码或反爬机制严格的站点
- HTML结构极度混乱、缺乏语义标签

提取策略设计原则

  • 先观察再编码:用浏览器“检查元素”功能确认关键节点的选择器是否唯一有效。
  • 由外向内逐层解析:先定位大容器(如.content-area),再在其内部查找具体字段。
  • 优先使用class/id定位:比遍历标签更高效稳定。
  • 打印中间结果调试:例如输出doc.select(".news-list").size()验证是否命中。

异常处理必须到位

所有网络操作都应包裹在try-catch中,并区分不同异常类型:

try { Document doc = Jsoup.connect(url).timeout(8000).get(); // 解析逻辑... } catch (SocketTimeoutException e) { System.err.println("请求超时:" + url); } catch (HttpStatusException e) { System.err.println("HTTP错误码:" + e.getStatusCode() + " -> " + url); } catch (ConnectException e) { System.err.println("连接被拒:" + url); } catch (IOException e) { System.err.println("IO异常:" + url + " | " + e.getMessage()); }

同时建议记录失败URL,便于后期重试或告警。

性能与资源控制

  • 首次验证阶段:快速使用.select()+.text()验证逻辑正确性。
  • 生产环境部署:加入Thread.sleep(1000~3000)延时,避免高频请求冲击服务器。
  • 追求速度时:可启用线程池并发抓取,但需控制并发数(建议≤5)。
  • 长期维护性:将CSS选择器写入配置文件或数据库,便于网页改版后快速调整。

常见问题与解决方案

抓不到内容怎么办?

  1. 确认URL是否可正常访问;
  2. 查看是否是JS动态加载——使用“查看页面源代码”功能,若源码中无内容则说明是前端渲染;
  3. 尝试添加User-Agent;
  4. 打印doc.html()确认是否成功获取HTML。

图片路径是相对路径怎么解决?

使用abs:src属性自动补全域名:

img.attr("abs:src")

同理适用于href等链接属性。

如何防止数组越界?

  • 提取前判断Elements.size()
  • 使用safeGet工具方法
  • 或用try-catch捕获IndexOutOfBoundsException

中文乱码怎么处理?

Jsoup通常能自动识别编码。若出现乱码,可尝试指定Content-Type:

Connection conn = Jsoup.connect(url); conn.header("Content-Type", "text/html; charset=utf-8"); Document doc = conn.get();

此外,保存文件时也需确保使用UTF-8编码。

被封IP了怎么办?

这是典型的反爬信号。应对措施包括:

  • 添加随机延迟:Thread.sleep(1000 + new Random().nextInt(2000))
  • 使用代理IP池(企业级方案)
  • 控制并发请求数量
  • 遵守robots.txt协议,尊重网站爬虫规则

性能参考与最佳实践

抓取性能估算

场景时间范围说明
单页解析100~500ms不含图片下载
含图片下载1~3秒/条受网络带宽影响大
批量10条3~8秒建议加1秒间隔保护服务器

内存占用一般在50~150MB之间,CPU主要用于I/O等待,整体资源消耗较低。

推荐工作流程

  1. 探索阶段
    - 手动分析DOM结构,复制片段本地测试
    - 编写最小可行脚本验证提取逻辑

  2. 开发阶段
    - 拆分为连接、解析、清洗、存储模块
    - 加入日志和异常捕获
    - 编写单元测试覆盖边界情况

  3. 部署阶段
    - 使用定时任务调度(如Quartz或Cron)
    - 实现增量抓取(基于时间戳或URL去重)
    - 添加失败重试机制与邮件通知

  4. 维护阶段
    - 定期检查选择器是否失效
    - 更新User-Agent和Headers
    - 监控成功率、响应时间和资源占用


写在最后

Jsoup的价值不仅在于其API的简洁易用,更在于它促使开发者回归“结构化思维”——即通过对HTML语义的理解来实现精准提取。这种轻量级、高可控的方式,在大多数非复杂场景下远胜于重型框架。

当然,面对现代Web的复杂性,我们也应理性看待其局限:对于完全依赖JavaScript的页面,仍需结合Puppeteer、Playwright或Selenium等工具。

但如果你的目标是稳定抓取传统新闻站、教育机构官网或政府公告类内容,Jsoup依然是那个值得信赖的老兵。合理运用本文介绍的技巧,你完全可以构建出一个健壮、可维护的信息采集系统。

“好的爬虫不是跑得最快的那个,而是活得最久的那个。”

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询