ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

电商评论情感分析毕设实战:从爬虫、scikit-learn到Hive与Django全链路

电商评论情感分析毕设实战:从爬虫、scikit-learn到Hive与Django全链路 简介这是一套面向高校学生与进阶学习者的电商评论情感分析毕业设计资源包基于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(rhttp[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, indexFalse)这段代码做了四件事去 HTML、去链接、压缩空白、合并重复标点。参数上str.len() 3这个阈值可以按数据情况调太短评论往往没有情感信息。清洗完先别急着分词把清洗前后的样本各抽几条看一眼确认没有把有用信息误删。2.2 中文分词和停用词处理中文和英文不一样英文按空格切就行中文得用分词工具。常用的是 jieba轻量、上手快。分词之后要去停用词像「的」「了」「是」这些对情感判断没帮助的词要去掉。停用词表可以用公开的也可以自己根据评论语料补。import jieba # 加载停用词 with open(stopwords.txt, encodingutf-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, indexFalse)这里有几个参数值得注意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_state42) sample[[clean_text, score, label]].to_csv(label_check.csv, indexFalse)抽样检查这一步别省。我见过太多人直接拿评分当标签训练结果模型学到的其实是「评分分布」而不是「文本情感」。如果发现某类矛盾样本特别多可以考虑把评分标签降级为参考改用人工标注一小批高质量数据做验证集。标签质量直接决定模型上限这一步花的时间值得。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_size0.2, random_state42, stratifydf[label] ) vectorizer TfidfVectorizer( max_features5000, # 最多保留 5000 个词 ngram_range(1, 2), # 考虑单字词和双字词组合 min_df3, # 至少出现在 3 条评论里才保留 max_df0.9 # 出现在超过 90% 评论里的词丢掉 ) X_train_vec vectorizer.fit_transform(X_train) X_test_vec vectorizer.transform(X_test)参数说明max_features5000控制特征维度太大容易过拟合太小会丢信息ngram_range(1,2)让模型能捕捉「不 好用」这种组合min_df3过滤低频噪声词max_df0.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(alpha0.1), lr: LogisticRegression(max_iter1000, C1.0), svm: LinearSVC(C1.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))alpha0.1是朴素贝叶斯的平滑参数越小越依赖数据C1.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按天分比如dt2025-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, indexFalse, headerFalse) conn hive.Connection(hostlocalhost, port10000, databaseecommerce) cursor conn.cursor() cursor.execute( LOAD DATA LOCAL INPATH /tmp/sentiment_result.tsv OVERWRITE INTO TABLE ecommerce.comment_sentiment PARTITION (dt2025-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 结果同步到 MySQLDjango 只查 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_length64) product_id models.CharField(max_length64) clean_text models.TextField() sentiment models.CharField(max_length16) prob_positive models.FloatField() prob_negative models.FloatField() prob_neutral models.FloatField() predict_time models.CharField(max_length32) dt models.CharField(max_length16) 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_idproduct_id, dtdt) total qs.count() positive qs.filter(sentimentpositive).count() negative qs.filter(sentimentnegative).count() neutral qs.filter(sentimentneutral).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 dt2025-01-01查不到数据。原因多半是分区字段没在 LOAD 时指定或者数据文件里的列数和表定义不一致。解决先SHOW PARTITIONS看分区有没有建上再SELECT * FROM comment_sentiment LIMIT 10不带分区条件查一下确认数据到底进没进。6.3 Django 查询 Hive 超时页面一直转圈现象Django 页面请求后长时间没响应最后超时。原因是每次请求都新建 Hive 连接或者查询没加分区条件全表扫。解决把 Hive 结果同步到 MySQLDjango 只查 MySQL如果非要直连 Hive加连接池并强制查询带dt分区条件。6.4 模型线上预测结果和离线不一致现象离线评估 F1 很高线上预测结果却很差。原因通常是线上用的向量器和训练时不是同一个或者文本预处理流程不一致。解决把向量器和模型一起保存线上加载同一个向量器预处理函数抽成公共模块离线和线上调用同一个函数。6.5 类别不平衡导致负面评论全漏现象负面评论召回率极低模型几乎全预测成正面。原因是负面样本太少模型偏向多数类。解决训练时加class_weightbalanced或者对负面样本过采样再或者调整分类阈值把正面概率阈值降低让更多样本被判为负面。7. 进阶技巧用模型概率做阈值调优和人工复核队列模型输出概率之后别急着按 0.5 一刀切。实际业务里正面概率在 0.4 到 0.6 之间的评论模型其实拿不准这部分可以单独拎出来做人工复核。我一般会设两个阈值高于 0.7 判正面低于 0.3 判负面中间的交给人看。这样既能保证自动判定的准确率又能把不确定的样本捞出来。def predict_with_threshold(text, model, vectorizer, low0.3, high0.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 页面出来点十条。这十个样本花不了几分钟但能挡住大部分低级错误。希望帮到你。本文还有配套的精品资源点击获取
返回列表