☰
SpringBoot新闻聚合平台实战:Scrapy爬虫与Java后端整合
2026/10/9 2:40:26 网站建设 项目流程

简介:本资源是一套基于JAVA与SpringBoot的在线新闻聚合平台完整项目源码,面向计算机相关专业的毕业设计、课程作业开发者,以及希望实践网页开发与数据抓取技术的学习者。项目通过爬虫定期抓取各大新闻网站内容,聚合标题、摘要、图片与链接,并借助数据库完成存储与检索,前端采用Vue构建响应式界面,后端由SpringBoot处理请求与业务逻辑。压缩包共466个文件,约13.77MB,包含99个java后端源码、46个vue前端组件、34个js脚本、9个py爬虫程序,以及xml配置、sql建表脚本、yml与properties配置文件、bat与cmd启动脚本和svg、jpg等静态资源,覆盖从抓取、存储到展示的完整链路。目前已有86人学习下载。读者可据此掌握爬虫策略调优、数据库索引优化、响应式页面设计及SQL注入、XSS防护等要点,并参考需求分析、系统设计与部署上线的工程流程,快速搭建可运行的新闻聚合系统。

1. 从一份带.bat的 Java 新闻聚合项目说起:它到底能跑起来吗

第一次拿到这个包,我盯着根目录那几个文件看了半天:1-install.bat、2-run.bat、3-build.bat,旁边还躺着scrapy.cfg、mvnw.cmd、main.js.bak和一个app.97a85389.css。这套组合挺有意思——一个号称用 SpringBoot 做在线新闻聚合平台的 Java 项目,却把 Python 爬虫的配置文件和前端打包产物混在同一层目录里。对正在找毕业设计或课程作业参考的人来说,这种"混搭"恰恰是最真实的状态:它不是实验室里精修过的样板,而是一个真被跑过、被改过、留下过痕迹的工程。

这个资源解决的核心问题很明确:给你一套能本地启动的新闻聚合平台骨架,前端用 SpringBoot 渲染响应式页面,后端负责调度抓取、清洗、入库和展示。适合三类人——要交毕业设计但不想从零搭架子的、想学 Java 后端怎么和爬虫协作的、以及想拆一个完整数据流看它怎么串起来的人。下面我按"能不能跑、怎么跑、哪里会翻车"的顺序拆一遍。

2. 环境与启动链路:三个 bat 脚本背后的真实依赖

2.1 为什么是 SpringBoot + Scrapy 这种组合

先讲选型。新闻聚合这件事,本质是"抓取—清洗—存储—展示"四段流水线。展示层用 SpringBoot 是常见做法,因为它把 Tomcat 内嵌了,mvnw一跑就能起服务,不用单独装容器,对课程作业这种交付场景特别友好。抓取层用 Scrapy 而不是纯 Java 的 Jsoup 或 WebMagic,理由也很实际:Scrapy 自带调度器、去重队列和中间件机制,写规则比手搓 HttpClient 省事得多,scrapy.cfg就是它的项目入口配置。

两者怎么协作?常见做法是 Scrapy 抓完把结构化数据落到数据库或中间文件,SpringBoot 只读不抓,通过定时任务触发或直接读结果表。这样职责清晰,爬虫崩了不影响页面展示,页面重启也不用重抓。你看到根目录同时有scrapy.cfg和mvnw.cmd,基本就是这个架构的痕迹。

提示:如果你的环境里 Python 和 JDK 版本对不上,先别急着改代码,八成是环境问题,不是逻辑问题。

2.2 三个 bat 脚本的执行顺序与含义

Windows 下这套脚本是给不熟命令行的人准备的快捷入口,顺序不能乱:

# 1-install.bat —— 首次运行,装依赖 # 通常包含:mvnw 拉取 Maven 依赖 + pip install scrapy 相关包 # 只跑一次,除非你改了 pom.xml 或 requirements # 2-run.bat —— 日常启动,起 SpringBoot 服务 # 内部一般是 mvnw spring-boot:run 或 java -jar target/xxx.jar # 3-build.bat —— 打包,产出可部署的 jar # 对应 mvnw clean package,产物在 target 目录

