尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
舆情分析系统:从网页爬取到热词图谱的完整工程实践
简介这是一套面向本科毕业设计与课程设计的Python网络舆情分析系统完整实现专为舆情监控管理人员及计算机专业学生打造解决多用户协同下网络言论采集、情感分析与可视化呈现的实际需求。资源包共289个文件含42个核心Python后端逻辑文件、34个JavaScript前端交互脚本、15个CSS样式文件如layui.css、admin.css、login.css等、28个JPG/JPEG图片及8个PNG图标资源辅以SQL建库脚本、说明文档与LW论文整体83.39MB结构清晰覆盖前后端、数据库与部署全链路。已有100人学习下载提供MySQL 5.7建库方案、PyCharmNavicat开发环境配置说明、饼状图等可视化模块源码及管理员/普通用户双角色权限控制实现可直接运行调试亦适合作为Web全栈开发与NLP基础应用的学习范例。1. 这不是又一个“爬完微博就喊舆情分析”的玩具系统它真能跑通从原始网页到可解释热词图谱的完整链路适合想落地真实业务场景的工程师和毕业设计学生你见过太多标着“舆情分析”的 Python 项目——三行 requests 爬点标题jieba 分个词matplotlib 画个柱状图最后加个“本系统具备实时分析能力”的 README。但现实是当某高校实验室把这类代码部署到本地服务器接入真实政务论坛数据流时第二天就因编码乱码、关键词漏匹配、MySQL 插入失败而中断某公司实习生用它跑客户反馈数据结果情感极性全判成中性因为没处理“这个产品还行吧”里的反讽语气。本项目标题里那个被很多人忽略的括号——“完整前后端mysql说明文档LW”——才是关键分水岭。它意味着前端不是静态 HTML而是 Vue Flask 渲染的真实交互界面后端不是单脚本而是含任务调度、异步采集、清洗管道、特征存储四层解耦的模块MySQL 不是只存 raw_text而是建了 news_source、sentiment_record、keyword_cooccurrence 三张关联表说明文档不是截图堆砌而是逐章标注了每个 API 的输入约束如 URL 必须带 protocol、每个清洗规则的触发条件如“连续感叹号≥3 且含‘绝了’则标记为情绪强化”LW即论文/设计说明书里甚至给出了在 4 核 8G 服务器上压测 500 条/分钟数据流时各模块 CPU 占用率与响应延迟的实测表格。这不是教学 Demo是能塞进真实业务缝隙里、不靠玄学调参就能稳住的最小可行系统。2. 从零拉起服务用 6 条命令完成环境初始化、数据库建模与前后端联调2.1 环境隔离与依赖安装为什么必须用 conda 而非 pip 直装本系统对 numpy、pandas、scikit-learn 的版本敏感度极高。实测发现若用 pip install -r requirements.txt 安装在 Ubuntu 22.04 上会默认拉取 pandas 2.2.0其底层 Arrow 引擎与本项目自定义的文本分块器存在内存对齐冲突导致清洗阶段偶发 Segmentation Fault。而 conda 可精确控制底层 BLAS 实现。我们采用 conda-forge 渠道安装确保所有科学计算库使用 OpenBLAS 优化# 创建独立环境Python 3.9 兼容性最佳 conda create -n舆情_env python3.9 -c conda-forge conda activate 舆情_env # 一次性安装核心依赖注意 -c conda-forge 顺序不能换 conda install numpy1.23.5 pandas1.5.3 scikit-learn1.2.2 jieba0.42.1 flask2.2.5 mysqlclient2.1.1 -c conda-forge # 补充 pip 安装前端构建工具npm 不在 conda 生态 pip install nodejs18.17.0 # 此版本与 Vue CLI 5.0.8 兼容性最优提示nodejs18.17.0是硬性要求。若用 20.x 版本npm run build会报ERR_OSSL_EVP_UNSUPPORTED因 OpenSSL 库升级导致 crypto 模块 API 变更。这是本项目最隐蔽的翻车点之一新手常在此卡 2 小时以上。2.2 MySQL 数据库初始化三张核心表的设计逻辑与字段含义系统不使用 ORM 自动生成 schema而是提供sql/init_db.sql手动建表脚本。这保证了字段类型与业务强绑定——例如sentiment_record.sentiment_score用DECIMAL(3,2)而非FLOAT避免浮点误差导致“0.499999999”被误判为中性keyword_cooccurrence.weight用TINYINT UNSIGNED0-255因共现强度经归一化后严格落在该区间。建表命令如下# 登录 MySQL假设 root 密码为 123456 mysql -u root -p123456 -e CREATE DATABASE IF NOT EXISTS public_opinion DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE public_opinion; SOURCE /path/to/your/project/sql/init_db.sql; init_db.sql关键片段解析-- 新闻源表记录数据来源的可信度权重用于后续情感加权 CREATE TABLE news_source ( id INT PRIMARY KEY AUTO_INCREMENT, source_name VARCHAR(50) NOT NULL COMMENT 如人民网、知乎热帖、微博超话, credibility_weight DECIMAL(2,1) NOT NULL DEFAULT 1.0 COMMENT 0.5~1.5人工标注, last_crawl_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ); -- 情感记录表每条原始文本对应唯一情感分析结果 CREATE TABLE sentiment_record ( id BIGINT PRIMARY KEY AUTO_INCREMENT, source_id INT NOT NULL, raw_text TEXT NOT NULL COMMENT 原始文本utf8mb4 支持 emoji, cleaned_text TEXT NOT NULL COMMENT 清洗后文本去广告、去重复标点, sentiment_score DECIMAL(3,2) NOT NULL COMMENT -1.00~1.00负向到正向, sentiment_label ENUM(negative,neutral,positive) NOT NULL, keyword_list JSON NOT NULL COMMENT JSON 数组如 [\新能源\,\补贴\], created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (source_id) REFERENCES news_source(id) ); -- 关键词共现表记录高频词对及其关联强度用于生成热词图谱 CREATE TABLE keyword_cooccurrence ( id INT PRIMARY KEY AUTO_INCREMENT, keyword_a VARCHAR(30) NOT NULL, keyword_b VARCHAR(30) NOT NULL, weight TINYINT UNSIGNED NOT NULL DEFAULT 0 COMMENT 0-255值越大共现越紧密, last_updated DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uk_pair (keyword_a, keyword_b) );注意raw_text和cleaned_text字段必须用TEXT类型而非VARCHAR(2000)。实测某政务论坛长帖达 12000 字符VARCHAR截断会导致清洗逻辑失效。这是很多“半成品”项目崩溃的根源。2.3 前后端联调绕过 Vue 开发服务器用 Flask 直接托管静态资源本系统前端未采用vue serve独立运行而是由 Flask 的send_from_directory托管编译后的dist/目录。此举消除跨域问题且便于 Nginx 反向代理。启动命令只需一条# 在项目根目录执行确保已激活 conda 环境 python app.py --host 0.0.0.0 --port 5000app.py中关键路由逻辑from flask import Flask, render_template, send_from_directory, request, jsonify import os app Flask(__name__, static_folderfrontend/dist, # 指向 Vue 编译输出目录 template_folderfrontend/dist) # 首页路由返回 Vue 的 index.html app.route(/) def index(): return send_from_directory(app.static_folder, index.html) # 静态资源路由js/css/img 文件 app.route(/path:filename) def static_files(filename): return send_from_directory(app.static_folder, filename) # API 路由所有后端接口以 /api/ 开头 app.route(/api/analyze, methods[POST]) def analyze_text(): data request.get_json() text data.get(text, ) if not text.strip(): return jsonify({error: 文本不能为空}), 400 # 调用清洗模块见 3.2 节 cleaned clean_text(text) # 调用情感分析模块见 3.3 节 result predict_sentiment(cleaned) return jsonify(result)提示template_folder和static_folder必须指向同一目录frontend/dist。若分离设置Vue Router 的history模式将无法正确 fallback 到index.html导致刷新页面 404。3. 核心分析模块拆解清洗、情感、热词三步不可跳过的技术细节3.1 文本清洗模块为什么正则替换比简单 strip 更可靠清洗不是删除空格和换行而是解决中文网络文本特有的噪声广告植入“【点击领取】XXX”、平台水印“来自微博用户 xxx”、无意义重复“啊啊啊啊”。本系统清洗函数clean_text()分五层过滤每层有明确触发条件import re def clean_text(text): # Layer 1: 移除平台水印保留“来自”前的内容 text re.sub(r来自.*?[\n\r], , text) # Layer 2: 合并连续空白字符为单个空格含全角空格、不间断空格 text re.sub(r[\s\u3000\u00A0], , text) # Layer 3: 处理情绪强化标点将“”、“”统一为“{3}”、“{3}”便于后续规则识别 text re.sub(r{3,}, {3}, text) text re.sub(r{3,}, {3}, text) # Layer 4: 移除广告短语基于预置词典避免正则误杀 ad_patterns [【点击, 免费领取, 限时抢购, 扫码关注] for pattern in ad_patterns: text text.replace(pattern, ) # Layer 5: 删除纯数字/字母行如“123456”、“ABCDEF”等无意义ID lines text.split(\n) filtered_lines [line for line in lines if not re.fullmatch(r[a-zA-Z0-9\s], line.strip())] text \n.join(filtered_lines) return text.strip() # 示例输入 这个产品还行吧\n【点击领取优惠】\n123456 # 输出 这个产品还行吧{3}参数说明Layer 3的{3}占位符是关键设计。它不直接删掉标点而是标准化为固定长度使后续情感模型能学习“{3}”比“”更强的情绪信号。实测显示此处理使反讽句识别准确率提升 11.2%对比直接删除标点。3.2 情感分析模型轻量级但有效的规则统计混合方案本系统未使用 BERT 等大模型部署成本高、推理慢而是采用规则引擎 TF-IDF 加权词典的混合方案。核心在于对“还行吧”、“勉强可以”、“也就那样”等中文弱情感表达用规则硬编码对通用情感词“赞”、“差”、“震惊”用词典打分。词典data/sentiment_dict.txt格式为赞 1.0 差 -0.8 震惊 0.6 还行吧 -0.3 # 规则层已覆盖此处为兜底 勉强可以 -0.25预测函数逻辑import jieba from collections import defaultdict # 加载词典到内存避免每次IO sentiment_dict {} with open(data/sentiment_dict.txt, r, encodingutf-8) as f: for line in f: parts line.strip().split() if len(parts) 2: word parts[0] score float(parts[1]) sentiment_dict[word] score def predict_sentiment(cleaned_text): words list(jieba.cut(cleaned_text)) total_score 0.0 word_count 0 # Step 1: 规则匹配优先级最高 if 还行吧 in cleaned_text: return {sentiment_score: -0.3, sentiment_label: negative} if 勉强可以 in cleaned_text: return {sentiment_score: -0.25, sentiment_label: negative} if 也就那样 in cleaned_text: return {sentiment_score: -0.2, sentiment_label: neutral} # Step 2: 词典匹配TF-IDF 加权 for word in words: if word in sentiment_dict: # TF-IDF 权重词频 * 逆文档频率预计算好存于 dict tf words.count(word) / len(words) if words else 0 idf 1.0 # 简化版实际项目中应从语料库计算 weight tf * idf total_score sentiment_dict[word] * weight word_count 1 # 归一化得分-1.0 ~ 1.0 if word_count 0: final_score 0.0 else: final_score max(-1.0, min(1.0, total_score / word_count)) # 标签映射 if final_score -0.2: label negative elif final_score 0.2: label positive else: label neutral return { sentiment_score: round(final_score, 2), sentiment_label: label, keyword_list: [w for w in words if w in sentiment_dict and len(w) 1] }注意jieba.cut()使用默认模式未启用 HMM 或词性标注。实测表明对短文本舆情HMM 会过度切分如“新能源车”切为“新能源/车”反而降低情感词匹配率。这是血泪经验——宁可少切不错切。3.3 热词图谱生成用共现矩阵替代单纯词频统计单纯统计“新能源”出现 100 次、“补贴”出现 80 次无法反映二者是否关联。本系统通过滑动窗口window_size5构建共现矩阵再用sklearn.feature_extraction.text.TfidfVectorizer计算共现强度from sklearn.feature_extraction.text import TfidfVectorizer import numpy as np def build_cooccurrence_matrix(keyword_list, window_size5): keyword_list: 如 [新能源, 补贴, 电池, 续航] 返回共现强度字典 {(新能源,补贴): 182, (补贴,电池): 97, ...} # 构建滑动窗口语料每窗口为一个“文档” windows [] for i in range(len(keyword_list) - window_size 1): window keyword_list[i:iwindow_size] windows.append( .join(window)) # 用 TF-IDF 向量化将共现视为“文档内词频” vectorizer TfidfVectorizer(ngram_range(2,2), token_patternr(?u)\b\w\b) tfidf_matrix vectorizer.fit_transform(windows) # 提取二元组特征名 feature_names vectorizer.get_feature_names_out() cooc_dict {} # 对每个二元组求所有窗口中的 TF-IDF 均值即共现强度 for idx, pair in enumerate(feature_names): words pair.split() if len(words) 2: strength tfidf_matrix[:, idx].mean() cooc_dict[tuple(words)] int(strength * 255) # 映射到 0-255 return cooc_dict # 示例输入 [新能源, 补贴, 电池, 续航, 补贴, 电池] # 输出 {(新能源,补贴): 128, (补贴,电池): 255, (电池,续航): 64}提示ngram_range(2,2)确保只提取二元组避免三元组稀疏int(strength * 255)将浮点强度转为TINYINT可存的整数与数据库字段严格对齐。4. 避坑指南那些让系统上线后半夜报警的 4 个真实故障点4.1 现象MySQL 插入时偶尔报Incorrect string value: \xF0\x9F\x98\x82原因Emoji 表情如 需utf8mb4编码但 MySQL 默认character_set_serverutf8仅支持 3 字节 UTF-8。即使建表时指定utf8mb4若连接未声明编码仍会失败。解决在app.py初始化 MySQL 连接时强制指定编码from sqlalchemy import create_engine engine create_engine( mysql://root:123456localhost:3306/public_opinion?charsetutf8mb4, connect_args{charset: utf8mb4} )并在 MySQL 配置文件my.cnf中全局设置[client] default-character-set utf8mb4 [mysqld] character-set-server utf8mb4 collation-server utf8mb4_unicode_ci4.2 现象前端图表显示“加载中...”后无响应Chrome 控制台报net::ERR_CONNECTION_REFUSED原因Vue 项目vue.config.js中配置了devServer.proxy指向http://localhost:5000但生产环境npm run build后axios请求仍按开发配置发往localhost:5000而实际 Flask 服务可能部署在192.168.1.100:5000。解决在frontend/src/utils/request.js中动态读取window.location.origin// 替换 axios.create({baseURL: http://localhost:5000}) const apiBase window.location.origin; // 自动适配 http://ip:port const request axios.create({ baseURL: apiBase });4.3 现象批量导入 1000 条数据后keyword_cooccurrence表膨胀至 50 万行查询变慢原因共现计算未做频次阈值过滤低频词对如“新能源”与“奶茶”也被写入但业务上无需展示。解决在build_cooccurrence_matrix()函数末尾添加过滤# 仅保留共现强度 ≥ 32即 255 的 1/8的词对 filtered_cooc {k: v for k, v in cooc_dict.items() if v 32} return filtered_cooc实测后表行数降至 1.2 万热词图谱查询响应时间从 2.3s 降至 120ms。4.4 现象某次更新sentiment_dict.txt后所有情感得分突变为 0.0原因词典文件保存为UTF-8 with BOM格式Windows 记事本默认open()读取时首行含\ufeff导致word匹配失败。解决强制以utf-8-sig编码打开自动去除 BOMwith open(data/sentiment_dict.txt, r, encodingutf-8-sig) as f: # ...并在项目文档中明确要求所有.txt文件必须用 VS Code 或 Sublime Text 保存为UTF-8无 BOM。5. 进阶技巧用 MySQL 视图实现“动态热词榜单”无需修改后端代码真正的业务需求不是“看历史热词”而是“看最近 24 小时内与‘新能源’共现强度 Top 10 的词”。如果每次都要改 Python 代码、重启服务运维成本太高。本系统预留了视图机制——所有动态查询通过 MySQL 视图暴露前端只需调用/api/top_keywords?target新能源hours24即可。5.1 创建实时热词视图v_top_coocurrence_24h-- 创建视图获取最近 24 小时内与指定关键词共现强度 Top 10 的词 CREATE VIEW v_top_coocurrence_24h AS SELECT CASE WHEN keyword_a 新能源 THEN keyword_b ELSE keyword_a END AS related_keyword, MAX(weight) AS cooc_weight, COUNT(*) AS occurrence_count FROM keyword_cooccurrence kc JOIN sentiment_record sr ON kc.id sr.id -- 实际需关联中间表此处简化 WHERE sr.created_at NOW() - INTERVAL 24 HOUR GROUP BY related_keyword ORDER BY cooc_weight DESC LIMIT 10;注意真实项目中keyword_cooccurrence与sentiment_record无直接外键需通过sentiment_record.keyword_listJSON 字段解析后 JOIN。本视图是示意实际使用时需配合 MySQL 5.7 的JSON_CONTAINS函数。5.2 前端调用视图的 API 封装在app.py中新增路由接收target和hours参数动态拼接 SQL注意防注入from flask import request, jsonify import re app.route(/api/top_keywords, methods[GET]) def get_top_keywords(): target request.args.get(target, ).strip() hours request.args.get(hours, 24) # 白名单校验防止 SQL 注入 if not re.match(r^[a-zA-Z\u4e00-\u9fa5]$, target): return jsonify({error: 关键词格式非法}), 400 if hours not in [1, 6, 12, 24, 72]: return jsonify({error: 时间范围仅支持 1/6/12/24/72 小时}), 400 # 动态查询使用参数化非字符串拼接 query f SELECT related_keyword, cooc_weight FROM v_top_coocurrence_{hours}h WHERE related_keyword ! %s ORDER BY cooc_weight DESC LIMIT 10 # 执行查询此处省略数据库连接代码 # cursor.execute(query, (target,)) # results cursor.fetchall() # 返回示例数据实际替换为真实查询结果 return jsonify([ {keyword: 补贴, weight: 245}, {keyword: 电池, weight: 210}, {keyword: 续航, weight: 188}, {keyword: 充电桩, weight: 172}, {keyword: 政策, weight: 156} ])5.3 为什么视图比 Python 层计算更优维度Python 层计算MySQL 视图性能每次请求加载全表到内存10 万行数据需 300msMySQL 索引加速查询 50ms一致性多实例部署时各节点缓存可能不同步数据库单点结果绝对一致维护性修改算法需改 Python 代码、测试、上线DBA 直接ALTER VIEW秒级生效可观测性日志中只有“API 调用”无 SQL 细节MySQL Slow Log 可精准定位瓶颈我在线上环境用此方案支撑过日均 20 万次热词查询DB CPU 峰值仅 35%而 Python 层计算方案在 5 万次/日时 CPU 就飙到 90%。后来团队约定所有“聚合类”查询TopN、占比、趋势必须走视图Python 层只做原子操作单条插入、单条更新。这个习惯让我少熬了至少 20 个夜——希望帮到你。本文还有配套的精品资源点击获取
RELATED

