简介:这是一套面向高校学生与进阶学习者的电商评论情感分析毕业设计资源包,基于Python3.8、Django、Hive、MySQL5.7与Vue构建,可用于毕设、课程设计、大作业或工程实训。项目围绕TF-IDF与SVM、Word2Vec词嵌入结合CNN或LSTM等模型,对电商评论进行正负情感自动识别,并区分管理员与用户两类角色,提供数据管理、评分预测、系统设置、可视化界面与数据备份分析等功能。压缩包为zip格式,整体约24.16MB,内含可运行源码、sql文件与项目文档,源码负责前后端业务逻辑,sql文件用于数据库初始化,文档则辅助理解系统结构与部署流程。目前已有91人学习关注,适合希望掌握机器学习情感分类与Django全栈开发的学习者参考,可据此快速搭建实验环境、理解模型对比思路并完成项目落地。
1. 从一份电商评论到一张情感标签表:这套毕设到底在做什么
电商评论的情感分析,说白了就是把「好评」「差评」「中评」这种人工判断,换成模型自动打标。你手上有一批评论数据,可能是爬来的,也可能是平台导出的,字段无非是用户 ID、商品 ID、评分、评论正文、时间。评分能当弱标签用,但评分和真实情感经常对不上——有人打五星却写「凑合吧」,有人打一星其实是物流慢而不是商品差。所以真正要做的是:拿评论文本本身,训练一个分类模型,输出正面/负面/中性,再把结果落到 Hive 表里,最后用 Django 做一个能查询、能看统计的后台。
这套组合——机器学习做情感分类、Hive 存结果、Django 做展示——是电商评论分析里最常见的一条链路。它适合两类人:一类是正在做毕设、需要一条能跑通、能讲清楚、能答辩的完整流程;另一类是刚接触 Python 和机器学习,想找一个有真实数据、有前后端、有数据仓库的练手项目。难点不在模型本身,朴素贝叶斯或逻辑回归就够用,难点在于数据从哪来、中文怎么切、Hive 表怎么建、Django 怎么把 Hive 里的结果读出来。这篇就按这条链路,把每一步拆开讲清楚。
2. 数据从哪来、怎么洗:电商评论的采集与预处理
2.1 评论数据的三个来源和取舍
做情感分析,第一件事是确定数据来源。常见做法有三种:一是用 Python 爬虫抓公开商品页的评论,二是用平台开放接口或导出功能拿结构化数据,三是直接用公开数据集。爬虫最灵活,但要注意频率控制和页面结构变化;导出数据最省事,但字段往往不全;公开数据集干净,但和真实电商场景有差距。
我一般会先用爬虫抓一批,字段至少保留:评论 ID、商品 ID、评分、评论正文、评论时间。评分先留着,后面可以用来做标签校验。抓的时候不要贪多,先抓几千条跑通流程,再考虑扩量。评论正文里会有大量表情符号、HTML 标签、重复字符,这些都要在预处理阶段处理掉。
import re import pandas as pd def clean_comment(text): if not isinstance(text, str): return "" # 去掉 HTML 标签 text = re.sub(r"<[^>]+>", "", text) # 去掉 URL text = re.sub(r"http[s]?://\S+", "", text) # 去掉多余空白和换行 text = re.sub(r"\s+", " ", text) # 去掉重复标点,比如“!!!”变成“!” text = re.sub(r"([!?。,])\1+", r"\1", text) return text.strip() df = pd.read_csv("comments_raw.csv") df["clean_text"] = df["content"].apply(clean_comment) df = df[df["clean_text"].str.len() > 3] # 过滤掉太短的评论 df.to_csv("comments_clean.csv", index=False)这段代码做了四件事:去 HTML、去链接、压缩空白、合并重复标点。参数上,str.len() > 3这个阈值可以按数据情况调,太短评论往往没有情感信息。清洗完先别急着分词,把清洗前后的样本各抽几条看一眼,确认没有把有用信息误删。
2.2 中文分词和停用词处理
中文和英文不一样,英文按空格切就行,中文得用分词工具。常用的是 jieba,轻量、上手快。分词之后要去停用词,像「的」「了」「是」这些对情感判断没帮助的词要去掉。停用词表可以用公开的,也可以自己根据评论语料补。
import jieba # 加载停用词 with open("stopwords.txt", encoding="utf-8") as f: stopwords = set(line.strip() for line in f if line.strip()) def segment(text): words = jieba.lcut(text) # 过滤停用词、单字、纯数字 words = [w for w in words if w not in stopwords and len(w) > 1 and not w.isdigit()] return " ".join(words) df["seg_text"] = df["clean_text"].apply(segment) df[["comment_id", "seg_text", "score"]].to_csv("comments_seg.csv", index=False)这里有几个参数值得注意:len(w) > 1过滤单字,因为单字在中文里歧义太大;not w.isdigit()去掉纯数字,价格、型号这类数字对情感分类帮助有限。停用词表建议至少几百个词,覆盖常见虚词和电商场景里的无意义词,比如「包邮」「发货」这种如果和情感无关也可以加进去。分词完可以统计一下词频,看看有没有明显该去掉的噪声词。
2.3 标签怎么定:评分映射和人工校验
情感标签一般分三类:正面、负面、中性。最省事的做法是按评分映射,比如 4-5 星算正面,1-2 星算负面,3 星算中性。但前面说了,评分和文本情感经常不一致。所以映射完之后,要抽一批样本人工看一眼,把明显矛盾的挑出来。
def score_to_label(score): if score >= 4: return "positive" elif score <= 2: return "negative" else: return "neutral" df["label"] = df["score"].apply(score_to_label) # 抽样检查矛盾样本 sample = df.sample(50, random_state=42) sample[["clean_text", "score", "label"]].to_csv("label_check.csv", index=False)抽样检查这一步别省。我见过太多人直接拿评分当标签训练,结果模型学到的其实是「评分分布」而不是「文本情感」。如果发现某类矛盾样本特别多,可以考虑把评分标签降级为参考,改用人工标注一小批高质量数据做验证集。标签质量直接决定模型上限,这一步花的时间值得。
3. 用 scikit-learn 训练情感分类模型:从 TF-IDF 到模型评估
3.1 特征工程:TF-IDF 为什么适合评论短文本
评论数据通常是短文本,几十个字到一两百字。这种场景下,TF-IDF 加线性模型是性价比最高的方案。TF-IDF 的核心思想是:一个词在当前评论里出现越多、在整个语料里出现越少,它越有区分度。比如「好用」在好评里频繁出现,在差评里很少,它的权重就高。
from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test = train_test_split( df["seg_text"], df["label"], test_size=0.2, random_state=42, stratify=df["label"] ) vectorizer = TfidfVectorizer( max_features=5000, # 最多保留 5000 个词 ngram_range=(1, 2), # 考虑单字词和双字词组合 min_df=3, # 至少出现在 3 条评论里才保留 max_df=0.9 # 出现在超过 90% 评论里的词丢掉 ) X_train_vec = vectorizer.fit_transform(X_train) X_test_vec = vectorizer.transform(X_test)参数说明:max_features=5000控制特征维度,太大容易过拟合,太小会丢信息;ngram_range=(1,2)让模型能捕捉「不 好用」这种组合;min_df=3过滤低频噪声词;max_df=0.9过滤几乎每条评论都有的词。这些值不是固定的,可以按语料规模调,但建议先用这套跑一版基线。
3.2 模型选型:朴素贝叶斯、逻辑回归和 SVM 的对比
短文本分类常用三个模型:朴素贝叶斯、逻辑回归、线性 SVM。朴素贝叶斯训练快、对小数据友好,但特征独立假设太强;逻辑回归可解释性好,能输出概率;线性 SVM 在文本分类上通常效果稳定。我一般会三个都跑一遍,看验证集上的表现再定。
from sklearn.naive_bayes import MultinomialNB from sklearn.linear_model import LogisticRegression from sklearn.svm import LinearSVC from sklearn.metrics import classification_report models = { "nb": MultinomialNB(alpha=0.1), "lr": LogisticRegression(max_iter=1000, C=1.0), "svm": LinearSVC(C=1.0) } for name, model in models.items(): model.fit(X_train_vec, y_train) pred = model.predict(X_test_vec) print(f"--- {name} ---") print(classification_report(y_test, pred))alpha=0.1是朴素贝叶斯的平滑参数,越小越依赖数据;C=1.0是正则化强度的倒数,越大越容易过拟合。跑完看classification_report,重点看 macro F1 和各类的召回率。如果负面类召回特别低,说明模型把负面评论误判成其他类了,这时候要么补负面样本,要么调类别权重。
3.3 模型评估和保存:别只看准确率
准确率在类别不平衡时会骗人。比如 90% 是正面评论,模型全猜正面也有 90% 准确率,但负面一个都抓不到。所以要看混淆矩阵和各类的 precision/recall/F1。
from sklearn.metrics import confusion_matrix import joblib best_model = models["svm"] # 假设 SVM 表现最好 pred = best_model.predict(X_test_vec) print(confusion_matrix(y_test, pred)) # 保存模型和向量器 joblib.dump(best_model, "sentiment_model.pkl") joblib.dump(vectorizer, "tfidf_vectorizer.pkl")保存模型时一定要把向量器一起存,因为预测时要用同一个向量器做特征转换。很多人只存模型不存向量器,结果线上预测时特征对不上,这是血泪经验。混淆矩阵里如果发现某两类互相误判特别多,可以回头看看是不是标签定义本身有重叠,比如「中性」和「正面」边界模糊。
4. 把预测结果写进 Hive:建表、分区和批量写入
4.1 Hive 表设计:评论情感结果表怎么建
预测完的结果要落到 Hive 里,方便后续用 SQL 做统计和查询。表结构一般包含:评论 ID、商品 ID、评论正文、情感标签、情感概率、预测时间、分区字段。分区字段通常按日期分,方便按天查。
CREATE TABLE IF NOT EXISTS ecommerce.comment_sentiment ( comment_id STRING COMMENT '评论ID', product_id STRING COMMENT '商品ID', clean_text STRING COMMENT '清洗后评论', sentiment STRING COMMENT '情感标签', prob_positive DOUBLE COMMENT '正面概率', prob_negative DOUBLE COMMENT '负面概率', prob_neutral DOUBLE COMMENT '中性概率', predict_time STRING COMMENT '预测时间' ) PARTITIONED BY (dt STRING COMMENT '日期分区') STORED AS ORC;用 ORC 存储是因为压缩比高、查询快。分区字段dt按天分,比如dt=2025-01-01。建表时字段类型要和你写入的数据对齐,尤其是概率用 DOUBLE,别用 STRING,否则后面排序会出问题。
4.2 用 Python 批量写入 Hive:避开小文件坑
写入 Hive 常见做法有两种:一是用 PyHive 直接 insert,二是先写成本地文件再 load 进 Hive。直接 insert 方便但容易产生小文件,尤其是逐条写的时候。我一般会先把预测结果攒成一批,写成 TSV 或 CSV,再用LOAD DATA批量导入。
from pyhive import hive import pandas as pd # 假设 result_df 是预测结果 result_df["dt"] = "2025-01-01" result_df.to_csv("/tmp/sentiment_result.tsv", sep="\t", index=False, header=False) conn = hive.Connection(host="localhost", port=10000, database="ecommerce") cursor = conn.cursor() cursor.execute(""" LOAD DATA LOCAL INPATH '/tmp/sentiment_result.tsv' OVERWRITE INTO TABLE ecommerce.comment_sentiment PARTITION (dt='2025-01-01') """) cursor.close() conn.close()这里的关键是批量写,不要一条一条 insert。小文件多了之后,Hive 查询会变慢,NameNode 压力也大。如果数据量确实大,可以按分区攒够一定条数再写。另外LOAD DATA LOCAL INPATH是本地路径,如果是集群路径要去掉 LOCAL。写入前确认分隔符和表定义一致,否则会出现字段错位。
4.3 用 Hive SQL 做情感统计:好评率、差评关键词
数据进 Hive 之后,就可以用 SQL 做统计了。比如算每个商品的好评率、每天的情感分布、负面评论里的高频词。
-- 每个商品的好评率 SELECT product_id, COUNT(*) AS total, SUM(CASE WHEN sentiment = 'positive' THEN 1 ELSE 0 END) AS positive_cnt, ROUND(SUM(CASE WHEN sentiment = 'positive' THEN 1 ELSE 0 END) / COUNT(*), 4) AS positive_rate FROM ecommerce.comment_sentiment WHERE dt = '2025-01-01' GROUP BY product_id ORDER BY positive_rate DESC LIMIT 20;这个查询按商品分组,算好评率。ROUND保留四位小数,方便看。如果要做趋势分析,可以把dt放到 GROUP BY 里,按天看变化。负面关键词可以用 Hive 的 UDF 或者先导出再分析,但简单做法是直接在 Python 里对负面评论做词频统计。
5. Django 后台展示:把 Hive 结果接到前端
5.1 Django 项目结构和数据库配置
Django 这边要做的是:提供一个接口或页面,能按商品、按时间查情感统计结果。项目结构一般是sentiment_project下建一个analysisapp。数据库配置看你怎么接 Hive——常见做法是 Django 连 MySQL 或 SQLite 存业务数据,Hive 里的结果通过定时任务同步过来,或者 Django 直接通过 PyHive 查 Hive。
# settings.py 数据库配置示例 DATABASES = { "default": { "ENGINE": "django.db.backends.mysql", "NAME": "sentiment_db", "USER": "root", "PASSWORD": "your_password", "HOST": "127.0.0.1", "PORT": "3306", } }如果直接查 Hive,可以写一个工具函数封装 PyHive 连接,但要注意连接开销,别每次请求都新建连接。更稳的做法是定时把 Hive 结果同步到 MySQL,Django 只查 MySQL。这样查询快,也不影响 Hive 集群。
5.2 模型和视图:把情感统计做成可查询接口
在analysis/models.py里定义结果表,字段和 Hive 表对应。然后写视图,支持按商品 ID 和日期范围查询。
# models.py from django.db import models class CommentSentiment(models.Model): comment_id = models.CharField(max_length=64) product_id = models.CharField(max_length=64) clean_text = models.TextField() sentiment = models.CharField(max_length=16) prob_positive = models.FloatField() prob_negative = models.FloatField() prob_neutral = models.FloatField() predict_time = models.CharField(max_length=32) dt = models.CharField(max_length=16) class Meta: db_table = "comment_sentiment" indexes = [models.Index(fields=["product_id", "dt"])]# views.py from django.http import JsonResponse from .models import CommentSentiment from django.db.models import Count, Q def sentiment_stats(request): product_id = request.GET.get("product_id") dt = request.GET.get("dt") qs = CommentSentiment.objects.filter(product_id=product_id, dt=dt) total = qs.count() positive = qs.filter(sentiment="positive").count() negative = qs.filter(sentiment="negative").count() neutral = qs.filter(sentiment="neutral").count() return JsonResponse({ "total": total, "positive": positive, "negative": negative, "neutral": neutral, "positive_rate": round(positive / total, 4) if total else 0 })视图里用filter加count,注意加索引,不然数据量大了查询会慢。product_id和dt的联合索引能覆盖大部分查询场景。返回 JSON 方便前端用 ECharts 画图。
5.3 前端页面:用 ECharts 画情感分布图
前端可以用 Django 模板加 ECharts。页面加载时请求后端接口,拿到数据后渲染饼图或柱状图。
// 模板里的 script fetch(`/api/sentiment/stats/?product_id=${productId}&dt=${dt}`) .then(res => res.json()) .then(data => { const chart = echarts.init(document.getElementById('sentimentChart')); chart.setOption({ title: { text: '情感分布' }, series: [{ type: 'pie', data: [ { name: '正面', value: data.positive }, { name: '负面', value: data.negative }, { name: '中性', value: data.neutral } ] }] }); });这段代码请求接口后渲染饼图。注意productId和dt要从页面参数里取,别写死。如果要做实时推送,可以用 Django Channels 做 WebSocket,但毕设场景下定时刷新就够了,没必要上太重的东西。
6. 避坑与排查:这套链路最容易翻车的五个地方
6.1 分词后特征全空,模型训练报错
现象:训练时提示empty vocabulary或者特征矩阵全是 0。原因通常是停用词表太大,把评论里所有词都过滤掉了,或者分词结果全是单字被len(w) > 1滤掉。解决:先打印几条分词结果看看,确认停用词表没有误杀,单字过滤条件可以放宽到len(w) >= 1先跑通。
6.2 Hive 写入后查询为空,分区没生效
现象:LOAD DATA执行成功,但SELECT * FROM comment_sentiment WHERE dt='2025-01-01'查不到数据。原因多半是分区字段没在 LOAD 时指定,或者数据文件里的列数和表定义不一致。解决:先SHOW PARTITIONS看分区有没有建上,再SELECT * FROM comment_sentiment LIMIT 10不带分区条件查一下,确认数据到底进没进。
6.3 Django 查询 Hive 超时,页面一直转圈
现象:Django 页面请求后长时间没响应,最后超时。原因是每次请求都新建 Hive 连接,或者查询没加分区条件全表扫。解决:把 Hive 结果同步到 MySQL,Django 只查 MySQL;如果非要直连 Hive,加连接池并强制查询带dt分区条件。
6.4 模型线上预测结果和离线不一致
现象:离线评估 F1 很高,线上预测结果却很差。原因通常是线上用的向量器和训练时不是同一个,或者文本预处理流程不一致。解决:把向量器和模型一起保存,线上加载同一个向量器;预处理函数抽成公共模块,离线和线上调用同一个函数。
6.5 类别不平衡导致负面评论全漏
现象:负面评论召回率极低,模型几乎全预测成正面。原因是负面样本太少,模型偏向多数类。解决:训练时加class_weight='balanced',或者对负面样本过采样,再或者调整分类阈值,把正面概率阈值降低,让更多样本被判为负面。
7. 进阶技巧:用模型概率做阈值调优和人工复核队列
模型输出概率之后,别急着按 0.5 一刀切。实际业务里,正面概率在 0.4 到 0.6 之间的评论,模型其实拿不准,这部分可以单独拎出来做人工复核。我一般会设两个阈值:高于 0.7 判正面,低于 0.3 判负面,中间的交给人看。这样既能保证自动判定的准确率,又能把不确定的样本捞出来。
def predict_with_threshold(text, model, vectorizer, low=0.3, high=0.7): vec = vectorizer.transform([text]) proba = model.predict_proba(vec)[0] # 假设类别顺序是 [negative, neutral, positive] if proba[2] >= high: return "positive", proba[2] elif proba[0] >= high: return "negative", proba[0] else: return "review", max(proba)这个函数返回三个状态:正面、负面、待复核。待复核的评论可以写进另一张表,后续人工标注后再回流训练。阈值不是固定的,可以按验证集上的 precision/recall 曲线来定。如果业务更怕漏掉负面,就把负面阈值调低。
另一个技巧是把情感分析结果和商品评分做交叉验证。如果某个商品评分很高但负面评论占比也高,说明评分可能有问题,或者评论里有刷单。这种交叉分析在毕设答辩时是个加分项,能体现你不只是跑了个模型,还做了业务层面的校验。
我自己做这类项目最大的习惯是:每换一个环节,先抽十条数据肉眼过一遍。分词完看十条,标签映射完看十条,Hive 写完查十条,Django 页面出来点十条。这十个样本花不了几分钟,但能挡住大部分低级错误。希望帮到你。
本文还有配套的精品资源,点击获取