逻辑说明:1-install负责把两套依赖(Java 的 Maven 依赖、Python 的 Scrapy 依赖)都拉齐,这是最容易失败的一步,因为要联网且对镜像源敏感。2-run是开发态启动,改代码后重启即可。3-build是交付态,打出 jar 后可以脱离 IDE 运行。

参数说明:如果你机器上有多个 JDK,mvnw.cmd会读JAVA_HOME,务必确认它指向的是项目要求的版本(一般是 JDK 8 或 11,SpringBoot 2.x 居多)。Python 侧则要确认scrapy命令在 PATH 里,否则1-install.bat里调 pip 的那段会静默失败。

2.3 手动跑通的替代路径

bat 脚本在 Mac 或 Linux 上没用,我一般会手动走一遍,反而更清楚每一步在干嘛:

# Java 侧:用自带的 mvnw,避免本机 Maven 版本冲突 ./mvnw clean install -DskipTests # 启动服务,指定端口防止和本机其他服务撞车 ./mvnw spring-boot:run -Dspring-boot.run.arguments=--server.port=8081 # Python 侧:进入爬虫目录,确认 scrapy.cfg 同级执行 scrapy list # 先看有哪些 spider 被识别到 scrapy crawl news # news 换成你实际的 spider 名

逻辑说明:./mvnw clean install会编译并安装到本地仓库,-DskipTests跳过测试加快速度,课程作业阶段测试往往不全,跳过能省时间。scrapy list是个被低估的命令,它能告诉你 Scrapy 到底认出了几个爬虫,如果输出为空,说明scrapy.cfg里的[settings]指向的模块路径写错了。

参数说明:--server.port是 SpringBoot 的标准覆盖参数,比改application.yml更灵活,适合临时调试。scrapy crawl后面的名字必须和 spider 类里的name属性完全一致,大小写敏感。

3. 数据抓取与入库:从网页到数据库的完整链路

3.1 Scrapy 抓取规则怎么写才不翻车

新闻站点的结构差异极大,所以爬虫的通用性永远是伪命题。这个项目里scrapy.cfg只是入口,真正的规则在 spider 文件里。一个能用的新闻 spider 通常长这样:

import scrapy class NewsSpider(scrapy.Spider): name = "news" # 起始 URL 列表,实际项目里常从配置或数据库读 start_urls = ["https://example-news-site.com/list"] def parse(self, response): # 列表页:提取每条新闻的详情链接 for link in response.css("a.news-title::attr(href)").getall(): yield response.follow(link, self.parse_detail) # 翻页:找下一页按钮,递归抓取 next_page = response.css("a.next::attr(href)").get() if next_page: yield response.follow(next_page, self.parse) def parse_detail(self, response): # 详情页:抽取标题、摘要、图片、正文 yield { "title": response.css("h1.title::text").get(default="").strip(), "summary": response.css("div.summary::text").get(default="").strip(), "image": response.css("img.cover::attr(src)").get(), "url": response.url, }

逻辑说明:parse处理列表页,负责发现详情链接和翻页;parse_detail处理详情页,产出结构化字典。response.follow会自动补全相对链接,比手动拼 URL 稳。get(default="")是关键防御,字段缺失时返回空串而不是 None,避免入库时报错。

参数说明:start_urls建议改成从外部配置读,硬编码在代码里后期维护很痛苦。::text和::attr()是 CSS 选择器的两种取值方式,前者取文本,后者取属性。如果目标站是 JS 渲染的,这套 CSS 选择器会抓不到内容,得换 Selenium 或 Playwright,这是新手最容易踩的坑。

3.2 请求频率与 robots.txt 的边界

抓取策略里最容易被忽视的是频率控制。默认 Scrapy 的并发不低,对着小站点猛抓,轻则被封 IP,重则给对方服务器造成压力。常见做法是在settings.py里加几行:

# 每次请求间隔,单位秒,给目标站喘息空间 DOWNLOAD_DELAY = 1.5 # 同一域名并发数,调低更稳 CONCURRENT_REQUESTS_PER_DOMAIN = 2 # 遵守 robots.txt,这是底线不是可选项 ROBOTSTXT_OBEY = True # 自动限速,根据响应时间动态调整 AUTOTHROTTLE_ENABLED = True AUTOTHROTTLE_TARGET_CONCURRENCY = 1.0

