
简介这是一套基于Python实现的情感分析系统源码包面向自然语言处理初学者或需要快速落地文本情绪判别的开发者可用于舆情监控、评论分析等场景。资源共34个文件以22个Python脚本为核心涵盖数据清洗、情感分类、Flask Web接口等模块另附7个txt语料与情感词典、1个xlsx验证数据、1个配置文件及README说明包体仅1.6MB结构清晰易于上手。已有77人学习下载。借助该项目读者可掌握SnowNLP等库的实际用法理解从原始文本清洗、模型训练到Web端情感分类接口的完整链路同时附带常见工具脚本与部署配置便于二次开发或改造为自用情感分析服务。1. 基于 Python 的情感分析系统从清洗源码到 Web 接口调用很多人拿到“情感分析系统”源码第一反应就是跑通接口、传一句文本、看返回结果。但如果直接这么做大概率会翻车——这套基于 snownlp 的项目核心并不在分类器本身而在它前面的数据清洗层和针对特定领域语料的重训流程。项目里带着neg_53kf.txt、pos_53kf.txt这类客服对话语料说明作者的真实意图是让模型适应 53KF 客服场景的消息风格而不是拿通用语料凑合。源码用 Flask 暴露分析接口支持情感标签和情感值返回对需要批量处理对话记录、舆情评论、客服质检文本的从业者来说是一个可以直接改动落地的框架。整套系统从prepareTrainData.py清洗原始数据到train.py训练 snownlp 模型再到 Flask 蓝图里的emotion_blueprint提供对外接口链路完整。适合有 Python 基础、想快速搭建一个私有情感分析服务的人——无论是做爬虫后的评论分析还是给内部系统加一个文本倾向判断能力。2. 拆解项目结构与数据清洗训练语料决定模型上限2.1 源码目录映射先搞清楚每个文件在链路里的位置解压 zip 后建议按功能分三条线去读数据准备、模型训练、Web 接口。我把核心文件整理成了下表方便对照你自己的需求做取舍。文件 / 目录所属阶段作用prepareTrainData.py数据准备清洗原始文本过滤 HTML、表情、非人工消息生成训练语料neg_53kf.txt/pos_53kf.txt数据准备原始正负向客服语料带标签neg_default.txt/pos_default.txt数据准备备用默认语料可能用于补充训练集clearMsg.py数据准备消息清理工具处理特殊字符train.py模型训练调用 snownlp 训练分类器并持久化emotionclassify.py模型训练 / 接口提供情感分类核心逻辑verify.py/verify_online.py验证本地 / 在线批量验证分类效果test_snownlp.py验证单独验证 snownlp 原始表现start_flask.pyWeb 接口Flask 启动入口flask_module/emotion_blueprint/Web 接口情感分析接口蓝图flask_module/config_blueprint/Web 接口配置管理接口蓝图config.ini配置语料路径、模型参数、服务端口等这个项目的思路很明确不直接拿 snownlp 默认模型上线而是先用真实场景语料重新训练。所以train.py和prepareTrainData.py的优先级高于 Flask 部分——接口只是个壳模型准不准才是核心。2.2 清洗逻辑实现去掉标签和表情只是第一步prepareTrainData.py里最值得看的是它的数据过滤规则。常见做法是先用正则去除 HTML 标签和超链接再按字符范围过滤 emoji最后根据消息长度和关键词判定是否属于“人工对话”。import re import pandas as pd from clearMsg import clean_message def prepare_corpus(raw_path, output_path): df pd.read_csv(raw_path, encodingutf-8, sep\t) cleaned_sentences [] for msg in df[message]: # 去掉 HTML 标签和脚本块避免网页噪音进入训练语料 text re.sub(r[^], , msg) text re.sub(rscript.*?/script, , text, flagsre.S) # 统一空白字符并过滤纯符号消息 text re.sub(r\s, , text.strip()) if len(text) 2: continue # 调用 clearMsg 中的规则过滤表情和特殊字符 text clean_message(text) if text: cleaned_sentences.append(text) with open(output_path, w, encodingutf-8) as f: f.write(\n.join(cleaned_sentences))snownlp训练时会把每行文本当作一条独立样本所以清洗后输出的是一行一句的纯文本文件不需要带标签。neg_53kf.txt这类文件名的后缀暗示它来自某个在线客服系统的真实会话这类短消息和新闻评论的句式差异很大——这也是为什么要重训而不是直接用预训练模型。参数层面有两个调整点一是len(text) 2的阈值如果语料里有大量单字回复如“好”“嗯”建议降到 1否则模型会丢失短文本特征二是clean_message里的表情过滤范围snownlp 内部分词对 emoji 会当作未知字符处理不清理会在贝叶斯计算时引入噪音。2.3 snownlp 默认模型的局限为什么必须自己训练snownlp 的好处是开箱即用、接口简单但它内置训练语料偏向电商评论像“质量很好”“物流快”这类表达。你拿客服对话去测比如“你们这个功能到底怎么用啊”结果往往偏向中性甚至错误。原因在于朴素贝叶斯分类器依赖词与标签的联合概率领域词汇在默认语料里出现频率太低先验概率被中性词拉走了。明白这一点就能理解项目里train.py的核心逻辑了。它不是简单调用snownlp.SnowNLP(text).sentiments而是先构造SnowNLP的贝叶斯分类器实例再用正负语料分别训练最后保存。3. 训练参数与分类器持久化让 snownlp 学会你的领域语言3.1 train.py 的执行过程从语料到可用的分类器snownlp的贝叶斯分类器不是直接暴露在公开文档里的但项目通过SnowNLP内部模块绕过了这个限制以Bayes类加载正向和负向语料执行训练。train.py的关键实现大致如下from snownlp import sentiment from snownlp import SnowNLP def train_model(pos_file, neg_file, model_pathsentiment.marshal): # 读取正负语料每行一条样本 pos_sentences open(pos_file, encodingutf-8).read().splitlines() neg_sentences open(neg_file, encodingutf-8).read().splitlines() # 实例化贝叶斯分类器并加载训练数据 bayes sentiment.Bayes() bayes.train(pd.Series(pos_sentences), pd.Series(neg_sentences)) # 保存训练结果后续 SnowNLP 会从该文件加载 bayes.save(model_path) print(模型已保存到, model_path)Bayes.train接收的是两个 pandas Series分别代表正负向样本。训练过程中它会对每条文本分词、统计词频、计算条件概率。save方法会把模型序列化到sentiment.marshal文件之后SnowNLP(text).sentiments会自动读取这个文件。这里有一个关键细节sentiment.marshal的路径是 snownlp 默认加载的模型文件。如果你训练完想保留原始模型需要先把原文件备份否则再跑SnowNLP就会用你的新模型。常见做法是训练时把模型保存为sentiment_custom.marshal然后手动替换snownlp/sentiment/目录下的默认文件。3.2 参数选择语料规模、正负比例与阈值调整训练效果主要由三件事决定语料量、正负比例、分类阈值。语料量上我一般建议正负各至少 2000 条以上否则贝叶斯概率估计的方差会很大测试集上看着还行一上真实数据就现原形。正负比例不必严格 1:1但偏差太大会让先验概率失衡——比如负向语料是正向的三倍模型会更倾向于输出负向。分类阈值调整的思路是sentiments返回 0 到 1 之间的浮点数默认取 0.5 为分界大于 0.5 判正向。项目里的emotionclassify.py在这个基础上做了三分类映射把得分映射为正向、负向、中性三种标签。中立区间的边界可以按业务调比如做客服质检你会希望“中性”区间小一点宁可模型表达不确定也不要硬分正负。3.3 重训后的保存策略防止覆盖与方便回滚在train.py里增加模型版本号是个好习惯能让你在切换语料后对比效果。比如model_path sentiment_v2.marshal bayes.save(model_path) # 备份当前线上模型 import shutil shutil.copy(sentiment.marshal, sentiment_v1_backup.marshal) # 切换到新模型 shutil.copy(model_path, sentiment.marshal)这个逻辑很实用因为 snownlp 在加载时会直接读取sentiment.marshal没做模型版本管理。如果你多训练几次没备份就再也回不到之前的版本了。4. Flask 接口调用与常见避坑上线前必须检查的五个问题4.1 启动 Flask 服务蓝图如何组织情感分析接口项目用 Flask 蓝图划分模块start_flask.py是入口通过注册emotion_blueprint暴露情感分析接口。启动逻辑如下from flask import Flask from flask_module.emotion_blueprint import emotion_bp from flask_module.config_blueprint import config_bp app Flask(__name__) app.register_blueprint(emotion_bp, url_prefix/api/emotion) app.register_blueprint(config_bp, url_prefix/api/config) if __name__ __main__: app.run(host0.0.0.0, port8080, debugFalse)host0.0.0.0让服务监听所有网卡这样局域网内其他机器也能调用。端口在config.ini里可以改如果 8080 被占用换 8000 或 5000 都行。debugFalse是必须的——Flask 调试模式会暴露 Werkzeug 调试器生产环境开 debug 等于把服务器钥匙交给别人。emotion_blueprint里的接口接收 JSON 或表单文本返回结果的结构大致是情感标签、情感值、分类方式。这个分类方式字段一般表示结果是来自 snownlp 的概率输出还是规则修正调试时很有用。4.2 调用方式与返回解析curl 和 Python 请求示例接口部署后直接用curl验证最快curl -X POST http://127.0.0.1:8080/api/emotion/analyze \ -H Content-Type: application/json \ -d {text: 你们这个功能太难用了弄了半天也没搞懂}返回的 JSON 里包含了情感标签和情感值。在 Python 里调用也很直接import requests response requests.post( http://127.0.0.1:8080/api/emotion/analyze, json{text: 这个方案看起来挺靠谱的我准备试试} ) result response.json() print(result[sentiment_label], result[sentiment_score])sentiment_score的数值范围是 0 到 1sentiment_label由项目内的三分类逻辑决定。批量场景下建议走 requests.Session 复用连接能明显减少握手开销。4.3 避坑记录编码、路径、模型加载与并发问题问题一训练时报 UnicodeDecodeError现象读取语料文件时出现utf-8 codec cant decode byte。原因项目里的neg_53kf.txt可能是 GBK 或 GB2312 编码直接按 UTF-8 读会崩。解决读取时指定encodinggb18030或者先用工具把文件统一转成 UTF-8。gb18030向下兼容 GBK/GB2312是处理中文老文件的安全选择。问题二训练后 sentiment 结果没变化现象重新跑了train.py但测试文本的得分和之前一模一样。原因snownlp 默认加载的是sentiment.marshal你保存的模型文件路径不对或者没覆盖到 snownlp 包内部的默认模型位置。解决确认bayes.save(model_path)的路径指向snownlp/sentiment/下的默认文件而不是当前目录下的随机文件。用绝对路径最稳妥。问题三Flask 接口响应很慢现象单条文本分析耗时几百毫秒甚至更久。原因snownlp 的sentiments属性每次调用都会重新分词和计算概率没有缓存机制。解决高频文本可以做结果缓存比如用字典或 Redis 存储已分析文本的结果。同一条消息重复提交时直接返回缓存值。问题四中性文本判决不稳定现象同样的文本多次调用一会儿正向一会儿中性。原因三分类逻辑是基于阈值区间映射的中文文本边界本身模糊阈值点附近微小概率波动就会跨区间。解决扩大中性区间比如0.35 ~ 0.65判定为中性保留更多不确定空间。不要追求所有文本都分出正负客服场景里“中性”本身就是有业务价值的答案。问题五模型文件是黑匣子不知道训练到了什么程度现象训练完想确认模型是否真的学进去了但看不到特征权重。原因snownlp 的 marshal 文件是序列化对象不是可读文本。解决用verify.py抽样测试一批已知标签的样本文本统计准确率和混淆矩阵比看文件可靠得多。这种“拿数据说话”的方式在模型迭代里应该成为习惯。5. 效果验证与进阶用法从批量回测到线上日志接入接口跑通只是起点判断这个系统是否可用要验证两件事一是模型整体准确率是否够用二是接口在真实调用压力下能否稳定运行。项目里verify_online.py的作用就是在线批量验证——读取一批待测文本逐条请求接口然后对照人工标注计算准确率。常用做法是准备一份带标签的测试集循环调用predict_sentiment函数def predict_sentiment(text): 返回情感标签和 0-1 情感倾向值 s SnowNLP(text) score s.sentiments if score 0.6: label pos elif score 0.4: label neg else: label neu return label, score阈值 0.6 和 0.4 是经验值你可以按业务场景调整。如果偏保守就拉大两个阈值之间的距离如果希望模型更有倾向性就收窄。然后用测试集跑准确率统计观察负向文本的召回率是否够用。进阶用法有三个方向。一是接入线上日志把每天客服会话或评论区文本批量写入待分析队列调用接口打标签后入库做趋势统计二是做文本相似度过滤——项目里有textSimilarity.py可以判断两条用户消息是否重复提问避免同一条负面反馈被重复计数三是把 snownlp 的分词器和情感分类器解耦对长文本分段分析取均值得到的整体情感倾向比直接输入全文更稳定。我自己的习惯是每次修改语料或阈值后都会固定跑一遍验证脚本记录准确率、召回率和耗时三个指标。有一次我重训后觉得模型变好了结果一跑验证负向召回率从 82% 掉到了 67%原因是我把负向语料扩充了一倍但没平衡正负样本比例。从那以后每轮训练我都强制走一遍完整验证不再凭感觉判断效果。希望这份源码的清洗思路和训练链路能帮你把情感分析真正落地到自己的数据上少踩几个翻车的坑。本文还有配套的精品资源点击获取