校园舆情管理系统实战:Flask + MySQL + 情感分析预警
简介这份校园舆情管理系统源码是一套面向Python爬虫与数据分析方向的毕业设计/课程设计完整项目适合计算机相关专业学生用于选题参考、二次开发或答辩展示。项目围绕大学生微博舆情采集、存储、统计与预警展开涵盖登录与密码管理、微博爬取、负面信息百分比分析及饼状图/柱状图可视化并设置了负面比例超过20%即触发预警提示的机制。压缩包共256个文件约46.54MB核心文件包括29个py源码、30个pyc编译文件、1个sql数据库脚本以及html/css/js等前端页面另有75个gif操作演示图、27张jpg图片和文档说明便于对照运行与理解结构。配套环境为Python 3.6.8、MySQL 5.7、PyCharm与Navicat。目前已有64人学习浏览适合需要快速搭建校园舆情监控原型、学习爬虫与数据可视化整合流程的开发者。通过完整源码、演示素材和说明文档可直接复现负面舆情占比分析、预警阈值判断等模块并参考前后端交互方式迁移到其他爬虫分析项目中。1. 校园舆情管理系统到底解决什么问题从“谁在看热搜”到“一封预警邮件”这个标题拆开看是一个非常典型的 Python 全栈毕业设计核心题目是“校园舆情管理系统”交付物是完整前后端、MySQL 数据库、说明文档和论文素材。很多人第一眼把它当成爬虫项目真正动手才发现采集只是入场券决定系统能不能用、答辩好不好讲的是后面的文本清洗、情感打分和预警触发。系统解决的是学校宣传部门和辅导员的一个具体痛点学生讨论分散在微博超话、贴吧、表白墙、课程群截图里靠人工盯页面既慢又会漏。舆情管理系统要做的是把散落的文本收拢进 MySQL用 Python 后端给每条内容算一个情感分分数越低越负面再把负面且传播面大的内容以邮件形式推给管理员。管理者打开后台看到的不再是杂乱帖子而是“今天有 3 条负面舆情其中 1 条与食堂相关已发送预警”。这个选题适合想用 Python 走通全链路的同学后端用 Flask数据库用 MySQL前端是 Vue 页面或普通 HTML 模板再配说明文档就能凑齐一份合格的毕设交付。难度也恰到好处功能链路完整但复杂度可控唯一要花功夫的地方是情感分析的准确度而它恰好是答辩时最有话说的点。2. 系统架构与技术选型为什么是 Flask Vue MySQL 而不是全家桶确定课题后第一件事不是写代码而是把架构定下来。我做这种全栈毕设有条原则每一层选最成熟、最不大惊小怪的方案而不是选最潮的方案。一套方案如果能让我在答辩前一周还睡得着觉它就是好方案。2.1 三层架构拆解采集端、服务端、展示端各管什么把系统拆开看它其实是三个独立的部分。采集端负责把舆情文本送进系统。它可以是后台爬虫也可以是批量导入接口。毕业设计里我强烈建议把“接口抓取”做成可选项主力走 JSON 导入和手动录入。原因很简单真实平台的页面结构会变、反爬会升级答辩前一天爬虫突然失效这种事太常见而一条条 JSON 数据永远不会背叛你。服务端用 Flask 承载所有业务逻辑接收数据、清洗文本、跑情感分析、按阈值发预警、读写 MySQL。Flask 在这种体量下非常合适整个后端可以拆成四个模块models 管数据库操作utils 管清洗与情感分析services 管预警与统计routes 管 API 接口。展示端给管理员一个看板舆情列表、按情感分排序、详情页、趋势图、预警记录。技术选 Vue 还是原生 HTML 都行关键是别把精力耗在工程化配置上。这套三层结构最大的价值是边界清晰。答辩时老师问“数据从哪来”“谁做的判断”“怎么通知用户”你可以分别指向三个模块比在乱成一团的一锅炖代码里找答案好解释得多。2.2 Flask 与 Django 的取舍毕业设计看重的是快速出活和全链路覆盖可能有人会问Django 自带 Admin 后台和 ORM不是更适合管理系统吗我的看法是毕业设计不是选力气最大的工具而是选最能讲清楚自己工作量的工具。Django 的优势在于开箱即用劣势也很现实Admin 是框架生成的ORM 把 SQL 全封装了评委追问“你的表结构怎么设计的”时只能说“ORM 帮我建的”场面容易冷掉。Flask 更“裸”建表 SQL 自己写接口自己定义每一行代码都能说得出理由工作量也更容易被看见。搜 Python 毕业设计源码时你会发现Flask 方案占比明显高于 Django原因就在这项目结构扁平、阅读成本低、部署简单Flask Vue 这种前后端分离项目实战组合已经非常成熟遇到现成问题基本都能搜到解法。另外 Flask 的单文件起步方式对新手友好先在一个 app.py 里跑通再拆模块。我一般建议先把所有接口集中在 app.py 里验证最后按 routes/models/services 拆分而不是上来就套工厂模式。那是给自己找麻烦不是在做毕设。2.3 MySQL 在舆情场景下的角色一张舆情主表 两张辅助表的经典设计数据库是这套系统的地基。舆情数据特征很明确写入多、查询集中、单条数据不大就是一段文本加几个属性MySQL 完全够用没必要上 MongoDB。毕设环境里 MySQL 安装教程一搜一大把部署门槛最低。我习惯的设计是三张表tb_user 管后台登录tb_opinion 管舆情主体tb_warning 管预警记录。核心是 tb_opinion其他表都围绕它展开。CREATE DATABASE IF NOT EXISTS campus_opinion DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE campus_opinion; CREATE TABLE tb_user ( id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(64) NOT NULL, role TINYINT DEFAULT 1 COMMENT 1管理员 2普通用户, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) COMMENT用户表; CREATE TABLE tb_opinion ( id INT AUTO_INCREMENT PRIMARY KEY, title VARCHAR(200) NOT NULL, content TEXT NOT NULL, source VARCHAR(50) DEFAULT 手动录入 COMMENT 微博/贴吧/表白墙, sentiment_score FLOAT DEFAULT 0.5 COMMENT 0到1越低越负面, label VARCHAR(10) DEFAULT 中性 COMMENT 正面/负面/中性, category VARCHAR(50) DEFAULT 其他 COMMENT 食堂/宿舍/教学等, md5 CHAR(32) DEFAULT NULL UNIQUE COMMENT 内容指纹用于排重, status TINYINT DEFAULT 0 COMMENT 0未处理 1已处理, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, KEY idx_source (source), KEY idx_label (label) ) COMMENT舆情信息表; CREATE TABLE tb_warning ( id INT AUTO_INCREMENT PRIMARY KEY, opinion_id INT NOT NULL, warning_type VARCHAR(20) DEFAULT 邮件, warning_content VARCHAR(500), is_read TINYINT DEFAULT 0, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, KEY idx_opinion (opinion_id) ) COMMENT预警记录表;建表时有三个细节值得记一下。第一整库用 utf8mb4别用 utf8否则遇到 emoji 表情直接报错或乱码。第二md5 字段做唯一索引这是最简单的排重手段同一段贴文在微博和表白墙重复出现是常态重复数据会让情感统计失真。第三source 和 label 建普通索引因为前端列表页最常见的操作就是按来源过滤、按正负面筛选没有索引的表在几千条数据时也会明显变慢。如果你想让系统带点管理味道可以在 tb_opinion 的 status 字段上做文章前端列表支持“标记已处理”配合预警记录就能形成“发现-处理-归档”的闭环。这比单纯展示列表更贴近真实业务答辩时也拿得出手。3. 舆情数据采集与清洗从接口抓取到文本入库的完整代码数据进库是整个系统第一步也是最容易被低估的一步。很多同学急着写情感分析结果数据入口糊里糊涂后面所有排序和统计都建立在脏数据上越做越虚。3.1 三种数据来源的取舍手动录入、JSON 导入、接口抓取手动录入适合开发期验证单条流程表单提交即可没什么可说的。JSON 导入是演示阶段的主力准备一份带标签的批量数据一次导入几十条列表页立刻能看到排序和预警效果。接口抓取在真实舆情监控里最核心但毕业设计建议点到为止——写一个模拟来源接口或者对接一个自己有权限的校内通知源就行别去硬爬微博和贴吧。反爬和合规先不谈光是页面结构改版就能让你在答辩前一夜崩溃。下面是我常用的批量导入接口直接挂在 Flask 路由上import hashlib from flask import request from pymysql import IntegrityError app.route(/api/opinion/import, methods[POST]) def import_opinions(): items request.get_json().get(items, []) if len(items) 500: return fail(单次最多导入 500 条, code1001) rows [] for item in items: content clean_text(item.get(content, )) if not content: continue md5 hashlib.md5(content.encode(utf-8)).hexdigest() rows.append(( item.get(title, content[:30]), content, item.get(source, 手动录入), 0.5, # 初始情感分真实打分在写入后统一跑 中性, item.get(category, 其他), md5, 0 # 未处理 )) saved batch_insert_with_skip(rows) return ok({saved: saved, skipped: len(rows) - saved}, message导入完成)这段代码里有个很容易被忽略的细节初始情感分先给 0.5 占位写入成功后再统一跑模型打分。批量打分比逐条插入时分析快得多也方便你调阈值时重新回算所有数据不用反复改导入逻辑。返回体里同时带 saved 和 skipped前端可以直接提示“导入成功 46 条跳过重复 4 条”。参数上限制 500 条是为了防止一次性提交过大数据导致请求体超时实际项目里 500 条足够一次批量演示了。3.2 清洗文本的四个步骤去标签、去重复、滤特殊字符、切分长文从任何来源拿到的原始文本都不能直接入库尤其从网页粘贴来的内容带着 HTML 标签和大量不可见字符。我习惯把清洗写成四个连续的正则操作import re def clean_text(raw: str) - str: text re.sub(r[^], , raw) # 1 去掉 HTML 标签 text re.sub(rhttp[s]?://\S, , text) # 2 去掉网址 text re.sub( r[^\u4e00-\u9fa5a-zA-Z0-9。、\\\-\s], , text ) # 3 滤掉特殊符号与表情 text re.sub(r\s, , text).strip() # 4 压缩空白 return text为什么保留中文标点因为情感分析模型分词时会参考标点附近的语义全去掉会让“你做得真棒”和“你做得真棒”变成两句完全不同的话。为什么压缩空白Excel 导出的文本经常带大量空格和换行不压缩会导致同一句话算出不同的 md5排重直接失效。文本清洗之后还有一步去重和切分。贴吧和表白墙的转载量很大同一条内容会以不同标题反复出现所以我在清洗后做了两层处理def dedup_and_split(text: str, max_len: int 500): # 按句号/感叹号切分长文拆成多条再逐个排重 parts re.split(r(?[。]), text) seen set() result [] for part in parts: part part.strip() if part and part not in seen: seen.add(part) result.append(part) return result长文切分是有实际理由的SnowNLP 对超长文本的分词耗时接近线性增长500 字以上的贴文直接跑会卡住几秒。拆成短句后不仅模型跑得快单条的排重粒度也更细。3.3 建库建表与批量写入MySQL 的编码、索引与防重复插入清洗完的数据最后要落进 MySQL。连接串的写法直接决定后半夜你是在睡觉还是在处理乱码import pymysql conn pymysql.connect( host127.0.0.1, port3306, userroot, password123456, databasecampus_opinion, charsetutf8mb4, # 必须和建库保持一致 cursorclasspymysql.cursors.DictCursor ) INSERT_SQL INSERT INTO tb_opinion (title, content, source, sentiment_score, label, category, md5, status, create_time) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, NOW()) def batch_insert_with_skip(rows): saved 0 with conn.cursor() as cur: for row in rows: try: cur.execute(INSERT_SQL, row) saved 1 except IntegrityError: pass # md5 冲突说明已存在静默跳过 conn.commit() return saved这里要解释一个很多教程不会告诉你的点为什么不用 executemany 一把梭因为批量执行时只要有一条撞上唯一索引整批都会回滚前功尽弃。逐条 execute 并捕获 IntegrityError让重复的跳过去不重复的正常写入最后只 commit 一次性能和可靠性都兼顾。参数化 %s 是必须的直接用 f-string 拼 SQL 等着被注入。连接参数里最容易踩的是 charset 和 cursorclass。charset 不写或写 utf8中文可能变问号cursorclass 不指定查询结果是一堆元组前端拿不到字段名开发体验非常差。这几个参数我用下来基本是固定的直接抄就行。参数取值理由host127.0.0.1本地开发部署时改成服务器内网 IPport3306MySQL 默认端口charsetutf8mb4和建库保持一致防中文乱码cursorclassDictCursor返回字典前端直接消费 JSON4. 情感分析与预警规则落地让系统知道“这条舆情是负面的”数据进了库接下来的核心任务是给每条内容打情感分。这一步的产出直接决定预警准不准也是最需要反复调的地方。我的做法是“模型打底、规则兜底”两条腿走路。4.1 引入 SnowNLP一条评论怎么变成 0.87 的情感分人的注意力是有限的模型也一样。用现成的中文情感分析库是最快路径业界入门基本都从 SnowNLP 开始from snownlp import SnowNLP def get_sentiment(text: str) - float: s SnowNLP(text[:300]) # 控制长度避免长文拖慢 return float(s.sentiments) # 0 到 1 之间越接近 1 越正面sentiments属性内部走的是一个贝叶斯分类器输出 0 到 1 的浮点数。0.5 是决策边界0.6 以上倾向正面0.4 以下倾向负面。这个库的好处是调用极简坏处是它像一个黑匣子——你没法精确控制模型内部路径也不知道它为什么给一条文本打 0.87。解决方式不是去读源码而是用规则层在外面把结果“拉回来”。截取前 300 字是实测得出的折中方案舆情内容 90% 的有效信息集中在前 200 字截断后模型速度明显提升准确率几乎没有损失。4.2 自定义情感词典为什么默认模型在学校场景里会失灵SnowNLP 默认模型是拿电商评论训练的对“这个商品不错”很敏感但遇到“食堂涨价”“宿舍断电”“选修课划水”这些校园词汇基本无感。甚至会出现“食堂真不错除了贵没毛病”这种句子被模型判断为正面原因就是模型没学过“贵”在大学语境里的消极含义。所以我在模型分数之上加了一层规则纠偏用小而精的词表直接影响结果NEGATIVE_WORDS [投诉, 难吃, 不新鲜, 态度差, 乱收费, 宿舍断电, 漏水, 不负责] POSITIVE_WORDS [很棒, 干净, 方便, 负责, 点赞, 解决了, 优秀] def adjust_score(score: float, text: str) - float: neg sum(1 for w in NEGATIVE_WORDS if w in text) pos sum(1 for w in POSITIVE_WORDS if w in text) score score pos * 0.08 - neg * 0.12 return max(0.0, min(1.0, score)) # 夹在 0 和 1 之间参数说明0.08 和 0.12 不是拍脑袋定的是拿一版人工标注的 30 条测试数据跑一遍看分错了多少再往回调的。负面词的权重特意比正面词高因为舆情系统的首要目标是“不漏”宁可误报也不能放过一条真负面。词表控制在 10 个左右答辩时能跟评委解释清楚每个词为什么入选比堆 200 个词更有说服力。打完分数还要转成业务标签前端列表页要能按正负面筛选def score_to_label(x: float) - str: if x 0.6: return 正面 if x 0.4: return 负面 return 中性这里有个设计细节入库时把原始 sentiment_score 和 label 分开存。以后想调阈值只需用同一批分数重新跑 score_to_label不需要重跑模型。4.3 预警模块阈值触发与邮件通知的最小实现预警是整个系统真正“有用”的证明。我的实现是情感分低于某个阈值就触发邮件通知同时往 tb_warning 插一条记录。邮件用 Flask-Mail 发配置非常固定from flask_mail import Mail, Message app.config[MAIL_SERVER] smtp.qq.com app.config[MAIL_PORT] 465 app.config[MAIL_USE_SSL] True app.config[MAIL_USERNAME] adminexample.com app.config[MAIL_PASSWORD] 你的邮箱授权码 # 不是登录密码 mail Mail(app) def check_and_warn(opinion_id: int, score: float, title: str): if score 0.45: return subject f[校园舆情预警] {title[:20]} body f舆情编号 {opinion_id}情感分 {score:.2f}请及时处理。 try: msg Message(subjectsubject, recipients[managerexample.com], bodybody) mail.send(msg) except Exception as e: app.logger.error(邮件发送失败: %s, e) insert_warning_record(opinion_id, 仅记录, str(e)) else: insert_warning_record(opinion_id, 邮件, subject)阈值为什么取 0.45 而不是 0.4因为校园舆情里的负面表达往往带反讽和缩写模型打分普遍偏高0.45 的召回率更稳。SMTP 用 QQ 邮箱或网易邮箱都行注意用的是邮箱授权码不是登录密码。邮件发送失败时不抛异常而是写一条“仅记录”的预警至少日志里有证据证明系统判断到了只是通知渠道没通。调用位置放在情感分析之后批量写入时一条条触发for row in batch_sentiment_rows: score adjust_score(get_sentiment(row[content]), row[content]) update_score(row[id], score, score_to_label(score)) check_and_warn(row[id], score, row[title])4.4 前端接口联调统一返回 JSON 格式与跨域处理后端接口的风格要统一否则前端对接时非常痛苦。我习惯所有接口返回同样的壳子def ok(dataNone, messagesuccess): return {code: 0, data: data, message: message} def fail(messageerror, code1): return {code: code, data: None, message: message}前端只判断 code不管成功失败都能解析 data这套约定在任何前后端分离项目里都好用。真正的坑在跨域。前端本地跑 Vite 的 5173 端口Flask 在 5000 端口浏览器默认拦截跨域请求。最简单的处理是加一个全局响应头app.after_request def add_cors_headers(response): response.headers[Access-Control-Allow-Origin] request.headers.get(Origin, *) response.headers[Access-Control-Allow-Credentials] true response.headers[Access-Control-Allow-Headers] Content-Type,Authorization if request.method OPTIONS: response.status_code 204 return response注意 Access-Control-Allow-Origin 回显请求里的 Origin 而不是直接写*这样带着 cookies 的请求也能正常工作。另外 Flask 2.3 之后返回 JSON 时默认会把中文转成\u...转义序列前端显示一堆乱码。要在配置里关掉app.json.ensure_ascii False # Flask 2.3旧版本用 app.config[JSON_AS_ASCII] False这个配置项我至少帮别人排过三次错每次都是前端跑来问“为什么接口返回的是转义字符”。属于看一眼就能记住、不记住就要浪费半小时的坑。5. 部署与排查校园舆情系统在 Windows 和 Linux 上的 5 个常见坑这部分写的是我在跑类似源码时踩过的坑每条都按“现象→原因→解决”整理基本覆盖了环境差异和运行时最可能翻车的点。把它当排查手册用就行。5.1 坑一MySQL 8 认证插件导致连接失败现象Python 环境、MySQL 安装都正常但 Flask 一启动第一次查询就报Authentication plugin caching_sha2_password cannot be loaded后台刷出一整屏红色堆栈。原因MySQL 8 默认的认证插件改成了 caching_sha2_password而旧版 PyMySQL 0.9 及以下版本不认识这个插件握手阶段直接失败。解决优先升级 PyMySQL一步到位pip install pymysql1.0.2如果项目里还有其他连接 MySQL 的组件不兼容新驱动再退回改 MySQL 侧认证方式ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY 你的密码; FLUSH PRIVILEGES;这两种办法我优先推荐升级驱动因为改认证方式只是绕开了新特性以后别的工具连接还会遇到类似问题。看到这个报错先别慌先确认 PyMySQL 版本再动数据库配置。5.2 坑二SnowNLP 首次运行卡在模型下载现象第一次执行SnowNLP(text).sentiments终端没有任何报错就像死循环一样几分钟都没结果网页接口一直 pending。原因SnowNLP 的情感模型文件 sentiment.marshal 首次使用时需要放在 snownlp/sentiment/ 目录下如果本地没有代码内部会尝试联网获取。网络不通或下载源慢的时候它就静默挂在那里没有超时、没有提示非常坑。解决提前准备好模型文件放到 Python 环境的 site-packages/snownlp/sentiment/ 目录下代码启动时检测到文件存在就不会再触发下载。最稳的办法是找一台已经跑通的环境从同版本安装目录里拷贝 sentiment.marshal 过来避免在答辩现场干等。还有一种兜底写法在调用处加超时控制超时就改用纯规则评分import timeout_decorator timeout_decorator.timeout(2) def get_sentiment_with_timeout(text: str) - float: return get_sentiment(text)这个兜底可能不够优雅但能在关键时刻保你不丢脸。模型文件的问题属于一次配置、长期受益值得花十分钟提前解决。5.3 坑三前端跨域请求的三种报错与统一修复现象前端页面能打开但点按钮后浏览器控制台报错常见有这三种Access to XMLHttpRequest ... blocked by CORS policy一般缺 CORS 头请求带 cookies 时报Cannot use wildcard in Access-Control-Allow-Origin是用了*配了 Credentials请求打到 OPTIONS 预检返回 405是后端路由没处理 OPTIONS 请求原因前后端分离项目开发时前端在 5173 端口后端在 5000 端口浏览器会把它们当成两个域。Flask 没配跨域响应头前端就什么都拿不到。解决使用第 4.4 节里的 after_request 统一下发响应头同时处理 OPTIONS 预检。如果项目里已经装了 flask-cors一行也能搞定from flask_cors import CORS CORS(app, resources{r/api/*: {origins: [http://localhost:5173]}})注意开发环境用具体域名而不是*避免带 cookies 的登录请求在浏览器里被拦。部署时更推荐用 Nginx 把 /api 代理到 Flask 端口前后端同源跨域问题直接消失。5.4 坑四中文乱码从数据库到 JSON 的完整排查链现象数据库里手动插入的中文显示正常但网页端列表全是问号或者数据库正常、网页也正常接口返回的却是一堆\u5962\u8bae转义序列。原因中文乱码通常是三层叠加出来的。建库时不是 utf8mb4、连接字符串没写 charset、Flask 返回 JSON 时开了 ensure_ascii三个问题任中一个就会出现乱码。解决按这条链路逐个检查。首查建库语句用SHOW CREATE DATABASE campus_opinion;看字符集不是 utf8mb4 就重建或改库名。再查 PyMySQL 连接的 charset 参数没有写就补上。最后查 Flask 配置2.3 及以上版本app.json.ensure_ascii False老版本用app.config[JSON_AS_ASCII] False这个配置改完后接口返回的就是可读的中文不用在前端再做解码处理。5.5 坑五定时预警在 Windows 上不工作现象本地开发时预警功能正常部署到 Windows 服务器后加了 APScheduler 每分钟检查一次结果一晚上一条预警都没发。原因Windows 有两个隐藏杀手。一是电源选项里的睡眠策略系统睡眠后 Python 进程被挂起任务自然不跑二是 Flask 的 debug 模式会启动 reloader 子进程定时任务被注册了两份逻辑错乱谁也说不清。解决毕业设计阶段最稳的办法是别依赖后台定时器改成“写入即检查”。每次导入舆情或新增舆情时立即在同一个请求里完成情感分析和预警判断用户操作一次系统就检查一次。复盘一下本章开头提的架构原则预警、统计这类辅助功能能让它跟着主流程走就别让它独立成进程。如果确实需要每日统计报表用 Windows 计划任务调一个独立的 Python 脚本而不是挂在 Flask 应用里常驻运行。这条对我来说是血泪经验第一次做类似系统时被这种玄学问题折磨了两个通宵。6. 答辩前的验证方法与演示技巧一条舆情从采集到预警的闭环最后一个建议答辩前把演示路线固定成一条不要现场随机点数据。准备一份固定的测试集覆盖食堂、住宿、教学三个分类正负中各有分布。我常用的是一份 8 条以内的 JSON[ {title: 食堂二楼菜品不新鲜, content: 今天午饭吃到变质的菜向窗口反映没人理。, source: 表白墙, category: 食堂}, {title: 宿舍热水停供, content: 宿舍楼热水晚间停供第三天无人处理。, source: 微博超话, category: 宿舍}, {title: 自助借还机好用, content: 图书馆新装的自助借还机很方便点赞, source: 贴吧, category: 教学}, {title: 停电检修通知, content: 明天全校停电检修范围是教学区请同学们互相转告。, source: 校内通知, category: 教学} ]演示顺序可以是打开列表页按情感分升序排列点开一条负面内容看详情切到预警记录页刚才那条已经生成了邮件记录再打开邮箱展示收到的预警。这条链路走完系统核心价值全部体现出来评委不需要看你的代码也能知道系统在做什么。验证接口是否准备好用一条命令就能回放curl -X POST http://127.0.0.1:5000/api/opinion/import \ -H Content-Type: application/json \ -d {items: [{title: 食堂二楼菜品不新鲜, content: 今天午饭吃到变质的菜向窗口反映没人理。, source: 表白墙, category: 食堂}]}导入后立刻查列表看这条数据的情感分是否低于 0.4、标签是否为负面、预警记录里是否多了一行。整个闭环验证完系统才算真正立住了。加分技巧如果评委问“你凭什么相信这个情感分析结果”不要硬夸模型准。可以拿那条停电检修通知举例它本身没有负面情感词但转发量很高系统按热度加权后把它排在评估列表前面说明你不只看单一分数而是结合传播度辅助判断。承认模型边界反而显得更有业务思考比“准确率百分之九十几”这种经不起追问的说法稳妥得多。前端再加一个按天聚合负面舆情数量的折线图趋势一旦呈现出来评委能非常直观地理解这个系统的价值。我第一次做类似课题时答辩前半小时发现情感模型文件没下好页面一直在转圈只能临时换假数据接口硬撑过去场面相当狼狈。从那以后我养成了“演示前三查”的习惯查库里有没有演示数据、查模型文件在不在、查端口有没有被占用。这套闭环跑通了你的毕业设计就成功了大半。希望帮到你。本文还有配套的精品资源点击获取