为什么选择 Spring Boot 搭建爬虫系统
数据采集在舆情分析、价格监控、内容聚合等业务场景中需求量很大,而Java生态在这方面有着天然的工程化优势。Spring Boot作为目前Java领域最主流的快速开发框架,内置了定时任务、异步处理、依赖注入等能力,非常适合承载爬虫这类需要长期稳定运行的采集任务。相比Python的Scrapy,Java方案在与现有业务系统整合、团队技术栈统一方面更有优势。
用Spring Boot做爬虫的另一个好处是生命周期管理非常省心。爬虫任务通常需要定时启动、优雅停止、失败重启,这些都可以借助Spring的@Scheduled注解和容器管理能力轻松实现。同时,采集到的数据往往需要落库或者推送到消息队列,Spring Boot对MyBatis、JPA、Kafka、Redis等组件的整合能力让整条数据链路的搭建变得十分顺畅。

在框架选型上,轻量级场景可以直接使用Jsoup做页面抓取与解析,它API简洁、上手极快;需要调度、多线程、流水线处理等完整爬虫能力时,则推荐WebMagic,它参照Scrapy的设计思想,提供了Downloader、PageProcessor、Pipeline等清晰分层。本文会以Jsoup加WebMagic结合的方式,演示一套完整的采集与清洗方案。
整合 Jsoup 完成页面抓取与解析
Jsoup的核心能力有两块:一是通过URL发起HTTP请求获取HTML文档,二是用类似CSS选择器的语法从文档中提取数据。先在pom.xml中引入依赖:
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.17.2</version>
</dependency>下面是一个典型的抓取示例,模拟浏览器请求头以降低被识别为爬虫的概率,并通过选择器批量提取列表页的文章标题与链接:
public List<Article> fetchList(String url) throws IOException {
Document doc = Jsoup.connect(url)
.userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64)")
.timeout(10000)
.header("Accept-Language", "zh-CN,zh;q=0.9")
.get();
List<Article> list = new ArrayList<>();
Elements items = doc.select("div.news-list li");
for (Element item : items) {
Article a = new Article();
a.setTitle(item.select("a.title").text());
a.setUrl(item.select("a.title").attr("abs:href"));
a.setPublishTime(item.select("span.time").text());
list.add(a);
}
return list;
}使用Jsoup时有几个细节值得注意。首先,abs:href会自动把相对路径补全为绝对地址,省去手动拼接的麻烦。其次,text()方法会清除标签只留纯文本,比html()更安全,避免把脚本片段带入数据。另外,目标站点的DOM结构随时可能改版,建议把选择器配置化,放到数据库或配置文件中,改版时无需重新发版。
对于列表页加详情页的两级采集模式,正确的做法是先抓列表页拿到所有详情链接,再逐个请求详情页提取正文。控制好并发和间隔,避免短时间高频请求导致IP被封。
用 WebMagic 构建可调度的爬虫流水线
当采集需求变复杂时,Jsoup的脚本式写法会显得力不从心。WebMagic提供了完整的爬虫框架抽象,包括页面下载、解析、去重、管道处理等环节。引入依赖:
<dependency>
<groupId>us.codecraft</groupId>
<artifactId>webmagic-core</artifactId>
<version>0.7.3</version>
</dependency>
<dependency>
<groupId>us.codecraft</groupId>
<artifactId>webmagic-extension</artifactId>
<version>0.7.3</version>
</dependency>实现一个PageProcessor来定义解析逻辑,再配合Pipeline把数据交给Spring管理的Service做入库处理:
@Component
public class NewsProcessor implements PageProcessor {
private Site site = Site.me()
.setCharset("UTF-8")
.setRetryTimes(3)
.setSleepTime(2000)
.setUserAgent("Mozilla/5.0");
@Override
public void process(Page page) {
// 列表页:提取详情链接并加入待抓取队列
page.addTargetRequests(
page.getHtml().xpath("//div[@class='news-list']").links()
.regex("/detail/\\d+").all());
// 详情页:提取正文数据
if (page.getUrl().regex("/detail/\\d+").match()) {
page.putField("title", page.getHtml().xpath("//h1/text()").get());
page.putField("content", page.getHtml().xpath("//div[@class='content']/tidyText()").get());
} else {
page.setSkip(true);
}
}
@Override
public Site getSite() {
return site;
}
}启动爬虫时可以结合Spring的定时任务,每天凌晨执行一次全量采集。通过Spider.create()设置线程数与Pipeline,并调用start()异步运行:
@Component
public class SpiderRunner {
@Autowired
private NewsProcessor newsProcessor;
@Autowired
private DataBasePipeline dataBasePipeline;
@Scheduled(cron = "0 0 2 * * ?")
public void runSpider() {
Spider.create(newsProcessor)
.addUrl("https://ipipp.com/news/list")
.addPipeline(dataBasePipeline)
.thread(5)
.run();
}
}WebMagic的setSleepTime可以控制两次请求之间的间隔,setRetryTimes实现失败重试,这两个参数是保证爬虫稳定性和礼貌性的关键配置。默认的去重基于HashSet,大规模场景建议替换为基于Redis的布隆过滤器去重器,能显著降低内存占用。
数据清洗策略与入库落地方案
采集到的原始数据通常充满噪音:正文里混杂广告文字、标题带有全角空格、时间格式五花八门、还有大量重复条目。数据清洗的目标就是把脏数据规范化,一般包括去重、格式化、缺失处理、内容过滤四个环节。
去重方面,除了URL级别的去重,还需要对内容做相似度判断。简单场景可以直接对标题加正文取MD5做唯一索引,配合数据库的INSERT IGNORE或者先查后插的方式实现。格式化方面,时间字段经常出现2024-5-6、06月07日等不同形态,建议用多种正则逐一匹配后统一转为标准格式:
public class DataCleaner {
private static final Pattern[] TIME_PATTERNS = {
Pattern.compile("(\\d{4})-(\\d{1,2})-(\\d{1,2})"),
Pattern.compile("(\\d{4})年(\\d{1,2})月(\\d{1,2})日")
};
public String cleanText(String raw) {
if (raw == null || raw.isBlank()) {
return null; // 空数据直接丢弃
}
return raw.replaceAll("\\s+", " ") // 压缩连续空白
.replaceAll("[\\u200b\\ufeff]", "") // 去除不可见字符
.replaceAll("(本文来源|责任编辑).*$", "") // 去除尾部广告文案
.trim();
}
public LocalDateTime parseTime(String timeStr) {
for (Pattern p : TIME_PATTERNS) {
Matcher m = p.matcher(timeStr);
if (m.find()) {
return LocalDateTime.of(Integer.parseInt(m.group(1)),
Integer.parseInt(m.group(2)),
Integer.parseInt(m.group(3)), 0, 0);
}
}
return null;
}
}清洗完成后通过Pipeline统一入库。Spring Boot配合MyBatis-Plus做批量插入效率很高,同时建议给关键字段建立索引,并在日志中记录每轮采集的新增条数与丢弃条数,方便后续监控数据质量。
@Component
public class DataBasePipeline implements Pipeline {
@Autowired
private ArticleService articleService;
@Autowired
private DataCleaner dataCleaner;
@Override
public void process(ResultItems items) {
String title = dataCleaner.cleanText(items.get("title"));
String content = dataCleaner.cleanText(items.get("content"));
if (title == null || content == null) {
return; // 清洗后无效的数据直接过滤
}
Article article = new Article();
article.setTitle(title);
article.setContent(content);
article.setMd5(DigestUtils.md5Hex(title + content));
articleService.saveIfAbsent(article);
}
}整体来看,Spring Boot整合Jsoup与WebMagic可以覆盖从轻量抓取到复杂采集的各种需求,再叠加一套规范化的清洗流程,就能构建出稳定、可维护的数据采集系统。实际落地时务必关注目标站点的robots协议与访问频率限制,合理设置抓取间隔,才能让系统长期平稳运行。
Spring Boot爬虫框架数据清洗修改时间:2026-09-02 05:03:22