
简介面向计算机相关专业毕业生、课程设计学生及希望积累项目经验的 Python 学习者该毕业设计项目聚焦微博舆情分析场景利用 NLP 技术完成数据采集、文本清洗、情感判断与结果可视化可直接作为毕业设计或期末大作业的完整参考。项目源码经过导师指导并获 99 分代码结构完整、可运行压缩包共 223 个文件大小约 53.54MB包含大量 gif 动态演示、mp4 操作录屏、png/jpg 界面截图以及 CSS/JS 前端静态资源、Java/JSP/XML 后端逻辑文件、数据库配置与说明文档从界面样式到业务处理均有覆盖便于对照学习。资源已有 66 人学习数据资料齐全可帮助读者快速理解舆情分析系统的模块划分、实现流程与排错思路适合在短时间内完成环境搭建、功能复现与二次开发并便于快速定位问题。1. 微博舆情分析系统的“高分”出在哪不止是跑通一个NLP脚本看到“Python基于NLP的微博舆情分析系统源码全部数据资料高分毕设”这个标题先别把它当成又一个跑一遍就完事的算法 demo。这类项目的实际工作量大约七成不在情感分析模型本身而在从微博公开页面稳定地拿到数据、把口语化文本清洗成模型能吃的干净语料再把这些结果组织成一个能演示、能答辩、能讲清参数的系统。受众也不是单一角色正在找毕设题目的学生看的是完整链路怎么落地入门 NLP 的工程师看的是任务拆解和代码组织负责指导项目的人则想确认每个环节有没有可验证的记录。下面按数据采集、中文 NLP 建模、可视化展示和验收归档四段展开穿插可以直接套用的代码与参数。2. 从爬虫到数据集微博舆情分析的数据获取与合规落库2.1 公开搜索接口与页面解析先定数据源微博舆情分析的第一步是拿到底层语料。常见的做法是抓取m.weibo.cn移动端搜索接口它在浏览器登录状态下会返回结构化 JSON比解析 PC 端 HTML 稳定得多。需要提醒的是爬取行为应当遵守网站服务条款与 robots 约定仅采集公开可见内容、控制请求频率数据脱敏后只用于学习研究不要把账号 Cookie 或完整用户信息写进开源代码。import requests import time import random from typing import List, Dict HEADERS { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36, Referer: https://m.weibo.cn/, Cookie: YOUR_COOKIE_VALUE # 浏览器登录后复制切勿提交到公开仓库 } def fetch_weibo_by_keyword(keyword: str, max_page: int 5) - List[Dict]: results [] for page in range(1, max_page 1): url https://m.weibo.cn/api/container/getIndex params { containerid: f100103type1q{keyword}, page_type: searchall, page: page, } resp requests.get(url, headersHEADERS, paramsparams, timeout10) cards resp.json().get(data, {}).get(cards, []) for card in cards: if card.get(card_type) 9: results.append(card[mblog]) time.sleep(random.uniform(2, 5)) # 随机停顿降低服务端压力 return results这段代码把搜索关键词拼进containerid翻页参数由page控制。card_type 9对应正文微博卡片card[mblog]里已经带着完整字段无需正则抠取 HTML。随机sleep的作用是模拟人工浏览节奏避免固定频率触发风控这个参数在毕业设计里不是装饰答辩时会被追问。2.2 字段规整与 pandas 清洗留下建模真正要用的列mblog返回的字段很多但舆情分析真正需要的不过几类文本内容、发布时间、互动数值、来源设备、话题标签。常见错误是一股脑全存后面做 DataFrame 时到处astype报错。我一般在采集层就做裁剪只保留下表字段字段类型清洗说明idstr微博 ID去重用created_atdatetime解析成 UTC8 时间戳textstr带 HTML 标签的原始正文reposts_countint转发数参与热度计算comments_countint评论数attitudes_countint点赞数sourcestr来源设备如 iPhone 客户端topicstr正则抽取的话题词import pandas as pd import re def clean_field(raw: dict) - dict: text_html raw.get(text, ) topic re.findall(r#([^#])#, text_html) return { id: raw[id], created_at: pd.to_datetime(raw[created_at], format%a %b %d %H:%M:%S %z %Y, errorscoerce), text: re.sub(r[^], , text_html), # 去掉段落与超链接标签 reposts_count: int(raw.get(reposts_count, 0)), comments_count: int(raw.get(comments_count, 0)), attitudes_count: int(raw.get(attitudes_count, 0)), source: raw.get(source, ), topic: |.join(topic[:3]) if topic else , } def build_dataset(raw_list) - pd.DataFrame: df pd.DataFrame([clean_field(r) for r in raw_list]) df df.drop_duplicates(subsetid).reset_index(dropTrue) df df[df[text].str.len() 5] # 去掉纯图片或无意义短文本 return df清洗逻辑里最容易被忽略的是created_at的格式微博返回的英文星期格式需要对应解析模板。去掉 HTML 标签后再做长度过滤可以剔除大量“转发微博”搬运产生的空壳内容。2.3 存储选型MySQL 表结构与 MongoDB 备选采集量在十万条以下时MySQL 够用且便于答辩讲解 SQL 查询。建表时把互动数值设置为INT UNSIGNED正文用TEXT时间字段加索引后续做舆情时间线聚合会快很多。CREATE TABLE weibo_posts ( id VARCHAR(32) PRIMARY KEY, created_at DATETIME NOT NULL, text TEXT NOT NULL, reposts_count INT UNSIGNED DEFAULT 0, comments_count INT UNSIGNED DEFAULT 0, attitudes_count INT UNSIGNED DEFAULT 0, source VARCHAR(64) DEFAULT , topic VARCHAR(255) DEFAULT , sentiment TINYINT DEFAULT -1, -- -1 未标注0 负面1 中性2 正面 INDEX idx_created (created_at), INDEX idx_topic (topic) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;如果后续要做更多字段扩展比如存储评论列表、转发关系图MongoDB 的文档模型更适合不需要预先定义复杂的关联表。多数毕设场景下MySQL 加pymysql就够了想在架构说明里体现选型差异可以把 MongoDB 作为“数据量上升后的演进方案”写进对比段落。3. NLP 预处理与情感分析从中文分词到模型评估3.1 微博语料的清洗与分词emoji、 用户与网络新词3.1.1 清洗规则要按微博句式叠写通用分词教程里的清洗规则放到微博语料上常常不够用。微博正文包含话题符#...#、 用户、短链接、emoji 占位符和“笑哭”等平台表情这些对情感判定都是噪音。清洗时我会依次做四步正则替换顺序不能乱。import re import jieba def clean_for_nlp(text: str) - str: text re.sub(rhttps?://\S, , text) # 1. 先移除短链接 text re.sub(r#([^#])#, r\1, text) # 2. 话题词去 # 保留内容 text re.sub(r[\w\u4e00-\u9fa5-], , text) # 3. 删除 用户名 text re.sub(r\[[^\]]\], , text) # 4. 去掉 [哈哈] 这类表情占位符 return text.strip()先删链接再处理话题是因为微博短链接经常被包在话题文案后方顺序反了会留下http: 话题这类脏片段。用户名的正则中\w可以覆盖数字、字母和下划线配合[\u4e00-\u9fa5]才能完整匹配中英文混写的账号。3.1.2 自定义词典让 jieba 认识“绝绝子”分词模型对“绝绝子、无语子、栓Q、破防”这类高频网络词切分很差要么切成单字要么拆成无意义词。解决方案是维护一个自定义词典在jieba初始化时加载。# weibo_dict.txt 示例格式词语 词频 词性 # 绝绝子 50 nz # 无语子 60 nz # 破大防 30 v jieba.load_userdict(weibo_dict.txt) cut_words jieba.lcut(clean_for_nlp(这也太绝绝子了吧)) print(cut_words) # 输出类似[也, 太, 绝绝子, 了, 吧]自定义词典的三列分别表示词语、词频和词性词频用于影响切分优先级。越罕见的网络新词词频数字越要设置得比默认值高否则jieba仍会按隐马尔可夫模型猜词。每次新增一批语料我建议先跑一次jieba.lcut抽样检查切分结果再补充词典。3.2 情感判定词典法、机器学习与预训练模型的取舍3.2.1 词典法先跑一个 baseline没有标注数据时最务实的方案是情感词典加权。把文本切成词后与 BosonNLP 情感词典或知网 HowNet 词典匹配正面词加 1 分负面词减 1 分否定词翻转后一分。优点是零训练成本缺点是微博新词覆盖率低且无法处理复杂语境。3.2.2 有监督学习TF-IDF 加逻辑回归的典型参数要拿高分通常还是得走有监督学习。先人工标注约 800 条语料为负面、中性、正面三类然后构造 TF-IDF 特征用逻辑回归做分类。这个组合在中文短文本上速度快且每个特征对应的权重可以直接导出答辩讲可解释性时很好用。from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.linear_model import LogisticRegression from sklearn.pipeline import make_pipeline from sklearn.model_selection import train_test_split from sklearn.metrics import classification_report texts df[clean_text].tolist() labels df[sentiment].tolist() # 0 负面1 中性2 正面 X_train, X_test, y_train, y_test train_test_split( texts, labels, test_size0.2, random_state42, stratifylabels ) model make_pipeline( TfidfVectorizer(max_features20000, ngram_range(1, 2)), LogisticRegression(max_iter1000, C1.0, class_weightbalanced) ) model.fit(X_train, y_train) print(classification_report(y_test, model.predict(X_test)))max_features20000控制特征维度防止垃圾词干扰ngram_range(1, 2)把“不/惊艳”组合成“不惊艳”这类二元特征对否定表达很关键。class_weightbalanced则在三类样本数量不均衡时自动放大少数类权重。若训练后负面类 F1 明显偏低优先检查这一项。3.2.3 预训练模型到底要不要上BERT 类模型在情感分析上效果更强但微博舆情项目的数据量通常只有几千条直接微调预训练模型容易过拟合。我一般建议把 BERT 作为对比实验而不是主模型主模型用 TF-IDF 加逻辑回归保证结果稳定再用 BERT 微调跑一组实验证明“如果标注数据增加到 3000 条以上深度模型准确率还能再涨 5 个百分点左右”。这类梯度型实验安排比单一模型堆精度更接近高分答辩的逻辑。3.3 模型评估与可解释性不能只看准确率三类情感任务里类别分布往往偏向中性准确率容易被大量中性样本抬高。所以评估指标要同时看宏平均 F1 和每类的 precision、recall并观察混淆矩阵中负面与中性之间互相误判的情况。from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay import matplotlib.pyplot as plt cm confusion_matrix(y_test, model.predict(X_test)) ConfusionMatrixDisplay(cm, display_labels[负面, 中性, 正面]).plot() plt.savefig(confusion_matrix.png, dpi150, bbox_inchestight)混淆矩阵图在毕业设计论文里几乎是必须的。看输出时重点关注负面类有多少被分到中性那代表舆情预警的漏报率。若漏报多可以在训练数据里增加负面语料比例或者调整逻辑回归的class_weight而不是盲目调大max_iter。4. Flask 与 ECharts 搭建舆情看板热度曲线、情感占比与词云4.1 舆情指标定义热度、情感倾向与预警阈值模型输出只是整数标签系统里要展示的却是趋势。我一般把三个指标作为核心舆情热度、情感指数和负面占比。舆情热度采用加权归一化方式转发、评论、点赞按 3:2:1 加权再除以当前时段的最大值压到 0 到 100 区间。情感指数则按负面、中性、正面分别取值 20、60、90再按条数加权平均。负面占比是当日负面微博数占当日总量比例超过预设阈值比如 30% 时在界面上给出预警标记。指标计算方式说明热度指数归一化的转发数 3 评论数 2 点赞数 1衡量讨论规模情感指数分类均值映射后的加权平均衡量整体态度负面占比负面数量 / 当日总量预警主要依据4.2 Flask 数据接口与指标聚合后端我用 Flask 提供一份聚合接口前端 ECharts 直接拉取 JSON 绘制。聚合逻辑放在 SQL 层还是 Python 层数据量小时用 pandas 更方便因为可以边调边看。from flask import Flask, jsonify import pandas as pd app Flask(__name__) app.route(/api/overview) def overview(): df load_processed_data() # 读入清洗后的 DataFrame df[date] df[created_at].dt.date daily df.groupby(date).agg( count(id, count), neg_ratio(sentiment, lambda s: (s 0).mean()), heat(heat_score, mean) ).reset_index() return jsonify(daily.to_dict(orientrecords)) if __name__ __main__: app.run(host0.0.0.0, port8000, debugTrue)heat_score在入库时就算好避免每次接口请求重新计算一遍。neg_ratio直接取均值是因为情感标签里 0 表示负面mean()等价于负面占比。这个接口返回的是按天聚合的数组前端可以少做一层数据加工。4.3 ECharts 看板渲染时间序列、占比与词云前端页面我采用单页容器左侧放情感占比环形图右侧放热度趋势折线图底部放词云。ECharts 的series数据结构与 Flask 返回的 JSON 高度对齐几乎不需要二次遍历。fetch(/api/overview) .then(res res.json()) .then(data { const dates data.map(d d.date); const heat data.map(d d.heat); const chart echarts.init(document.getElementById(trend)); chart.setOption({ tooltip: { trigger: axis }, xAxis: { type: category, data: dates }, yAxis: { type: value, name: 热度指数 }, series: [{ type: line, data: heat, areaStyle: {} }] }); });词云使用echarts-wordcloud插件词语频率直接用jieba分词的统计结果。需要提醒的是默认词云组件会保留“我们、觉得、今天”这类高频但无语义词必须在传入前端前用停用词表过滤否则词云图会大篇幅出现虚词。5. 答辩前的最后一公里验证实验、环境锁定与资料归档5.1 三组对比实验让“高分”可论证毕设项目的评价往往来自你如何证明“我做的方案合理”。我建议把实验设计成三组词典法作为最低基线TF-IDF 加逻辑回归作为主方案BERT 微调作为上限参照。整理实验时要记录训练样本量、是否调参、运行时长等信息做成一张结果表。方案准确率宏平均 F1训练数据量说明情感词典加权0.610.56无标注数据新词覆盖不足TF-IDF 逻辑回归0.780.74800 条主模型稳定BERT 微调0.850.821500 条需更多标注5.2 python 环境安装与依赖锁定保证复现答辩机器上重新跑通是刚需环境问题经常毁掉演示。python 环境安装完成后用requirements.txt锁住依赖比口头说明“我本地跑得好好的”更有说服力。常见做法是导出全量包列表后手动删掉不需要的传递依赖避免体积过大。pip freeze requirements.txt python -m venv venv source venv/bin/activate pip install -r requirements.txtrequirements.txt里应直接锁定主依赖的版本下限例如pandas1.5,3.0同时把jieba、scikit-learn、flask、pymysql全部列入。README 中写清 Python 大版本要求比逐条解释依赖更重要因为jieba和pandas在不同 Python 版本下的轮子差异会影响安装体验。5.3 源码与数据资料的归档规范交付时不要把所有文件堆在根目录。“全部数据资料”的卖点在于组织清晰我将目录按如下结构归档weibo-sentiment-analysis/ ├── data/ │ ├── raw/ # 采集原始 JSON │ └── processed/ # 清洗标注后 CSV ├── src/ │ ├── crawler/ # 采集与入库 │ ├── nlp/ # Bert │ ├── web/ # Flask 服务 │ └── evaluate/ # 模型评估脚本 ├── docs/ │ ├── 开题报告.md │ ├── 需求分析.md │ └── 答辩PPT大纲.md ├── requirements.txt └── README.mdREADME.md里除了运行命令还要写明数据字段说明、标注规则和模型输出格式。答辩前把采集、清洗、建模、可视化每一步对应到上述目录的具体文件和运行结果截图按顺序贴进验收清单能省掉大半“你怎么证明你做了”的追问。本文还有配套的精品资源点击获取