简介一份整合了电商产品评论数据采集、情感分析与主题挖掘的Python源码包以美的京东商品评论为样例数据面向想掌握中文文本情感分析完整流程的数据分析与NLP学习者可直接用于课程设计、毕业设计或评论文本挖掘入门练习。整个资源为rar压缩包共22个文件1个py主程序完成从数据读取、分词到情感判别的核心逻辑1个csv汇总原始评论数据另有20个txt分别存放停用词表、自定义词典、分词中间结果、正负面情感结果及LDA主题分析结果包体仅18.11MB结构清爽便于逐文件对照理解。资源目前已有4039人学习下载。配套代码与结果文件覆盖了评论预处理、分词过滤、情感极性判定、主题建模等关键环节还附带导入模块说明读者能快速还原实验环境并复现分析结果即使不熟悉中文分词也可借助各阶段txt结果直观了解每一步处理效果是学习电商评论文本挖掘的实用参考资料。1. 一套电商评论情感分析源码值不值得花时间复现拿到一套「电商产品评论数据情感分析 Python 源码.rar」很多人的第一反应是解压后去找模型训练入口结果翻半天发现目录里躺着的全是 CSV 和清洗脚本就懵了。这个现象很典型电商评论情感分析是个把数据清洗、中文分词、特征工程、模型训练、结果可视化串成一条线的小型 NLP 项目模型只是其中一段不是全部。它的核心价值在于你把任一家店铺的评论导出成 CSV跑一遍这套流程就能得到情感分布、差评主题词、随时间变化的舆情曲线直接支撑运营做差评预警和选品反馈。这种项目适合三类人刚学完 Python 语法想做点真实数据处理的数据分析新手需要给业务方做口碑量化汇报的运营或产品同学以及想低成本接触 NLP 完整链路、但还不想上 BERT 这类大模型的开发者。全文会按源码结构、数据清洗、模型训练、踩坑记录、结果下钻的顺序把每个环节讲透所有代码按最常用的落地方式写你照着敲就能跑。2. 拆开这套电商评论情感分析源码文件结构与建模选型2.1 典型源码包的文件结构先搞清楚每段代码是干什么的一份结构规范的中文电商评论情感分析源码包通常不是单文件脚本堆在一起而是按数据流分层排布。拿到 rar 后第一步不是跑代码而是打开目录看清分层我一般在虚拟机里用tree命令先扫一眼tree /path/to/comment_sentiment_analysis -L 2常见布局是 5 个模块加一个说明文档表格里列的是我复现过的典型划分路径模块名核心作用data/原始评论样本存放 CSV 或 Excel 评论数据字段至少要有评论文本、评分、时间process/清洗与分词正则去噪、jieba 分词、停用词过滤输出干净文本features/特征转换把分词结果转成 TF-IDF 向量或 Word2Vec 序列train/模型训练与评估训练情感分类器输出准确率、F1、混淆矩阵app/预测与可视化对新评论批量预测生成饼图、柱状图、词云README.md环境说明写明 Python 版本、依赖库、运行顺序这个分层的设计意图很明确清洗、特征、训练三个环节独立成模块意味着你可以只替换process/里的清洗逻辑不动模型代码就适配另一个平台的评论数据。很多新手把全部代码塞进一个.py结果换数据源时改一行要拖累整段流程这是最不值得踩的坑。2.2 建模选型三种情感分析路线各自适合什么场景电商评论情感分析在业界经历过几轮选型变化不同方案在解释性、数据需求和硬件成本上差异极大。常见路线是词典规则、传统机器学习、深度学习三条我按适用场景列了个对比表方案代表工具准确率区间成本门槛适用场景情感词典SnowNLP、BosonNLP 词典60%-70%极低普通笔记本快速看趋势、没有标注数据机器学习TF-IDF 逻辑回归/朴素贝叶斯75%-85%低纯 CPU 即可有一定标注数据、追求稳定可解释深度学习TextCNN、LSTM、BERT85%-92%中高需要 GPU数据量大、文本表达复杂、可接受黑盒这套标题为电商产品评论数据情感分析的源码走哪条路取决于压缩包里有没有现成标签。一个务实判断如果你拿到的评论数据自带 1-5 星评分先用评分映射成标签走 TF-IDF 加逻辑回归半小时跑通 baseline比一上来就调 LSTM 实在得多如果数据只有文本没有评分那就得先用 SnowNLP 批量打一个弱标签再做分类。我一般建议先从机器学习路线入手之后有余力再换 TextCNN这样每个环节的中间产物都能检查出了问题知道去哪查。2.3 评论数据字段与标签口径评分不是情感中间分必须单独处理电商评论数据字段听着简单实际打开文件会发现比想象中脏得多。一份典型的京东或天猫导出评论至少包含评论内容、评分、评论时间、商品属性这几列而评分和情感的关系并非线性对应这是最常见的理解偏差。我把标签映射的常见口径列成表评分值二分类口径三分类口径说明1-2 星负向负向明确不满问题集中在质量、物流、售后3 星负向不稳妥中立评分中性文本常是还行一般般4-5 星正向正向多数为满意评价如果按二分类直接映射3 星放正还是放负都会引入噪声。我处理过的一个真实案例里3 星评论中有大量带抱怨语气文本比如东西还行但快递太慢差评评分给了 3 星文本却是明确负面。所以更稳的做法是把 3 星单独划为中立用三分类模型如果业务方只需要看正负比例再把中立归入负向然后单独抽 3 星文本做人工复核。这个口径决策直接决定模型天花板必须在跑代码前定死。3. 清洗与分词链路把 10 万条中文评论变成模型吃得了的文本3.1 用 pandas 读入评论数据先解决编码和路径两个高频坑所有分析的第一步都是读数据而中文评论 CSV 最常见的坑是编码格式。Excel 导出的 CSV 是 GBKPyCharm 默认读 UTF-8两套编码遇上就是一片UnicodeDecodeError。不要用记事本手动转码直接在read_csv里指认编码import pandas as pd # enginepython 避免中文路径解析报错encoding 按文件实际编码调整 df pd.read_csv( data/comment_sample.csv, encodingutf-8-sig, # 带 BOM 的 UTF-8Excel 兼容性最好 enginepython, # 避免分割符识别异常 on_bad_linesskip, # 跳过格式错误的行防止一崩全崩 dtype{评分: Int64}, # 评分列强制整数类型空值变 NA ) print(df.shape) print(df.columns.tolist())这段代码里encodingutf-8-sig是给从 Excel 存出来的文件准备的utf-8-sig会自动吞掉 BOM 头如果你确认源文件是 GBK就改成encodinggbk。on_bad_linesskip值得单独解释pandas 2.0 之前用的是error_bad_linesFalse新版本改成了现在的参数名遇到引号不匹配、字段数不对的行会直接跳过而不是中断整个读取这对动辄几万行的评论文件是救命设置。读进来后先看shape和列名确认字段名是不是你预想的那几个再做后续处理。3.2 正则表达式清洗评论去 URL、去 HTML 实体、去重复标点原始评论文本里混着各种噪音包括https://t.cn/Ax3f...这种短链、 这类 HTML 实体、还有这类无意义重复标点。清洗的目的是把这些冗余信息剥离避免模型学到http这种和情感无关的垃圾特征。import re def clean_comment(text: str) - str: if not isinstance(text, str): return # 去 URL text re.sub(rhttps?://\S|www\.\S, , text) # 去 HTML 标签和实体 text re.sub(r[^]|[a-zA-Z];, , text) # 去重复标点连续 2 个以上的 !?~。压缩成 1 个 text re.sub(r([!?~。])\1, r\1, text) # 去掉多余空白字符 text re.sub(r\s, , text).strip() return text df[评论_清洗] df[评论内容].apply(clean_comment) # 清洗后可能产生空串统计一下丢弃量 empty_count (df[评论_清洗].str.len() 0).sum() print(f清洗后空文本数量: {empty_count})这里的正则有三个关键点URL 清洗用https?://\S会漏掉www.开头的链接所以补了www\.\SHTML 实体模式[a-zA-Z];能解决nbsp;、amp;但不处理数字实体如果不放心可以再叠加一条#\d;重复标点的压缩用了反向引用\1把连续出现的标点替换成单个这是防止分词阶段产生这种长度为 4 的标点 token 的有效手段。清洗完统计空文本数量如果超过 5%先检查是不是正则把正常文本误删了常见误删是长度很短的好评这类文本长度低于 2 但也表达情感不应该被丢弃。3.3 jieba 分词与停用词加载自定义词典是提升准确率最划算的一步分词是中文评论里承上启下的环节jieba 默认词典覆盖的通用词足够但电商评论有大量品类词和网络词比如到手价性价比客服这些词如果不加入自定义词典会被切得七零八落。加载自定义词典的代码一般在process/模块里单独成一个函数import jieba # 自定义词典每行一个词可带词频和词性格式为“词 词频 词性” jieba.load_userdict(data/userdict.txt) # 读取停用词表每行一个词 def load_stopwords(pathdata/stopwords.txt): with open(path, r, encodingutf-8) as f: return set(line.strip() for line in f if line.strip()) stopwords load_stopwords() def tokenize_and_filter(text: str) - list: # 精确模式分词不开启全模式 words jieba.lcut(text, cut_allFalse) result [] for w in words: w w.strip() # 长度小于2的词往往是单字情感信息少直接过滤 if len(w) 2: continue if w in stopwords: continue # 纯数字或标点组成的 token 不进特征 if w.isdigit() or not any(ch.isalpha() for ch in w): continue result.append(w) return result df[分词结果] df[评论_清洗].apply(tokenize_and_filter) print(df[分词结果].head(10))jieba.load_userdict的格式值得强调每行是自定义词 词频 词性词频可以不写让 jieba 自动推断但同一行用空格分隔不要用逗号否则加载不生效。停用词表用集合set存储原因是集合的in判断是 O(1) 复杂度评论量大时比列表快几个数量级。长度过滤len(w) 2会切掉这就但这类单字代价是丢掉服这种口语化单字评价我遇到这类情况会专门把业务高频单字加进保留名单比如值赞差在过滤时做一个if w in keep_words: result.append(w)的例外。3.4 空值、长评论与重复评论过滤策略决定样本质量清洗完文本后数据里还藏着几个会影响模型训练的问题空字符串、超长评论、完全重复的刷单式评论。空文本会在 Tokenizer 阶段直接报错超长评论如果不截断深度学习模型的内存占用会猛增重复评论往往是活动刷单产生的同文批量张贴会让模型在重复样本上过拟合。# 1. 去除空文本 df df[df[清洗后文本].str.len() 1].copy() # 2. 长评论按字符数做截断一般保留前 200 个字符足够 MAX_LEN 200 df[截断文本] df[清洗后文本].apply(lambda x: x[:MAX_LEN]) # 3. 去除完全重复的评价同用户同商品同文本视为刷单 df df.drop_duplicates(subset[用户ID, 商品ID, 评论内容], keepfirst) print(df.shape)截断长度选 200 是个业务经验值电商评论文本中 95% 以上在 200 字以内超出部分大多是凑字数或复述商品参数截断后情感信息几乎没有损失。drop_duplicates的subset参数必须同时包含用户、商品、文本三个维度因为不同用户对同一商品说很好是正常样本同一个用户反复说很好才是异常。这一步做完数据才真正到了可以喂给特征工程的干净状态。4. 训练一个能用的情感分类器从 TF-IDF 逻辑回归到深度学习进阶4.1 构造标签把评分映射成正负中三分类数据清洗完成后进入模型训练环节。第一步是把评分列转换成模型能学的标签向量。我建议按三分类处理即使业务只需要正负比例也先训三分类再合并中立到负向这样能保留更多信息。映射逻辑和代码都比较固定def rating_to_label(score): if pd.isna(score): return -1 # 异常值后面直接过滤 if score 2: return 0 # 负向 elif score 3: return 1 # 中立 else: return 2 # 正向 df[情感标签] df[评分].apply(rating_to_label) df df[df[情感标签] ! -1].copy() # 查看样本分布类别不均衡会直接影响后面模型评估 print(df[情感标签].value_counts(normalizeTrue))rating_to_label返回 -1 表示缺失或异常评分后续统一过滤这是为了避免模型学到缺失评分负向这种噪声规则。执行后输出value_counts(normalizeTrue)看比例如果正向超过 70%说明类别不均衡已经是事实后面训练必须加class_weight或者做下采样否则模型会偷懒地把所有样本都预测成正向准确率看起来高实际上毫无业务价值。4.2 TF-IDF 逻辑回归用 30 行代码跑通第一个 baseline传统机器学习的路线里TF-IDF 加逻辑回归是稳定性和解释性最平衡的组合。逻辑回归能输出每个特征的权重也就是哪些词在驱动负向判断这对电商业务方非常有说服力因为运营想知道用户到底在最常抱怨什么而不只是一个黑盒分数。from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.linear_model import LogisticRegression from sklearn.model_selection import train_test_split # 分词结果合并回空格分隔的字符串供 TF-IDF 直接使用 df[特征文本] df[分词结果].apply(lambda x: .join(x)) # 三次调参后我固定用这组参数过大模型会过拟合过小则欠拟合 vectorizer TfidfVectorizer( max_features5000, # 只保留出现频率最高的 5000 个特征控制维度 ngram_range(1, 2), # 同时考虑单个词和相邻双词能捕捉“不新鲜”“太慢” min_df2, # 至少在 2 篇评论中出现过过滤长尾噪声词 sublinear_tfTrue # 用 1log(tf) 平滑词频弱化高频词的绝对优势 ) X vectorizer.fit_transform(df[特征文本]) y df[情感标签] X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42, stratifyy ) # class_weightbalanced 让模型按类别频率反向加权解决样本不均衡 clf LogisticRegression( class_weightbalanced, max_iter1000, C1.0, solverlbfgs ) clf.fit(X_train, y_train)max_features5000是个典型的工程取舍电商评论文本去重后核心词大概只有两三千个截断到 5000 能保留完整表达又避免维度灾难。ngram_range(1,2)加入双词后能学到不新鲜物流慢这类组合情感副作用是特征维度会膨胀一倍所以我用min_df2把只出现过一次的随机组合排掉。sublinear_tfTrue是很多人忽略的参数它把原始词频做对数压缩防止很好这种高频词在向量里占比过大。C1.0是正则化强度的倒数如果你发现训练集准确率远高于测试集把 C 调小到 0.5 或 0.1过拟合会明显缓解。4.3 评估模型准确率会骗人F1 和混淆矩阵才是真相电商评论的正向比例通常在 70% 以上一个永远预测正向的模型能拿到 70% 准确率看起来不错实际对差评预警没有任何帮助。所以评估环节必须同时看 F1 和混淆矩阵我把评估代码和输出解读写在一起from sklearn.metrics import classification_report, confusion_matrix import numpy as np y_pred clf.predict(X_test) # target_names 要和标签数值对应否则报告解读会错位 print(classification_report( y_test, y_pred, target_names[负向, 中立, 正向], digits3 )) # 混淆矩阵的行是真实标签列是预测标签 cm confusion_matrix(y_test, y_pred) print(cm)一份健康的三分类结果里负向类别的 F1 至少要 0.7 以上才算可用。如果看到负向的召回率只有 0.3说明大量真实差评被模型当成了正向或中立这种模型上线后会漏报掉最关键的差评。混淆矩阵的解读顺序是看对角线是否明显大于同行的其他列如果第三列正向几乎吞掉了所有行那就是类别不均衡压倒了学习信号优先去查class_weight是否生效和训练数据里负向样本占比是否过低而不是急着调参。进阶一点可以用clf.coef_把负向类别权重最大的 20 个词打出来这些词往往就是差评关键词词典的雏形比如退货质量客服。我习惯把这个词表导成 CSV 交给运营他们可以基于业务经验直接补充新词再迭代回训练集。4.4 进阶路线换成 TextCNN 或 LSTM 需要注意的边界如果 baseline 的 F1 达到 0.8但业务方要求更高或者评论里大量出现心累绝了这类需要上下文才能判断情感的表达就该考虑深度学习模型。这里贴一段 TextCNN 的骨架代码用 Keras 写在train/textcnn_train.py里from tensorflow.keras.preprocessing.text import Tokenizer from tensorflow.keras.preprocessing.sequence import pad_sequences from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Embedding, Conv1D, GlobalMaxPooling1D, Dense, Dropout # 词表大小和序列长度是影响训练速度的核心参数 MAX_VOCAB 20000 MAX_SEQ_LEN 100 tokenizer Tokenizer(num_wordsMAX_VOCAB) tokenizer.fit_on_texts(df[分词结果]) X_seq tokenizer.texts_to_sequences(df[分词结果]) X_pad pad_sequences(X_seq, maxlenMAX_SEQ_LEN, paddingpost, truncatingpost) model Sequential([ Embedding(MAX_VOCAB, 128), Conv1D(filters128, kernel_size3, activationrelu), GlobalMaxPooling1D(), Dropout(0.5), Dense(3, activationsoftmax) ]) model.compile(optimizeradam, losssparse_categorical_crossentropy, metrics[accuracy]) model.fit(X_pad, y, validation_split0.2, epochs8, batch_size128)MAX_SEQ_LEN100是基于文本长度分布设定的中文评论平均长度在 30 到 50 个 token100 的截断能保留 98% 的信息同时控制 GPU 显存占用。paddingpost表示在句子尾部补零truncatingpost表示从尾部截断两者保持一致避免切断句首的主语。filters128, kernel_size3是 TextCNN 的常见配置kernel_size3对应三连词的局部特征窗口这个值太小学不到短语太大会引入噪声。训练时观察验证集 loss如果 epoch 3 后验证 loss 开始回升而训练 loss 还在降说明过拟合把Dropout(0.5)提到 0.6 或提前EarlyStopping。相比之下 LSTM 在评论长文本上有优势但训练速度慢一倍而且序列模型不容易解释特征。我的经验是评论超过 150 字的高价值数据用 LSTM大多数短平快的电商评论 TextCNN 更划算。5. 情感分析运行时的翻车现场5 个高频坑与排查方式5.1 翻车现象预测结果全是正向F1 报告里负向为 0这个现象几乎每个电商评论情感分析项目都会遇到一次。症状是模型跑完classification_report里正向着很高负向精确率和召回率全是 0混淆矩阵显示所有测试样本都被分到了正向。原因是训练集里正向评论占比超过 80%模型发现全预测正向就能拿到 80% 准确率于是偷懒走捷径。加上没有启用class_weight损失函数被大量正向样本主导负误差被淹没。解决分两步第一步在LogisticRegression里加class_weightbalanced让少数类样本的误差权重变大第二步检查训练集和测试集的stratifyy是否都在train_test_split里设置了。如果设置后负向 F1 仍然低于 0.6说明样本量太少优先扩充负向样本而不是改模型结构——去手动挑 1000 条 1-2 星评论搬进训练集比调任何参数都管用。5.2 翻车现象源代码能跑Excel 打开 CSV 全乱码训练完成导出结果时df.to_csv(result.csv)默认是 UTF-8 无 BOMExcel 会按本机 ANSI 编码中文环境是 GBK打开于是你看到的是一排浣犲ソ开头的乱码。这不是代码逻辑问题是编码兼容性问题解决方式是在导出时强制加上 BOM 头df.to_csv(result.csv, indexFalse, encodingutf-8-sig)utf-8-sig会在文件头部写入 BOM 标记Excel 能自动识别并切到 UTF-8 模式。注意到read_csv和to_csv都用utf-8-sig是一个好习惯全流程保持同一套编码口径能避免八成以上的字符问题。如果你的数据源本身就是 GBK那就统一用gbk走到底不要混用。5.3 翻车现象loss 一直降F1 却纹丝不动深度学习训练时出现训练 loss 正常下降、验证集 F1 却不涨的情况有点玄学但原因通常好查。最常见的是类别不均衡在深度学习模型里的放大效应sparse_categorical_crossentropy默认对所有类别的误差一视同仁多数类的梯度完全覆盖少数类导致模型只优化多数类。第二个高频原因是词表太大而训练轮次太少MAX_VOCAB20000但语料只有 3 万条评论很多词只出现几次Embedding 层根本学不好表示。解决方法是先用Tokenizer(num_words8000)冻结词表规模同时给model.fit传入class_weight参数。Keras 里用class_weight{0: 3.0, 1: 2.0, 2: 1.0}数字表示负向样本的损失放大倍数按照样本分布的反比来设。如果还是不涨检查pad_sequences前的分词列表是否为空分词结果全被停用词过滤掉时Embedding 层输入的 ID 全是 0模型等于在看一张白纸。5.4 翻车现象词云里全是这个东西真的看不出业务信息词云生成后高频词全是无意义的通用词这是停用词表覆盖不足的表现。电商通用停用词表要额外补充东西真的感觉但是现在这类口语高频词它们虽然不携带情感倾向但出现频率极高会占满词云核心位置。检查方法是打印 TF-IDF 向量中权重最高的 30 个词如果超过一半是这类词说明停用词表该扩了。我通常在data/stopwords.txt里追加 200 个电商评论高频废词比如客服物流它们在情感维度上是中性词意义要看上下文。停用词表也分场景正向词云和负向词云应该用不同的停用词表负向词云里保留客服能反映服务问题正向词云里保留物流则会稀释快的权重。另外加载停用词后要用jieba.suggest_freq(价格, tuneTrue)这类方法主动调整切分习惯避免性价比被切成性价和比。5.5 翻车现象时间字段解析异常按周聚合时报错评论时间字段经常是2025-01-15 10:23:45和2025/01/15混用直接pd.to_datetime解析靠系统推断一旦遇到空值或刚刚这类相对时间描述就会抛异常连带下游分组汇总报错。处理方式是先把时间统一成标准格式df[评论时间] pd.to_datetime( df[评论时间_raw], format%Y-%m-%d %H:%M:%S, # 严格格式解析比自动推断快且稳 errorscoerce # 解析失败置为 NaT不中断 ) df df.dropna(subset[评论时间]).copy() df[周次] df[评论时间].dt.to_period(W)errorscoerce是关键参数它会跳过无法解析的值并置空而不是整列报错。format指定严格模板后混合格式会更快暴露出来把手动清洗格式的步骤放在to_datetime之前而不是依赖 pandas 的自动推断。周聚合时.dt.to_period(W)返回的是周期对象直接配合groupby(周次)[情感标签].mean()就能得到每周情感均值的变化曲线这也是第 6 章要做趋势下钻的前置条件。6. 从准确率到业务结论情感结果的三种下钻方式模型跑通后别急着写报告把整体准确率汇报给业务方没意义他们会追问哪些商品差评最多差评集中在哪个星期大家都在抱怨什么。三种下钻方式按业务价值排序我依次讲。第一个维度是 SKU 下钻按商品 ID 聚合情感分布找出差评率异常的商品。实现方式是df.groupby(商品ID)[情感标签].mean()平均分接近 0 的商品就是重点排查对象。第二个维度是时间趋势用 5.5 节构造的周次字段做滚动均值看差评率是否在某个大促节点后跳升这能直接反映物流承压或品控下滑。第三个维度是主题词抽取对负向评论单独用CountVectorizer生成 TOP 词表并落到词云聚合出质量售后异味这类高频抱怨点。趋势分析加状态控制形成一条验证闭环# 负向评论 Topic 抽取只统计负向样本的高频关键词 neg_df df[df[情感标签] 0] from sklearn.feature_extraction.text import CountVectorizer cv CountVectorizer(tokenizerlambda x: x.split(), max_features20) cv.fit_transform(neg_df[特征文本]) print(cv.get_feature_names_out())我的个人习惯是任何模型结果交付前都抽 100 条预测样本人工复核正负各 50 条拿不准的算误判。第一次这么做你就会发现模型把不像以前好用了分成正向把包装也太简陋了分成了中立——这种边界误差只有人工能兜底。交叉验证是后悔药人工复核才是上车险。希望这一整套从清洗到落地的路径能帮到你少踩几个我踩过的坑。本文还有配套的精品资源点击获取