逻辑说明:DOWNLOAD_DELAY是最直接的限速手段,1.5 秒意味着每分钟最多 40 个请求,对大多数新闻站是安全区间。AUTOTHROTTLE更智能,它会根据服务器响应延迟自动放慢,服务器慢它就等,服务器快它也不冒进。

参数说明:ROBOTSTXT_OBEY = True是合规底线,别为了多抓几条新闻把它关掉。CONCURRENT_REQUESTS_PER_DOMAIN设成 2 是保守值,如果你抓的是自己的测试站可以调高,抓别人的站就老实点。

3.3 入库:MySQL 表结构与去重逻辑

抓下来的数据要落库,表结构设计直接决定后面查询快不快。一个够用的新闻表大致这样:

CREATE TABLE news ( id BIGINT PRIMARY KEY AUTO_INCREMENT, title VARCHAR(255) NOT NULL, summary TEXT, image_url VARCHAR(500), source_url VARCHAR(500) NOT NULL, source_name VARCHAR(100), publish_time DATETIME, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, -- 用 URL 做唯一索引,天然去重 UNIQUE KEY uk_source_url (source_url), -- 按发布时间倒序查列表,这个索引必须有 KEY idx_publish_time (publish_time) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

逻辑说明:uk_source_url唯一索引是去重的核心,同一条新闻重复抓取时用INSERT ... ON DUPLICATE KEY UPDATE或INSERT IGNORE就能避免脏数据。idx_publish_time服务于首页按时间倒序的查询,没有它数据量上万后列表页会明显变慢。

参数说明:utf8mb4而不是utf8,因为新闻标题里可能有 emoji 或生僻字,utf8存不下会报错。VARCHAR(500)给 URL 留足空间,有些带参数的链接很长。summary用TEXT因为摘要长度不定。

注意:如果项目用的是 MyBatis-Plus,实体类字段和表字段的映射要确认驼峰转下划线是否开启,否则sourceUrl对不上source_url,查询会返回空。

4. 前端展示与接口联调:SpringBoot 怎么把数据喂给页面

4.1 控制器与分页接口

后端对外的核心接口就是新闻列表和详情。用 SpringBoot 写一个分页查询:

@RestController @RequestMapping("/api/news") public class NewsController { @Autowired private NewsService newsService; // 分页查询,page 从 1 开始,size 默认 10 @GetMapping("/list") public Result list(@RequestParam(defaultValue = "1") int page, @RequestParam(defaultValue = "10") int size) { // 用 MyBatis-Plus 的 Page 对象做物理分页 Page<News> p = new Page<>(page, size); newsService.page(p, new QueryWrapper<News>() .orderByDesc("publish_time")); return Result.ok(p.getRecords(), p.getTotal()); } }

逻辑说明:@RequestParam的defaultValue保证前端不传参也能用。orderByDesc("publish_time")配合前面的索引,查询效率有保障。返回结构里带上total是为了前端分页组件能算总页数。

参数说明:page从 1 开始是 MyBatis-Plus 的约定,别传 0,否则会算出负数偏移。size建议设上限,比如超过 50 就强制截断,防止有人传size=100000把数据库拖垮。

4.2 响应式页面与静态资源

根目录那个app.97a85389.css是前端构建产物,带哈希后缀说明用了打包工具(Vite 或 Webpack)。SpringBoot 默认从src/main/resources/static提供静态资源,所以这些文件要放在正确位置才能被访问到。响应式布局靠的是 CSS 媒体查询或框架栅格,常见做法是用 Bootstrap 或自己写 flex 布局。

main.js.bak这个备份文件值得说一句:它说明有人改过前端逻辑又留了后路。如果你要改交互,先看main.js而不是.bak,.bak是历史版本,可能和当前 CSS 哈希对不上。

4.3 接口联调时的跨域问题

前后端分离时,浏览器会因为同源策略拦截请求。开发阶段常见做法是加一个全局跨域配置:

@Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/api/**") .allowedOriginPatterns("*") .allowedMethods("GET", "POST") .allowCredentials(true); } }

逻辑说明:addMapping("/api/**")只对接口路径放开,静态资源不受影响。allowedOriginPatterns("*")比allowedOrigins("*")更安全,后者在带凭证时会被浏览器拒绝。

参数说明:allowCredentials(true)允许携带 Cookie,如果不用 Session 可以设 false。生产环境务必把*换成具体域名,否则等于没做跨域限制。

5. 避坑与排查:那些让我重跑三遍的坑

5.1 启动报OutOfMemoryError或堆内存不足

现象:2-run.bat跑到一半抛java.lang.OutOfMemoryError: Java heap space,服务起不来。

原因:默认 JVM 堆太小,而 SpringBoot 加 MyBatis 加连接池本身就吃内存,再加上如果爬虫和 Web 在同一进程里跑,内存更紧张。

解决:启动时显式指定堆大小,java -Xms512m -Xmx2048m -jar xxx.jar。如果用的是mvnw spring-boot:run,加-Dspring-boot.run.jvmArguments="-Xmx2048m"。别一上来就设 8G,先看实际占用再调。

5.2 爬虫抓回来全是空字段

现象:数据库里title有值,但summary、image全是空。

原因:目标站改版了,CSS 选择器失效;或者详情页是 JS 动态渲染,Scrapy 拿到的是空壳 HTML。

解决:先用scrapy shell "详情页URL"进去手动试选择器,response.css("div.summary")看返回什么。如果是 JS 渲染,换 Selenium 或找站点的 JSON 接口直接请求。别硬啃 HTML。

5.3 中文乱码

现象:页面显示问号或方块,数据库里存进去也是乱码。

原因:三个环节任一没对齐都会乱——数据库字符集、连接串编码、页面 meta 声明。

解决:数据库建库时用utf8mb4,JDBC 连接串加?useUnicode=true&characterEncoding=utf8,HTML 头部确认<meta charset="UTF-8">。三处都对了才不乱。

5.4 端口被占用导致启动失败

现象:Port 8080 was already in use。

原因:本机有其他服务占了 8080,或者上一次的进程没退干净。

解决:netstat -ano | findstr 8080找到 PID,taskkill /PID xxx /F干掉。或者直接换端口启动,--server.port=8081,省事。

5.5 Maven 依赖下载卡住

现象:1-install.bat卡在下载依赖,半天不动。

原因:默认走中央仓库,网络不稳时容易超时。

解决:在settings.xml里配国内镜像源,或者用mvnw -o离线模式(前提是依赖已经下过)。别反复重跑,先确认是网络问题还是依赖冲突。

6. 进阶技巧:把抓取和展示解耦成定时任务

跑通之后,我建议做一件事:别让爬虫和 Web 服务绑在一起。常见做法是用 Spring 的@Scheduled定时触发抓取,或者干脆把 Scrapy 做成独立进程,通过命令行调用。

@Component public class NewsFetchTask { // 每 30 分钟触发一次,cron 表达式按需改 @Scheduled(cron = "0 0/30 * * * ?") public void fetch() { try { // 调用外部 Python 脚本,阻塞等待完成 Process p = Runtime.getRuntime() .exec("scrapy crawl news"); int code = p.waitFor(); if (code != 0) { // 非零退出码说明抓取异常,记日志 System.err.println("抓取任务失败,退出码:" + code); } } catch (Exception e) { e.printStackTrace(); } } }

逻辑说明:@Scheduled的 cron 表达式0 0/30 * * * ?表示每 30 分钟执行一次。Runtime.exec起一个子进程跑 Scrapy,waitFor阻塞到它结束。退出码非零时记日志,方便排查。

参数说明:cron 里?表示不指定,用在"日"或"周"字段避免冲突。waitFor会阻塞当前线程,如果抓取耗时长,建议放到独立线程池里,别堵住调度线程。

验证方法:改完 cron 后,把时间调成每分钟一次,观察日志里是否按时触发、退出码是否为 0、数据库记录数是否增长。确认无误再改回 30 分钟。

从那以后我每次拿到这种带 bat 脚本的项目,都强制先手动走一遍mvnw和scrapy命令,确认每一步的报错信息,再回头用脚本——因为脚本会把错误吞掉,而手动跑能让你看清到底哪一环断了。希望帮到你。

本文还有配套的精品资源,点击获取

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

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

立即咨询