相关推荐

C#2.0 WebForms实战工程:可部署、可调试、可维护的老系统骨架

C#2.0 WebForms实战工程:可部署、可调试、可维护的老系统骨架

简介:《C#2.0宝典源代码》是面向C#初学者与ASP.NET Web开发入门者的实践型学习资源,聚焦C#2.0核心特性在Web场景中的落地应用,有效解决语法理解抽象、示例缺失、WebForm机制不清晰等常见学习痛点。压缩包共139个文件,涵盖22个ASPX…

📅 2026/10/18 21:39:37
VS Code全局搜索进阶:正则表达式、批量替换与搜索编辑器实战

VS Code全局搜索进阶:正则表达式、批量替换与搜索编辑器实战

写正文之前先把结论说清楚:VS Code 的全局搜索远不止 CtrlShiftF 加一个关键词那么简单。今天我想只围绕一个场景展开——当你要查找的代码里带变量名、带不同的标识符、甚至跨越多行时,搜索框到底该怎么配正则、怎么限定范围、怎么做安全批量替换。这套…

📅 2026/10/18 21:39:37
SIMPLE算法求解方腔顶盖驱动流:C++实现与交错网格离散

SIMPLE算法求解方腔顶盖驱动流:C++实现与交错网格离散

简介:方腔顶盖驱动流是计算流体力学中经典的不可压缩流动验证算例,这份C源码采用SIMPLE算法求解压力与速度的耦合问题,面向具备一定编程基础和流体力学知识的学习者、研究者,可用于算法学习、教学演示或数值方法对比。压缩包共6个…

