
1. 立项分析为什么新闻聚合平台是毕设里“性价比”最高的题目之一如果你正在纠结毕业设计选题又恰好想用Spring Boot做开发那我强烈建议你认真看看“在线新闻聚合平台”这类题目。它不像纯管理系统那样容易写成CRUD堆砌也不像纯算法课题那样容易陷进理论出不了成果而是刚好卡在“工程应用”和“数据价值”中间能展示的东西非常多。先说这个题目到底在做什么系统定时从多个新闻网站抓取内容经过清洗、去重、分类后存到数据库再通过Spring Boot提供REST接口和Web页面让用户按类别浏览新闻、搜索关键词、查看文章详情。听起来简单但你会发现它天然串联了后端开发、数据采集、数据清洗、任务调度、全文检索这几个完全不同的技术点每一个都能在答辩时单独拿出来讲一段。我见过太多同学选“XX管理系统”然后被评委追问“你的系统有什么实际价值”而新闻聚合平台几乎不会被问倒因为它就是一个有明确应用场景的实用工具背后还站着一个完整的技术栈。更关键的是这个题目对外行人也很好解释——就是做一个自动帮你收集新闻的网站你自己不用一个个打开网站去看。再说说技术选型的合理性。用Spring Boot做主框架一是因为就业市场认这个二是因为它整合第三方组件的成本极低。爬虫部分你既可以用Java原生的Jsoup也可以用Python的Scrapy或Requests做数据源端采集然后通过接口或文件把数据交给Spring Boot。如果你会一点Python甚至可以做成混合架构——Python负责抓取Java负责业务两边通过HTTP或消息队列通信。从毕设角度讲这个题目最大的优势是数据源无限。新闻网站、博客、RSS源、甚至社交平台的热榜只要目标网页结构能被解析都能成为你的数据来源。这意味着你的系统演示时想展示多少条新闻都行数据库里塞几千上万条记录完全不费劲功能演示效果直接拉满。不过有一点要提醒如果指导老师要求的是“大数据”方向你需要在设计里体现一点批量处理和数据分析的味道。比如对抓取到的新闻做词频统计、按时间段生成趋势图、根据关键词聚类这些都是在“聚合”基础上往上加分的思路。原标题里写着“大数据毕设”所以我建议你在系统里至少留一个“数据分析/统计可视化”模块哪怕是简单的ECharts柱状图也能让评委觉得你的系统有数据思维。2. 整体架构与模块划分核心就是一条“从抓取到展示”的数据流水线这个项目的架构不复杂但一定要想清楚再动手。我见过有人一上来就写代码结果爬到一半发现数据没地方放或者接口和前端对不上返工好几次。先花一个小时把模块和数据结构理清楚后面能省出大把时间。2.1 系统分层表现层、业务层、数据层的标准划分后端还是经典的MVC三层但我会建议你在业务层里单独拆出一个CrawlerService和普通的新闻查询业务分开。原因很简单爬虫逻辑和普通业务逻辑的复杂度不是一个量级混在一起的话爬虫某个数据源挂了可能连正常的新闻浏览接口都被影响。拆开后哪怕爬虫模块整个崩了用户照样能看数据库里已有的新闻。从数据流向来看整条流水线长这样目标站点 - 抓取器(定时触发) - 解析清洗 - 去重 - 结构化存储(Mysql) - Spring Boot业务接口 - Web前端展示 - 用户浏览/搜索/分类这里面最容易被忽略的是“清洗”这一步。你从网上抓回来的HTML里全是标签、广告位、乱码直接存库既不美观也没法做后续检索。所以解析器里必须要有正文提取逻辑把p标签里的文本抠出来去掉脚本和样式。Java里用Jsoup的select()方法选节点非常方便比如抓CSDN的文章正文直接doc.select(div#content_views)就完事。Python的BeautifulSoup同理soup.select(.article-content p)能得到所有正文段落。2.2 功能模块清单哪些是核心、哪些是加分项我把这个系统的功能模块按优先级排个序你照着做就能保证主体功能完整核心功能必须做新闻列表展示按时间倒序、分页展示分类浏览国际、国内、科技、娱乐、体育等分组新闻详情页展示标题、来源、发布时间、正文内容关键词搜索通过标题或正文模糊搜索后台抓取管理查看最近几次抓取记录、数据量统计加分功能时间充裕再做用户收藏与浏览历史新闻来源管理增删改查要抓取的目标站点热词词云图统计近期高频词汇数据导出CSV或Excel方便展示“大数据”量级基于用户浏览记录做简单的推荐你去看市面上大多数同类毕设核心功能基本就是这七项。只要这些都跑通答辩时把系统操作一遍这段就稳了。2.3 Spring Boot在系统里的位置粘合一切的核心骨架Spring Boot在这个项目里扮演的其实是“中央调度者”的角色。它不负责真正复杂的数据抓取算法而是把数据抓取、数据清洗、数据存储和前端展示串成一个完整的闭环。它的优势在于三点第一自动配置让数据库操作零门槛。引入spring-boot-starter-jdbc或MyBatis-Plus配置一个数据源DAO层的增删改查就齐了。这对毕设项目来说效率是最重要的。第二定时任务内置。Spring Boot里加一个EnableScheduling然后在爬虫Service上用Scheduled(cron 0 0 */1 * * *)每小时自动抓一轮完全不用引入额外的调度框架。我后面会单独讲定时任务这节的细节。第三接口开发快。通过RestController加上Spring Data JPA或MyBatis-Plus简单几个方法就能把数据库表暴露成API前端对接时debug也方便。3. 爬虫核心实现让数据每日自动“流进”你的数据库爬虫是这项目的灵魂。很多同学觉得爬虫很难其实把它拆开看就是“下载网页、解析内容、存储数据”三个动作真正有技术含量的是如何把这些动作做得稳定、高效、不出错。下面我按实际开发顺序把整个爬虫模块的每个关键点展开说说。3.1 定时任务调度每小时/每天自动触发抓取定时任务是整个数据流水线的“节拍器”。Spring Boot内置的Scheduled注解是最简单的方案但别小看它用好了完全够毕设用。我建议的配置方式Component public class NewsCrawlTask { Autowired private CrawlService crawlService; // 每小时整点执行一次抓取 Scheduled(cron 0 0 0/1 * * *) public void hourlyCrawl() { crawlService.crawlAllSources(); } // 每天凌晨4点触发一次增量抓取 Scheduled(cron 0 0 4 * * *) public void dailyIncrementalCrawl() { crawlService.crawlIncrementalSources(); } }cron表达式里六个字段依次是秒、分、时、日、月、星期。0 0 0/1 * * *表示每小时的第0分第0秒执行0 0 4 * * *表示每天凌晨4点执行。这里有个实际经验抓取时间最好避开整点高峰很多新闻网站整点会更新数据同一时刻涌来的爬虫请求容易被封。所以我一般设置在每小时的第10分钟或第20分钟比如0 10 * * * *命中率更稳定。除了定时触发我还建议加一个手动触发接口GET /admin/crawl/now。这样你演示的时候不用干等定时任务想爬随时爬评委想看效果也方便。手动触发和定时触发走同一个Service方法不重复写逻辑。3.2 爬虫框架选择用Python做采集还是用Java原生Jsoup这是所有做这个题目的同学都会纠结的问题。我从实际效果出发说结论如果你熟悉Python用Python做采集端、Spring Boot做后端是最舒服的组合如果不想搞混合架构Java加Jsoup也完全够用。Python这边的优势是生态好。Scrapy是成熟的爬虫框架requests加BeautifulSoup的搭配上手也快IP代理池、请求重试、UA伪装这些反爬对策都有现成库。我在实际项目里是用Python写好抓取脚本把解析后的结构化JSON通过Spring Boot的POST /api/crawl/collect接口丢给后端再由后端统一入库。这种设计的好处是Python代码出问题不影响Java服务稳定运行两边解耦。如果坚持全JavaJsoup足够用了代码也简洁。抓取一个新闻列表页并解析它的核心代码长这样public ListNews parseNewsList(String url, String listSelector, String titleSelector, String linkSelector) { ListNews result new ArrayList(); try { // 设置超时和UA避免被服务器拒绝 Connection conn Jsoup.connect(url) .timeout(10000) .userAgent(Mozilla/5.0 (Windows NT 10.0; Win64; x64)); Document doc conn.get(); Elements items doc.select(listSelector); for (Element item : items) { String title item.select(titleSelector).text(); String link item.select(linkSelector).absUrl(href); if (title.length() 0 link.length() 0) { News news new News(); news.setTitle(title); news.setSourceUrl(link); result.add(news); } } } catch (IOException e) { log.error(抓取失败: {}, url, e); } return result; }我把每个数据源的抓取规则URL、列表选择器、标题选择器、链接选择器配置成一个模板对象存到数据库里。这样以后新增一个数据源不用改代码往配置表插一条记录就行系统设计上立刻显得灵活了不少。3.3 正文抽取与数据清洗把“网页”变成“干净新闻”列表页只是拿到标题和链接要形成每篇新闻的完整记录还得写一个“详情页解析器”。这是整个爬虫里比较麻烦的部分因为不同网站的正文HTML结构差异很大没有一个万能选择器能通吃全部网站。我的做法是给每个数据源配置一个“正文选择器”字段。比如新浪新闻的正文在div#artibody搜狐新闻在div.text网易新闻在div.post_body。站在毕设角度我强烈建议你至少配3个不同的数据源每个源的正文解析规则独立维护这既合理又能在答辩时展示“可扩展性”。正文抠出来后还有一道工序清理噪声。Jsoup提供了text()方法直接拿纯文本但拿到手后可能含大量空格、换行、广告词比如“本文来源XXX 责任编辑XXX”。你可以在存储前用正则把这类“版权尾巴”去掉public String cleanContent(String rawContent) { // 去掉来源、责任编辑、广告推广等不相关词句 return rawContent.replaceAll((本文来源|责任编辑|免责声明|广告).*?($|。), ); }这里要注意正则要小范围应用免得误删正文。更稳妥的做法是只替换已知的刺头模式宁可多留着也别把新闻内容给删坏了。实际做下来你会发现清洗文本的工作量远大于抓取本身但这步做得好不好直接决定新闻详情页看起来像不像一个真正可用的平台。3.4 编码问题与超时异常爬虫最磨人的隐性坑爬虫项目里十个报错八个是编码问题。国内老一些的网站还在用GBK或GB2312编码你用默认的UTF-8解析出来的就是满屏乱码。Jsoup在这点做得比较好如果服务端响应头里标注了charsetgbk它会自动转换。但如果Response头没带编码信息就需要你手动指定一下// 强制指定文档编码为GBK Document doc Jsoup.connect(url).parser(Parser.htmlParser()).get(); doc.outputSettings().charset(GBK);更稳妥的做法是直接用String手动解码响应字节byte[] bodyBytes Jsoup.connect(url).ignoreContentType(true).execute().bodyAsBytes(); String html new String(bodyBytes, Charset.forName(GBK));另一种高发异常是超时中断。现在很多网站会主动断开非浏览器发起的连接Jsoup默认10秒超时如果网络波动一下就报SocketTimeoutException。你可以在连接配置里加上timeout(15000)然后配合try-catch记录失败日志不要因为一个源挂了就让整个抓取任务终止。每个源单独try-catch即使一个源报错其余源照常抓才算是个能落地的爬虫模块。4. “聚合平台”里的技术含量关键词分词、去重与全文搜索新闻能抓下来只是第一步聚合之后怎么让用户舒服地找到想看的内容才是平台和普通爬虫脚本的区别。这一章讲三个容易出彩的技术细节也是答辩时容易被评委追问的地方。4.1 中文分词与关键词提取HanLP在Spring Boot里的正确使用方式如果你希望平台能按关键词给新闻打标签或者做一个词云图就需要中文分词。Java生态里我推荐HanLP它轻量、准确率不错而且和Spring Boot整合非常顺。引入依赖dependency groupIdcom.hankcs/groupId artifactIdhanlp/artifactId versionportable-1.7.8/version /dependency然后就可以在Service里做关键词提取// 提取新闻标题和正文的高频关键词 ListString keywords HanLP.extractKeyword(title content, 5);这个方法返回权重最高的前N个词。你可以把关键词存到新闻表里一个新字段keywords用户点标签就能看到同类新闻用户体验直接上一个档次。这个功能成本不高但“技术含量”的观感很足。不过有一点要注意HanLP的portable版本内置的是小模型对新词和网络热词识别一般它可能把“拜登”拆成“拜”和“登”把“小红书”识别成“小红”和“书”。如果你只用来做词云图没太大影响但如果你想用关键词做精确推荐最好加载自定义词典把地名、人名、品牌名手动加进去。4.2 相似新闻去重用文本指纹或SimHash避免整页重复新闻网站之间转载很普遍同一篇新闻可能在好几个源里出现如果不去重用户刷列表时会看到好几条一模一样的标题平台就露馅了。去重最简单的方案是标题精确匹配加个唯一索引ALTER TABLE news ADD UNIQUE KEY uk_title (title);但这个方案有个漏洞有些转载方会把标题稍加改动比如前面加“重磅”、“快讯”精确匹配就失效了。更好一点的做法是算内容指纹。比较基础的实现把标题和正文前200字拼接再用MD5或SHA-256取哈希存到content_hash字段并加唯一索引。抓取新新闻前先查一下库里有没有相同哈希有就直接跳过。public boolean isDuplicate(News news) { String hashSource (news.getTitle() news.getContent()).substring(0, Math.min(200, news.getContent().length())); String hash DigestUtils.md5DigestAsHex(hashSource.getBytes(StandardCharsets.UTF_8)); return newsService.existsByContentHash(hash); }如果追求更高级的去重可以用SimHash做局部敏感哈希它允许内容有少量增删仍能判断相似但实现复杂度高。毕设层面做MD5指纹去重已经够了在论文里把SimHash作为“未来改进方向”提一句比把代码写得复杂但跑不动强得多。4.3 全文搜索MySQL内置全文索引 vs ElasticSearch这个项目的搜索模块有两种路线可以走选择要看你想要多高的“上限”和多大的工作量。路线一MySQL内置全文索引简单直接MySQL在5.7以后支持中文全文索引基于ngram解析器把title和content加上全文索引用MATCH() AGAINST()查询即可SELECT * FROM news WHERE MATCH(title, content) AGAINST(大数据 爬虫 IN NATURAL LANGUAGE MODE);这种方案对毕设来说足够不需要额外装中间件配置改动小代码也好维护。不足是搜索效果一般分词不如专业搜索引擎精细而且数据量一旦上百万后性能会明显下滑。路线二ElasticSearch有大数据味道在和“大数据”挂钩的毕设里我很推荐顺便把ElasticSearch引进来。用Logstash或自写同步Job把MySQL里的新闻数据同步到ES搜索接口直接查ES查询速度和分析能力完全不在一个级别上。Spring Boot里整合ES的步骤一般是引入spring-boot-starter-data-elasticsearch定义NewsDocument实体类加上Document(indexName news)注解写NewsSearchRepository继承ElasticsearchRepository搜索调用searchSimilar()或自定义的QueryBuilderES在词云图、高亮展示、按时间聚合统计这些场景上非常好用但部署起来比MySQL重如果电脑配置不够本地跑会被卡得怀疑人生。所以我个人建议物理内存8G以上才考虑ES路线否则就老老实实用MySQL全文索引把ElasticSearch写进论文里的“系统改进方案”不会影响毕业。4.4 按分类聚合展示手写一个简单的Layout渲染逻辑新闻抓回来后归属到哪个分类是整个聚合体验的重点。实现方式有两种一种是依据来源站点直接归入固定分类比如抓CSDN的自然就是科技类另一种是依据标题和正文内容做文本分类。第二种听起来高大上不过做起来复杂Apache的OpenNLP或Stanford NLP做文本分类要给训练集对毕设来说量太吓人了。我更推荐的是“来源规则关键词规则”的折中办法先在数据源模板里配字段category抓取时默认打一个基础分类页面展示时再从标题里跑一遍关键词规则比如包含“特朗普”、“总统”、“国会”就重分类为“国际”。这个策略简单效果也不差大多数新闻门户的标题本身就很“直白”。5. 数据库设计与几个容易翻车的细节别在数据这一环拖垮整个项目数据库是爬虫项目的“储藏室”设计得好不好直接决定跑数据时会不会报错、演示时会不会卡死。这一章重点讲表结构设计和实践中经常踩的几个坑。5.1 三张核心表新闻表、分类表、抓取记录表这个项目最少需要三张表。我给出实际可用的建表思路你可以按需调整字段。新闻表newsCREATE TABLE news ( id bigint(20) NOT NULL AUTO_INCREMENT, title varchar(255) NOT NULL COMMENT 新闻标题, content mediumtext COMMENT 新闻正文, source varchar(100) DEFAULT NULL COMMENT 来源站点, source_url varchar(500) DEFAULT NULL COMMENT 原始链接, category varchar(50) DEFAULT 综合 COMMENT 分类, keywords varchar(500) DEFAULT NULL COMMENT 关键词逗号分隔, cover_image varchar(500) DEFAULT NULL COMMENT 封面图或题图, publish_time datetime DEFAULT NULL COMMENT 发布时间, created_at datetime DEFAULT CURRENT_TIMESTAMP COMMENT 入库时间, content_hash char(64) DEFAULT NULL COMMENT 内容去重指纹, PRIMARY KEY (id), UNIQUE KEY uk_content_hash (content_hash), KEY idx_category (category), KEY idx_publish_time (publish_time) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;几个字段值得解释一下。content用mediumtext因为新闻正文可能几十KBmediumtext能存16MB足够content_hash加唯一索引在入库前先查库从源头杜绝重复publish_time建议取文章页面标注的发布时间而不是抓取时间因为批量抓取的源站发布顺序可能倒序用发布时间排序展示更自然。分类表categoryCREATE TABLE category ( id int(11) NOT NULL AUTO_INCREMENT, name varchar(50) NOT NULL, sort int(11) DEFAULT 0, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;分类表就三列够用了。前端导航栏就从这里动态查出来不要在前端代码里写死这样以后想加一个“财经”或者“科技深度”分类只要数据库加一条前端自动出现新入口。抓取记录表crawl_logCREATE TABLE crawl_log ( id bigint(20) NOT NULL AUTO_INCREMENT, source varchar(100) NOT NULL, crawl_type varchar(10) DEFAULT auto, total_count int(11) DEFAULT 0 COMMENT 抓取总量, success_count int(11) DEFAULT 0 COMMENT 成功量, fail_count int(11) DEFAULT 0 COMMENT 失败量, cost_time bigint(20) DEFAULT 0 COMMENT 耗时单位毫秒, start_time datetime DEFAULT NULL, end_time datetime DEFAULT NULL, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;你可能没想到我会专门加一张抓取记录表。但实际做下来这张表在答辩中的价值非常大评委问“你的系统有没有监控”你直接打开后台页面展示抓取次数、成功率、耗时曲线说服力比用嘴讲强得多。而且这张表还能帮你排查爬虫问题——看看失败的是哪个源是超时还是被限流一目了然。5.2 编码和时区存储时最经典的双重坑Mysql里的中文乱码问题我敢说做爬虫项目的人90%都遇到过。根源多半不在Java代码而在数据库连接参数。你需要在application.yml里把连接串的参数写全spring: datasource: url: jdbc:mysql://localhost:3306/news_platform?useUnicodetruecharacterEncodingutf8mb4serverTimezoneAsia/ShanghaicharacterEncodingutf8mb4用来保证入库支持和存取中文和表情符号serverTimezoneAsia/Shanghai用来避免Mysql连接时区和Java本地时区不一致导致时间字段差8小时。这两个参数少了任何一个轻则乱码重则时间对不上而且排查起来很恼人。5.3 数据量增长后怎么处理索引和查询新闻抓取一天几百条三个月就有几万条。这时候不带索引的SELECT * FROM news ORDER BY publish_time DESC LIMIT 20就会越来越慢。实际优化手段就三招难度都不高给publish_time和category加组合索引idx_cat_time (category, publish_time)列表页查询只查需要的字段不要总查content这个字段很大拖慢速度。用Query的投影把标题、来源、时间单独查出来数据量超过50万条以后再考虑按月份分表或者把历史数据归档但这对毕设来说大概率用不上另外建议在系统启动时做一次初始化数据导入。Spring Boot里用CommandLineRunner启动时向新闻表写入几百条示例新闻这样即使爬虫还没跑到第一轮打开前端也有内容展示演示效果不会被“空数据库”拖累。6. 答辩与代码讲解这个项目怎么讲才能拿高分代码写完了还不算结束真正决定你毕设分数的往往是最后十分钟的答辩展示。这个项目内容丰富但如果讲得没条理反而容易让评委抓不住重点。我把长期带毕设项目的经验整理成了一套讲解思路。6.1 用一条主线把系统串起来开头30秒定基调别一上来就说“我用了Spring Boot和爬虫”太俗了。我建议的开场思路是先点出问题背景再亮出技术方案。比如“随着每天网络上新增的新闻数量越来越大用户从海量信息里找到自己关心的领域变得很困难。我的系统解决的是资讯聚合的问题通过定时爬虫从多个源站点采集数据经过清洗、去重、分类后存储到MySQL再基于Spring Boot提供查询和浏览接口最终以Web方式展示给用户。整体上是一个数据从采集到展示的完整闭环。”这段话三十秒讲完电脑上同时打开系统首页让评委看着新闻列表正在刷新效果就很直观。紧接着再切到后台管理页演示手动触发一次爬取展示log表里新增记录这一步会让人对你的工程能力印象深刻。6.2 技术选型的“为什么”要提前准备好评委非常喜欢追问“你为什么不用XXX”。你要为每一个选型准备好理由为什么爬虫用Python/Java答Python生态丰富、反爬工具多、开发效率高Java的Jsoup轻量和Spring Boot共用一套技术栈部署简单。我的实现里更看重前期开发效率所以选X。为什么定时任务用Spring内置答系统规模不大内置调度足以支撑每天几十次抓取避免外部依赖如果后续要分布式部署可以平滑替换成XXL-Job。为什么存储不高亮ES答MVP阶段用MySQL就够ES作为后续扩展方案在论文里写了。这样显示出你有架构演进思维而不只是“不会”。能答好“为什么”基本就赢了一半。有些同学明明系统做得不错一被问选型就支支吾吾档次直接掉一半。6.3 项目演示的先后顺序安排从“效果”到“技术内核”我强烈建议演示顺序按照“从用户到原理”来做别一上来就打开代码讲类先把系统首页打开展示新闻列表、分类导航、搜索框点进一条新闻看详情页接着用搜索功能搜一个热词展示结果然后打开词云或分类统计页展示数据分析模块切到后台手动触发一次爬取现场新增几条数据最后展示数据库记录或者抓取日志表证明数据真的进了库这五步走下来评委脑海里就形成了“能看、能搜、能分析、能更新、有数据”的完整认知。剩下的时间用来回答提问全程不用写一行代码但技术含量完全展示到位了。6.4 代码讲解的避坑建议讲“思路”而不是逐行念如果你需要做代码讲解内容组织比代码堆砌更重要。我见过的优秀讲法是用“设计模式”视角讲先讲接口抽象再讲具体实现。比如爬虫部分你可以在PPT里放一张图CrawlAdapter接口定义了fetchList()和parseDetail()两个方法每个数据源是一个Adapter实现类新数据源只需新增一个实现类不需要改老代码。这种“策略模式/适配器模式”的东西不用很复杂但老师听起来会觉得你具备工程抽象能力。再比如数据清洗阶段你强调“幂等性”和“容错性”同一个URL重复抓不会产生重复数据因为入库前做了指纹查重某个源抓失败不影响其他源。这两个词一出来评委很难不给高分。结尾说点过来人的体会做这个题目最花时间的其实不是写代码而是“把数据弄干净”和“让整个流程稳定跑起来”。我第一次跑通全流程的时候数据源里有三个网站乱码、两个超时、一个网站改版后选择器失效调试过程极其磨人。后来我把所有源的抓取规则抽成可配置模板新增源不再改代码系统才算真正稳定下来。这个配置化的思路也成了我后来答辩时最值得讲的部分。如果你正卡在这个项目上我的建议是先把一个数据源从抓取到展示全部跑通再横向扩展其他源。一旦你看到第一条新闻从网页流进数据库再渲染到前端页面那种成就感会推着你把剩下的功能都做完。这个题目中的每个模块都可以单独再深挖下去而你已经有一个扎实的地基了。