ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Spring Boot + 大数据组件实战:从零搭建舆情热点话题分析管理系统全攻略

Spring Boot + 大数据组件实战:从零搭建舆情热点话题分析管理系统全攻略 如果你正在准备毕业设计或者想从零做一个舆情热点话题分析管理系统这篇内容应该能给你省掉不少瞎折腾的时间。市面上的毕设项目源码很多但大多数拿到手之后要么环境跑不起来要么代码看不懂、不会改。这个项目就是典型的 Spring Boot 后端加重型大数据分析组件组合既覆盖了常规管理系统的增删改查又塞进了爬虫采集、热点发现、情感分析、可视化大屏这些偏应用型的功能作为毕设或者练手项目都相当合适。全文我会从项目思路、技术选型、核心模块实现、数据库设计、实操过程到排错经验把我实际开发调试时踩过的坑和验证过可行的方案都写出来希望能帮你真正把这套系统跑通、讲清、改得动。1. 项目思路这套系统到底要解决什么问题1.1 舆情热点话题分析到底在做什么简单说它做的事情是把互联网上散落的、和某个主题相关的信息收集起来清洗掉低质量内容再通过统计分析和文本挖掘手段找到哪些话题正在快速升温、哪些事件引发了强烈情绪波动最终用表格、图表、时间曲线这些直观的形式呈现给用户。它不只是一个信息展示平台核心价值在于“从海量数据里发现规律”。举个例子假设你关注“某品牌手机发布”这个关键词系统会定时从各大新闻门户、论坛帖子、公开讨论区抓取相关内容。抓回来的数据不是直接展示而要经过去重、去噪、分词、情感打分再按时间窗口聚合出热度趋势。如果某个时间点的讨论量突然暴增或者负面情绪比例升高系统就把这个时间点标记为异常热点推送给运营人员。整个过程里技术难点不在界面而在数据链路是否稳定、分析结果是否可信。1.2 从毕业设计角度拆解需求毕设项目不能让老师说“这不就是一个普通管理系统”。评判一个毕设好坏往往看两点一是技术栈有没有覆盖面二是功能能不能体现完整的业务流程。舆论热点分析系统恰好两头都占。从功能模块看它天然包含用户管理、角色权限、话题管理、数据统计、可视化管理这些后台功能又额外增加了数据采集任务、热点识别引擎、情感分析计算这些模块组合在一起就能形成一个完整的闭环数据进来、处理、存储、展示、反馈。从难度梯度看它也很有层次。基础部分就是 Spring Boot 写 RESTful 接口配合 Vue 做页面联调这部分只要做过普通管理系统就能上手。进阶部分是接入数据采集框架、消息队列、分布式计算引擎、全文检索服务这部分能拉开差距。答辩时你能清楚说出“为什么用 Kafka 削峰”“为什么用 Spark 做批量计算”“为什么舆情数据要放 ES 而不是全放 MySQL”这比单纯背八股文有说服力得多。1.3 项目功能边界与模块划分我这里把系统拆成五个子模块来理解后期写代码、写文档、答辩讲解都会清晰很多。第一个是数据采集层负责定时抓取新闻和公开讨论数据第二个是数据预处理层负责格式转换、去重、敏感信息过滤第三层是分析计算层提供关键词提取、话题聚类、热度计算和情感判别第四层是应用服务层也就是 Spring Boot 提供的各类业务接口最后一层是前端展示层包括管理后台和分析大屏。模块边界清晰之后你才会知道每个报错该去哪里排查。比如网页打开慢问题可能不在前端而是在 ES 查询抓取没有数据问题大概率在调度任务或者反爬策略上图表没有数据返回要去看后端接口的 Redis 缓存是否失效。把系统按模块拆开就等于把排查范围缩小了一大半这一点在实际调试中特别关键。2. 技术选型为什么是 SpringBoot 加大数据这套组合2.1 后端框架、语言版本和前端方案后端我建议直接用 Spring Boot版本选 2.7.x搭配 JDK 1.8。不要图新鲜上 JDK 17 或者 Spring Boot 3.x因为很多大数据组件对 JDK 版本的兼容测试没有跟上毕设阶段没必要自己给自己增加兼容性风险。JDK 1.8 加 Spring Boot 2.7 是这几年最稳的组合网上资料多遇到问题基本都有现成答案。前端以 Vue 2 加 Element UI 为主可视化报表部分采用 ECharts。Vue 3 也可以但如果你的参考资料大部分还是 Vue 2 的写法就老老实实用 Vue 2毕竟你重点不是前端而是整个系统的数据链路能不能讲通。有人会觉得大屏页面很炫其实核心就是 ECharts 的折线图、词云图、饼图、地图组件互相嵌套数据格式对了效果自然就出来了。2.2 大数据组件如何取舍这是整个项目里最需要动脑子的地方。大数据组件可选范围很广但不是越多越好每引入一个组件部署和调试成本就涨一倍。我最终采用的方案是Kafka 做消息队列Spark 做离线批量分析Elasticsearch 做舆情数据的存储和检索MySQL 做业务数据存储Redis 做缓存和热点数据加速。有人会问分析功能用 Spring Boot 自己写行不行行但不推荐。因为舆情分析里涉及分词的批量处理、聚类计算、时间窗口统计这些用 Spark 写起来更自然而且能在答辩时明确突出“大数据技术”这个标签。但也别把 Spark 用在所有地方比如用户登录、话题配置这类轻量操作还要走 MySQL 加 Redis不要让所有请求都经过 Kafka 和 Spark那样系统会被不必要地拖慢。2.3 系统整体架构与数据流转整套系统的数据流是典型的“采集-缓冲-计算-存储-展示”链路。数据采集模块定时从目标网站抓取内容解析成统一的数据结构后发送到 Kafka 指定主题。下游的 Spark 任务从 Kafka 拉取数据进行清洗和分析再把结构化结果写回 MySQL把原始文本和索引数据写入 Elasticsearch。Spring Boot 后端对外提供查询接口前端大屏通过接口读取数据渲染图表。这个链路里有一个非常容易忽略的点采集写入 Kafka 的速度和 Spark 消费分析的速度是不同步的。如果采集过快而消费太慢Kafka 里的积压数据会越来越多。所以我在设计时给 Kafka 配置了三个分区消费者线程数设为三个保证消费并行度同时在 Kafka 生产者端做了批量发送参数调优。这些细节不写进博客的话很多人根本意识不到数据量大了以后系统为什么会延迟。提示如果你的电脑配置一般跑不动完整的大数据组件可以把 Spark 部分先用 HanLP 分词加 JDK 原生线程池代替做成“准大数据架构”但系统文档里要写清楚生产环境用 Spark 的方案。这样本地运行稳答辩内容也不虚。3. 核心模块设计与实现3.1 数据采集模块数据从哪里来采集模块是整个系统的数据源头数据都没有分析就是空谈。我用的是半开源半自写的思路网页抓取部分基于 HttpClient 加 Jsoup调度部分用 Spring 自带的 ScheduledExecutor 和 Quartz 结合规则配置放在数据库里。具体流程是这样的系统维护一张采集规则表记录每个采集源的入口地址、链接匹配规则、正文区域选择器、发布时间解析表达式。定时任务到点后先根据规则抓取列表页提取详情页链接再对每个详情页进行正文解析。解析完成后生成一个统一的舆情消息对象里面包含标题、正文、来源、发布时间、作者、URL 这几个核心字段。采集策略上初次全量、后续增量。首次入库时把目标网站最近一个月的数据都抓下来之后每 10 分钟执行一次增量抓取只处理新出现的链接。为了避免重复入库我在数据库里对链接 URL 做了唯一索引插入时通过 ignore 关键字跳过重复数据。这里有个坑要提醒你很多门户网站正文页的链接后面会带统计参数同一个内容会生成不同 URL单纯对 URL 去重会失败。我的处理办法是取出规范的 URL 路径部分进行 MD5再配合标题模糊匹配准确性会高很多。3.2 数据清洗与归一化处理网络数据质量比想象中要差得多。广告、导航、推荐位内容混在正文里各种转义字符混乱有些页面发布时间格式还不统一。清洗这一步做不好后面分析结果肯定歪。清洗管道我分成四个环节。第一个是内容提取通过选择器选中正文容器过滤掉 script、style、iframe 这些非正文节点。第二个是文本归一化把全角字符转半角去除零宽空格和乱码字符统一换行符。第三个是信息补全有些页面没有发布时间就取 URL 中的日期参数或者文件最后修改时间兜底。第四个是质量过滤正文长度少于 30 个字符的或者相似度超过某个阈值的直接标记为低质量数据不进入后续分析流程。这里我特别想强调时间字段的处理。很多爬下来的数据时间是“昨天 18:30”或者“3 小时前”这种相对时间直接存数据库没法做趋势图。必须写一个时间解析器把所有相对时间根据爬取时间转换成标准时间戳。我一开始没做这步导致热度折线图的时间轴乱七八糟后来花了整整一个下午才排查到这里真的建议提前把格式统一好。3.3 热点发现与热度计算公式热点话题的发现是这个系统的灵魂部分也是答辩时最容易出彩的功能点。我采用的热点发现方案是“分时段统计加文本聚类”而不只是简单地按关键词出现次数排序否则和搜索引擎的搜索热榜没有区别。先按小时切片统计每小时内每条文本的词语频次提取 TF-IDF 权重排名靠前的词语作为候选标签。然后用滑动窗口把连续 3 个小时的数据合并为一个话题窗口在窗口内部对文本做相似度计算相似的归为一类话题。最后对每个话题计算热度值。热度值我采用的公式是热度得分 讨论量权重 × 0.35 传播速度权重 × 0.3 情感烈度权重 × 0.2 来源权重 × 0.15。所有指标都做了归一化处理这样不同时段的话题才有可比性。举个例子一条新闻被 50 个普通论坛转载和一条新闻被 5 个大型门户报道前者的绝对数量更大但后者的来源权重和传播速度更高综合热度往往更靠前。这就是公式设计背后的逻辑。计算公式跑完之后热度超过设定阀值的话题会自动进入热点话题表后台管理员可以手动审核确认保证系统里展示的内容有意义。3.4 情感分析与可视化输出情感分析我用的是基于情感词典加规则的方法。提前准备一个基础情感词典为每个词标注正向分值或负向分值然后对待分析文本进行分词匹配累计所有情感词的分值另外针对否定词、程度副词做修饰处理。比如“非常满意”和“不满意”如果不处理修饰词情感得分会完全错误。规则里加入程度副词权重后“非常”会将后接词的分值乘 1.5“不”会将后接词分值取反准确率会提高不少。可视化部分重点做四块。第一块是话题趋势折线图展示某个话题 7 天或者 30 天内的讨论量变化第二块是情感占比饼图按正面、中性、负面三类展示比例第三块是热点词云图按 TF-IDF 权重展示当前最热门的词汇第四块是来源分布图按新闻门户、论坛、社交媒体这些来源统计占比。每一块图表都不是凭空展示数据来源都在后端有对应的统计接口进行联调时把数据格式对齐即可。4. 数据库与核心接口设计4.1 核心数据表结构设计数据库设计我采用了业务库、分析库分离的思路。业务库放在 MySQL 里保存用户、角色、话题配置、预警设置这类变动频繁的数据分析库放在 Elasticsearch 里保存采集到的舆情原文和分析结果。但 MySQL 里也要保留一张结果汇总表方便前端做快速展示不然所有请求都打到 ES对 ES 的压力很大。推荐的核心表包括用户表、角色表、采集规则表、舆情信息表、热点话题表、情感统计表、预警配置表。舆情信息表字段可以设计为 id, title, content, source, url_md5, publish_time, crawl_time, emotion_score, status。热点话题表字段设计为 id, topic_name, keywords, heat_score, start_time, end_time, status。采集规则表字段设计为 id, name, entry_url, link_regex, content_selector, time_pattern, enabled。设计索引的时候要注意来源和时间字段要建联合索引因为最常见的查询就是按时间范围查某个来源的数据。4.2 后端接口设计要点后端接口遵循 RESTful 风格统一返回 Result 对象里面包含 code、message、data 三个字段。核心接口包括分页查询话题接口、话题详情接口、情感趋势接口、热词统计接口、数据采集任务启停接口、预警配置接口。接口路径建议这样设计/api/topic/page、/api/topic/detail、/api/analysis/trend、/api/analysis/emotion、/api/analysis/wordcloud。分页查询务必使用 MyBatis-Plus 的 Page 插件不要自己写 limit 去拼 SQL很容易出现 SQL 注入问题。趋势分析接口要注意时间粒度参数前端传入 day 或者 hour后端能自动切换分组统计。如果查询数据量比较大要给这些接口加上 Redis 缓存缓存键按照查询条件生成过期时间设置为 5 分钟既保证数据实时性也减少数据库压力。4.3 权限控制与用户管理权限控制采用 Spring Security 加 JWT 的方案维护一张用户表、一张角色表、一张用户角色关联表。管理员和普通用户看到的内容是不同的。管理员可以管理采集任务、审核热点话题、设置预警阈值普通用户只能查看分析结果和下载统计报告。前端根据后端返回的权限标识控制菜单显隐后端在接口上加 PreAuthorize 注解做二次校验。这里要提醒一个常见的坑很多同学只在后端返回 token前端把 token 存到 localStorage 就完事了结果刷新页面后权限状态丢失。正确的做法是登录成功后把用户信息和 token 都存到 Vuex 和 localStorage刷新时从 localStorage 恢复用户信息再用 token 请求一次后端用户信息接口刷新状态。5. 实操过程把系统从 0 跑到 15.1 环境准备与版本匹配整个开发环境的搭建是新手最容易放弃的地方因为组件太多版本又不兼容。我的建议是先把 JDK、Maven、MySQL、Redis 这几个基础组件装好再把 Kafka、Spark、Elasticsearch 装好一定要保证它们之间的依赖版本兼容。实测下来比较稳的组合是Kafka 2.8.0 配 Spark 3.1.2 配 Elasticsearch 7.10.0再配 Spring Boot 2.7.x。这三个大数据组件如果版本差距过大比如 Kafka 3.x 配 Spark 2.x日志里会报一堆奇怪的序列化错误排查起来让人怀疑人生。本地开发时不要把所有大数据组件全部启动太吃内存。我是在搭建阶段只启动 MySQL、Redis、Kafka做分析测试时再单独启动 Spark 和 ES。这样可以减少同时占用资源一台 16G 内存的机器跑起来也基本流畅。5.2 采集模块落地的关键实现采集模块的主流程放在一个定时任务里核心代码如下Component public class CrawlScheduledTask { Autowired private CrawlRuleService crawlRuleService; Autowired private NewsCrawlerService newsCrawlerService; Scheduled(cron 0 */10 * * * ?) public void crawlAllRules() { ListCrawlRule rules crawlRuleService.listEnabledRules(); for (CrawlRule rule : rules) { try { newsCrawlerService.crawlByRule(rule); } catch (Exception e) { log.error(采集任务执行失败规则ID: {}, rule.getId(), e); } } } }任务里一定要做异常隔离一个采集源失败不能影响其他采集源。我用了一个线程池去并发执行不同规则的采集每个任务都加了超时控制单个源超时 60 秒后就自动跳过避免某个网站响应过慢拖垮整个调度链路。采集到的原始对象在发送到 Kafka 之前会在内存里做一次快速去重降低下游压力。5.3 分析引擎落地的关键实现分析引擎是系统里最有技术含量的部分我用 Spark 写了一个批量分析任务用 Java 代码实现结构化流程。任务先读取 Kafka 指定主题的数据然后依次执行分词、关键词提取、情感计算、话题聚合四步处理。分词采用 HanLP 的感知机分词模型关键词提取使用 TF-IDF 算法情感分析用自定义词典做规则匹配。JavaInputDStreamConsumerRecordString, String stream KafkaUtils.createDirectStream( jssc, LocationStrategies.PreferConsistent(), ConsumerStrategies.Subscribe( Collections.singletonList(news_topic), kafkaParams ) ); stream.foreachRDD(rdd - { if (!rdd.isEmpty()) { // 转成 DataFrame进行后续清洗和分词处理 DatasetRow newsDF ...; // 调用自定义UDF进行文本分词与情感计算 DatasetRow resultDF newsDF.withColumn(keywords, callUDF(extractKeywords, col(content))) .withColumn(emotion, callUDF(computeEmotion, col(content))); // 按小时窗口聚合计算话题热度 resultDF.write().mode(SaveMode.Append).jdbc(mysqlUrl, hot_topic, props); } });在实际跑Spark任务的时候我遇到的第一个问题就是每个批次处理时间越来越长后来发现是Kafka的偏移量没有自动提交重启任务后老数据反复处理。解决办法是启用 Spark Streaming 的自动提交机制并且处理完成后手动提交偏移量。另外一个容易踩的坑是 HanLP 词典在 Spark Executor 中找不到需要把词典目录打成资源包提交到集群或者在代码里配置绝对路径。5.4 前端大屏与后端联调前端页面我采用 Vue 2 加 ECharts使用 vue-echarts 组件渲染图表。大屏页面分为头部标题区、左侧热点排行区、中间趋势图区、右侧情感分布与来源分布区。联调过程中最常出问题的就是跨域。前端地址是 localhost:8080后端接口是 localhost:9090必须配置后端跨域过滤器或者通过前端代理转发。我建议用代理转发开发环境配置 devServer 的 proxy 指向后端这样前端代码里全部使用相对路径部署上线时不容易出错。还有一个联调细节是要约定图表数据的空值处理。比如某个时间点没有任何数据后端返回 null前端如果用 ECharts 默认配置图表会出现断线或者空白视觉上很丑。我在后端统一把 null 转成 0前端再把 0 值标注为零点整体效果自然很多。6. 常见问题与调试技巧实录6.1 中文乱码问题这个几乎每个做中文舆情项目的人都会遇到。乱码出现的位置主要有三个页面显示乱码、MySQL 存储乱码、Kafka 消息消费乱码。页面显示乱码通常是因为 HTML 页面没有设置 UTF-8 或者后端响应头编码不对在 Spring Boot 的 application.yml 里把 server.servlet.encoding 配置好就行。MySQL 存储乱码多半是建库时字符集没指定。我建议建库时直接写成 DROP DATABASE IF EXISTS sentiment_db; CREATE DATABASE sentiment_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;注意要选 utf8mb4普通 utf8 在存 emoji 表情时会报错。舆情正文里经常带着 emoji不提前用 utf8mb4 后面有你哭的时候。Kafka 消费乱码则要检查生产者和消费者的参数是否一致我统一设置了 key.serializer 和 value.serializer 为 StringSerializer同时确认走的是 StringDeserializer基本不会再出问题。6.2 采集模块被封与反爬限制爬虫被封是这个项目里几乎必然会遇到的一个大坑。我实测了几个常见网站有的会校验浏览器标识有的会限制单位时间内的请求频率有的会弹出验证页面。针对这些最简单的处理是设置随机 User-Agent维护一个常见浏览器的 UA 池每次请求随机取一个。请求频率也要控制同一个域名的请求间隔设置为 800 到 1500 毫秒之间的随机值。如果还是被封就考虑接入 Cookie 池和代理池。代理池搭建比较费劲我建议毕设阶段采用一个降低频率加随机 UA 的轻量方案只要不是大规模并发抓取基本不会触发强制封禁。还有一点必须注意抓取行为要遵守目标网站的 robots 协议和版权要求毕设项目选几个允许公开访问的新闻源即可别盯着需要登录的网站硬碰硬。6.3 大数据组件资源占用过高我遇到过很多次这样的情况Spark、ES、Kafka 全部启动后笔记本风扇狂转系统卡到鼠标都不动。大数据组件默认配置是按照集群标准来设置的本地跑必须改资源参数。Kafka 启动脚本里的堆内存默认很大我改成了 256MElasticsearch 的 jvm.options 文件里设置 -Xms512m 和 -Xmx512mSpark 的 spark.executor.memory 设置为 1gspark.driver.memory 设置为 512m。这样整体占用能控制到 4G 以内。如果是使用 Windows 开发环境建议优先用 WSL 或虚拟机跑 Kafka 和 ESWindows 原生的 JDK 版本兼容问题少一些而且文件句柄限制也更好调整。如果电脑内存实在不够还有一个临时方案先只启动 MySQL、Redis 和 Kafka把采集到的数据先落到 Kafka 积压着分析模块开发完毕后再启动 Spark 做全量消费分析边开发边启动多种大数据组件对机器来说真的是一种折磨。6.4 远程调试配置与操作拿到别人分享的项目或者自己开发到一半需要远程调的时候远程调试能力很关键。Spring Boot 远程调试配置很简单在启动命令中加入 JVM 参数java -agentlib:jdwptransportdt_socket,servery,suspendn,address*:5005 \ -jar sentiment-analysis.jar然后在本地 IDEA 里配置一个 Remote JVM Debughost 填写服务器 IPport 填写 5005点击 Debug 按钮就能断点远程代码。这里要特别注意生产环境不要开启远程调试端口容易被扫描攻击调试完成后立刻关闭。还有一点远程调试需要保证本地代码和服务器上的 jar 包是同一个版本否则断点位置会错位调试起来非常头疼。6.5 关于项目定制与后续扩展方向很多同学拿到系统后不知道怎么改成自己的题目。我提供一个比较实用的定制思路换数据源。把采集规则表里的数据源配置改掉换成你指定的新闻源或者领域垂直网站分析模块保持不变项目就从“通用舆情分析”变成了“某行业舆情分析”。其次可以增加预警方式比如热点话题热度超过阈值后通过邮件或短信发送通知这个功能实现起来不复杂但很能提升完整度。如果以后想继续深入可以尝试引入预训练的深度学习模型做更细粒度的情感分类也就是判断一条文本是愤怒、高兴、悲伤还是惊讶这就比简单的正负情感更有说服力。还可以把文本聚类从 KMeans 改成基于密度的聚类对小热点话题的发现更敏感。整个系统扩展性还是不错的因为你用了消息队列和独立分析引擎新增算法只需要替换分析模块不需要大改业务端。7. 写在最后按这个思路完整做下来你对 Spring Boot 的理解、对大数据处理链路的概念、对前后端联调的熟悉程度都会有一个实打实的提升。我帮别人调试这个项目的时候最深的感受是它最大的价值不在于代码多复杂而在于你通过它理解了长数据链路的系统是怎么组织起来的。数据从网络抓取到消息队列再到计算引擎和业务接口每一层都有自己的职责每一层也都有自己可能出错的地方。这种“全链路思维”对做以后的复杂项目特别有帮助比背几个框架 API 重要得多。如果你正在为这套系统发愁建议先从数据流转方式入手把架构图理顺再一个模块一个模块地跑通别急着表面上的大屏效果底层逻辑扎实了表面效果只是时间问题。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进