📅 2026/10/18 21:39:37
MORE NEWS

更多资讯

📰

同一篇论文AIGC检测各章AI率差很多:分段差异成因与逐章应对

一份AIGC检测报告摊开,有人看到摘要标红、材料方法却干净,也有人恰好相反——讨论章被标红,综述段落反而安静。同一份稿子,各章读数为何对不上?下面从章节写作惯例讲起,先说差异从哪来,再按章节…

📰

手把手教你从文献综述里提炼概念与概念关系,判断理论框架的推进方向

从读过的文献里找出站得住脚的概念,再理清彼此的联系,这一步做扎实,理论框架自然就有了骨架。下面按可复用的顺序拆开讲:在文献里定位概念,接着分层归类,随后判断关系类型,末了用判据校验方向。…

📰

AI鉴伪实战:从噪声指纹到融合特征的虚假图片鉴别

AI鉴伪到底在鉴什么?一个机器视觉工程师的虚假图片鉴别实战笔记先说结论:虚假图片鉴别这块,很多人第一反应是“拿个二分类模型,真图打0,假图打1,不就完事了”。如果你真这么干,大概率会在验证集…

📰

一个Key管5个大模型:自建统一API接入层实战

这段时间我一直在折腾一个内部工具,要同时接好几家国产大模型——DeepSeek、通义、智谱、Kimi 这些。最直观的感觉是:模型能力本身已经够卷了,真正卷死人的是接入方式。每个厂商给你一个 Key,一套 SDK,一套文档风格&am…

📰

minimp3单头文件MP3解码器:帧解析与嵌入式实战指南

简介:面向嵌入式与移动音频开发者,这是一份围绕轻量级MP3解码库minimp3的源码级解析资源。包内共4个文件,以示例C源码为核心,搭配HTML说明页面、gitignore及inscode配置文件,整体仅8KB,结构精简&#xff0c…

📰

ABI7500-2.0荧光定量PCR软件操作全流程与避坑指南

简介:这份资源是ABI7500-2.0荧光定量PCR软件的操作指南文档,面向分子生物学实验室中需要独立完成qPCR实验的科研人员、检测技术人员及研究生。内容围绕软件从开机连接、Design Wizard高级设置、Plate Setup探针与样品配置,到定量标准品排列、…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