简介面向自然语言处理与数据科学学习者这份电商产品评论情感分析Python源码包提供了从数据预处理到模型评估的完整实现。项目以电商产品评论为对象借助中文分词、停用词过滤、TF-IDF特征构建以及朴素贝叶斯、支持向量机等分类算法帮助掌握中文情感分析的核心流程并理解情感词汇库和特征工程的实际作用。压缩包共22个文件其中20个为txt说明文档、1个为Python主程序、1个为csv评论数据集整体大小18.34MB说明文档对导入模块、项目结构和关键步骤做了分步讲解主程序与数据集则对应可直接运行的代码和示例数据。目前已有85人学习下载。通过对照源码与数据集可以实际跑通评论情感极性判别任务并以此为模板迁移至其他商品评论场景进一步学习模型调参与可视化分析。1. 电商产品评论数据情感分析Python源码先想清楚三件事再解压看到“电商产品评论数据情感分析Python源码.rar”这个文件名先别急着找解压密码。你真正需要关心的是这份源码能帮你回答什么问题一批量产评论文本是正向还是负向、情绪强度如何、随时间怎么变化、哪些商品维度在被抱怨。这是电商运营里最常被用到的数据分析场景也是Python入门后第一个能把爬虫、pandas、sklearn串起来的完整工程。它适合两类人——正在学Python数据分析、想拿真实业务数据练手的学习者以及刚接手电商数据、需要快速输出评论洞察的运营。源码本身不是价值完整跑通并把数据替换成你自己的才是价值。下面按我实际做这类项目的顺序把整条链路拆开讲。2. 评论数据先要能跑起来清洗、分词与停用词的取舍2.1 字段取舍评论正文、评分和评论时间谁都不能丢电商评论导出后大多是Excel或CSV常见字段有订单号、商品ID、SKU属性、用户名、评分、评论内容、评论时间、追评时间、晒图数。做情感分析时我一般只保留五列评论ID、商品ID、评分、评论内容、评论时间。订单号和用户名不去碰一是涉及隐私二是对情感判断没有直接帮助。评分必须保留它是后续验证模型有没有学歪的“影子标签”——评分不完全等于情感但可以当弱监督信号用。清洗的第一个动作是去重。同一用户对同一商品短时间内的重复评论往往是系统重试或手滑造成的直接按内容去重。第二个动作是删除纯空评论和少于两个字的评论。第三个动作是规整时间字段pandas读进来时经常是字符串需要转成datetime后面按周、月聚合情绪走势才方便。import pandas as pd # 读取导出文件编码按实际情况调整 df pd.read_excel(comments.xlsx, engineopenpyxl) # 只保留情感分析相关字段避免无关列干扰 df df[[评论ID, 商品ID, 评分, 评论内容, 评论时间]] # 去重同一条评论内容重复出现时只保留一次 df df.drop_duplicates(subset[评论内容]) # 过滤空评论与无意义短文本 df df[df[评论内容].notna() (df[评论内容].str.len() 2)] # 时间字段规范化用于后续按周/月聚合情绪走势 df[评论时间] pd.to_datetime(df[评论时间], errorscoerce) df df.dropna(subset[评论时间])这段代码的逻辑是先把宽表收窄成五列再做内容去重和空值过滤最后统一时间格式。活儿很基础但这里有两个参数值得细说subset[评论内容]只按内容判重如果同一段话出现在不同商品ID下会被保留这对区分“同文案多商品”很有用to_datetime里的errorscoerce会把无法解析的时间变成NaN所以后面必须补一次dropna不然聚合时会报错或者把错误行算进去。2.2 中文分词的坑为什么默认选 jieba中文评论没有天然空格必须先分词。最常见的选择是jieba原因很实在pip install jieba一条命令装完词典覆盖日常用词支持自定义词典几十万条评论跑起来速度也扛得住。jieba有三种模式全模式会切出过多冗余词搜索引擎模式主要用于检索场景情感分析用默认的精确模式就行。电商评论里有很多平台特有词比如“百亿补贴”“开箱”“凑单”默认词典不一定认识会被切成更小的碎片情感词就可能被切散。解决办法是为自己这个品类加自定义词典把品牌名、商品名、平台黑话一次性塞进去。下面是分词和自定义词典的最小用法import jieba import re # 自定义词典每行一个词格式可以是“词 词频 词性” jieba.load_userdict(user_dict.txt) def cut_text(text: str) - list: # 去掉URL、用户名、话题标签等噪声 text re.sub(rhttps?://\S|\S|#\S#, , text) # 压掉多余空白保留中英文和数字 text re.sub(r\s, , text).strip() return [w for w in jieba.cut(text) if len(w.strip()) 0] sample 物流很快包装很严实但客服态度一般 print(cut_text(sample)) # 典型输出[物流, 很快, 包装, 严实, 客服, 态度, 一般]load_userdict的参数是词典文件路径文件里每行一个词可以带词频和词性比如“百亿补贴 100000 n”。cut_text里做了两层清理先把URL和艾特噪声去掉再把空白符压掉这样可以减少大量无意义的分词碎片。这里必须留个心数字货号如果不清理会被切分成单个字符进入向量化后占用大量特征位对模型是纯噪声。2.3 停用词表宁可多删不能少删停用词表对电商评论效果影响很大。“的、了、吗、然后、就是”这类无意义高频词如果不删词汇表会被撑大训练变慢泛化也变差。我一般用哈工大停用词表打底再手工加电商高频噪声词“这个、那个、感觉、反正、真的、确实”。注意“真的”在“真的很好”里是有强调作用的机械删掉会损失语气强度所以停用词表要结合后续建模方式灵活增删。停用词过滤放在分词之后、向量化之前代码很简单def load_stopwords(path: str) - set: with open(path, r, encodingutf-8) as f: return {line.strip() for line in f if line.strip()} stopwords load_stopwords(stopwords.txt) def filter_words(words: list, stopwords: set) - list: # 只保留不在停用词表里、且长度大于1的词 return [w for w in words if w not in stopwords and len(w) 1] sentence 物流很快包装很严实但客服态度一般 filtered filter_words(cut_text(sentence), stopwords) print(filtered) # 可能剩[物流, 很快, 包装, 严实, 客服, 态度, 一般]停用词过滤用的是集合查找复杂度O(1)几十万条评论跑下来也不慢。过滤条件里我手动加了len(w) 1因为单字词在情感分析里基本没有判别力留下反而制造噪声。建停用词表有个经验先跑一轮完全不加载停用词的版本把高频前200个词打印出来人工扫一遍把明显没意义的词加进去。网上抄的表能覆盖通用高频词但覆盖不了你自己品类里的问题词。3. 电商评论情感分析的三种建模路线从词典到预训练模型3.1 词典法无监督打分的适用边界拿到一批评论后如果不想标注数据最容易跑通的是情感词典法。思路是对每条评论分词后查情感词典正向词加分、负向词减分最后汇总成情感得分。电商场景里这套做法对“物流快”“质量差”“退货麻烦”这类直白表达效果不错但对反转表达几乎无效对“性价比超高”这种带程度修饰的表达也容易算错强度。我常用一个简化版打分结构给自己跑基线# 简化版情感词典正分和负分分别存储 pos_words {好: 1, 快: 1, 漂亮: 2, 划算: 2, 推荐: 1} neg_words {差: -1, 慢: -1, 失望: -2, 退货: -1, 假: -2} def dict_score(words: list) - float: score 0.0 for w in words: if w in pos_words: score pos_words[w] elif w in neg_words: score neg_words[w] return score text 质量很差再也不来了 print(dict_score(cut_text(text))) # 得分 -1词典法的问题一眼就能看出来词典太薄覆盖率低“不差”会被切分成“不/差”“差”扣分“不”被忽略最后得分为负实际情感却接近正面。所以词典法只适合两类场景一是没有标注数据时先跑出个可解释的基线二是和模型结果做交叉验证看模型是不是学偏了。如果真要用词典法上生产至少要把否定词、程度副词、转折词单独建模不是加几行词典就能解决的。3.2 有监督路线TF-IDF 逻辑回归的最小实现电商平台都有评分字段可以把它当弱标签用4分、5分标为正向1分、2分标为负向3分先丢掉或单独归为中性。这样做的好处是不用手工标注坏处是评分和真实情感存在错位——“好评返现”给的5分内容可能完全是负面吐槽。更稳的做法是先建一份1000条左右的人工标注集模型迭代一轮后再用全量评分做数据扩充。文本转数字这步最常见的是TF-IDF。逻辑回归是短文本分类里性价比最高的模型训练快、可解释、对高维稀疏特征效果好。下面是最小可运行实现from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.linear_model import LogisticRegression from sklearn.model_selection import train_test_split from sklearn.metrics import classification_report # corpus是清洗并分好词、用空格拼接的评论列表labels是对应的0/1标签 corpus [物流 很快 包装 严实, 质量 差 退货 麻烦] labels [1, 0] # 将分词后的文本转成TF-IDF特征 vectorizer TfidfVectorizer() X vectorizer.fit_transform(corpus) # 划分训练集和测试集固定随机种子保证结果可复现 X_train, X_test, y_train, y_test train_test_split( X, labels, test_size0.2, random_state42 ) # 逻辑回归做二分类max_iter调大避免收敛警告 clf LogisticRegression(max_iter500) clf.fit(X_train, y_train) print(classification_report(y_test, clf.predict(X_test)))TfidfVectorizer默认按空格分词所以分词后要用 .join(words)把词列表拼成空格分隔的字符串。这一步是新手最容易漏的直接传原始中文句子进去它会把整句话当成一个词。max_iter500不是越大越好只是让默认的lbfgs求解器有足够迭代次数收敛小数据上经常看到默认max_iter100直接报ConvergenceWarning。random_state固定是为了调参时每次对比公平不固定的话两次运行准确率可能差一两个点你会误以为是参数生效了。3.3 预训练模型路线用transformers做中文情感分类如果数据量到几十万条、预算也允许我会上预训练模型。中文场景最常见的做法是用transformers库加载一个开源中文情感分类模型做迁移学习。电商短文本任务里模型不需要微调也能有不错效果直接调用pipeline就能出结果from transformers import pipeline # 加载中文情感分类pipelinedevice0表示用GPU没有GPU就删掉这个参数 classifier pipeline( sentiment-analysis, modelIDEA-CCNL/Erlangshen-Roberta-330M-Sentiment, device0 ) result classifier(物流很快包装也很严实客服态度一般) print(result) # 输出是标签和置信度例如[{label: positive, score: 0.97}]pipeline调用门槛低如果环境没配GPU直接去掉device0让它在CPU上跑单条评论几十毫秒速度也能接受。但我不建议一上来就拿全量数据跑而是先跑1000条小样本看分布因为这种模型输出的标签粒度通常只有正向、负向、中性三档而电商评论里“质量不行但客服态度好”这类混合情感单标签模型表达不了。它解决的是语义理解问题解决不了多对象混合评价问题后者需要按维度拆。下面把三条路线放在一起对比方便你按自己的数据量选型路线是否需要标注训练成本可解释性适用场景词典法不需要极低强快速基线、交叉验证TF-IDF 逻辑回归需要弱标签或少量标注低中万级到十万级短文本预训练模型可以零样本高弱大数据量、语义复杂场景我的建议是没有明显标注数据时先用词典法把得分分布打印出来确认数据里有没有大量反转表达等有了评分字段做弱标签立刻切到TF-IDF加逻辑回归这个组合在多数电商评论上已经能到85%以上的F1预训练模型留到需要处理同义词、口语缩略、隐含讽刺时再上。4. 电商评论情感分析的四大常见坑现象、原因与排查记录4.1 否定词被分词切散情感翻转判断失败现象测试集准确率90%以上但“物流不是很快”被判成正向“没有想象中好”也被判成正向。人工复查发现模型看到的特征顺序是“物流/不是/很快”逻辑回归并没有把“不是”和“很快”组合起来理解。原因分词之后否定词和情感词变成两个独立特征大规模语料里“不是”和“很快”的共现次数远低于“很快”单独出现次数模型自然学会了“很快”正向忽略了前面的否定。解决在特征层把否定词和后面的情感词合并。常见做法是分词后加一条后处理规则——扫描分词列表遇到“不、没、没有、别、不太、不怎么”时把否定词和紧邻的下一个词拼成一个整体再做向量化。neg_prefix {不, 没, 没有, 别, 不太, 不怎么} def merge_negation(words: list) - list: merged [] skip_next False for i, w in enumerate(words): if skip_next: skip_next False continue if w in neg_prefix and i 1 len(words): merged.append(w _ words[i 1]) skip_next True else: merged.append(w) return merged这条规则的代价是会让词汇表变膨胀比如出现“不_好”“不_快”这类组合词但它们恰恰是情感分析里最值的特征。留意一个边界否定词和情感词之间如果隔着程度副词比如“不是很满意”“不是”后面的“很”才是要合并的对象这样“不是_很”就失去了语义需要再叠一层程度副词规则才能处理完整。4.2 程度副词和夸张表达把情绪强度拉偏现象词典法打分时“差”扣1分“超差”也扣1分和“差”没区别但“性价比超高”这种表达却被词典法算成中性因为“超”和“高”都不在词典里。原因词典法默认每个词权重相等程度副词没有参与计算有监督模型如果训练集里“超”类表达很少也学不会强度差异。解决维护一个程度副词词典给“超、太、巨、无敌、简直”设置强度系数在词典打分时乘以系数。下面是一个简单实现intensity {超: 1.5, 太: 1.3, 巨: 1.8, 无敌: 2.0, 有点: 0.6, 稍微: 0.5} def dict_score_with_intensity(words: list) - float: score 0.0 pending 1.0 for w in words: if w in intensity: pending intensity[w] elif w in pos_words: score pos_words[w] * pending pending 1.0 elif w in neg_words: score neg_words[w] * pending pending 1.0 return score这段代码的思路是遇到程度副词先缓存系数遇到下一个情感词时把系数乘上去然后重置。它可以堵住“力度”问题但堵不住讽刺表达比如“真是太好了”配上一个差评上下文词典法照样判正向。所以强度修正只推荐在词典法里用有监督模型反而可以通过加入强度特征自动学到。4.3 缺主语评论“质量一般但客服态度很好”怎么算现象一条评论同时包含商品负向和客服正向整体判成正向。运营按情感占比看结果商品改进方向完全被带偏。原因情感分类输出的是全局标签没有按评价对象切分。评论里“质量一般”说的是商品“客服态度”说的是服务两个对象不能混在一个标签里。解决按电商评价的固定槽位切分——商品质量、物流时效、客服态度、包装外观、价格优惠。常见做法是先做槽位抽取规则再对每个槽位单独打分。规则可以很朴素比如分词里出现“物流、快递、送货”就归物流槽位出现“客服、售后、店家”就归客服槽位slot_keywords { 物流: [物流, 快递, 送货, 收货, 配送], 客服: [客服, 售后, 店家, 卖家, 态度], 商品: [质量, 做工, 材质, 用, 穿, 吃], } def extract_slot_sentences(words: list) - dict: result {} current_slot None for w in words: for slot, kws in slot_keywords.items(): if w in kws: current_slot slot break if current_slot: result.setdefault(current_slot, []).append(w) return result槽位切分这段代码的问题是它只按关键词硬匹配情感词和对象词之间如果隔着其他描述槽位文本会被污染。比如“客服说可以退货但态度不行”里“客服”开头的句子混入了“退货”会误判成客服正向。想做得更细可以把句子按标点拆成短句每个短句独立归属槽位避免跨句串味。4.4 好评返现式假评论让整个数据分布失真现象某商品评分4.8情感分析正向占比95%但退货率却在涨。抽查后发现大量“质量不错客服很好值得推荐”的模板式评论明显是返现刷出来的。原因有监督模型学的是训练集分布。如果训练集里真实评论和刷单评论混在一起模型会把这些模板当成正常正向样本上线后遇到同样的短评照单全收。解决先做一层可疑评论过滤再按时间监控情感得分漂移。可疑评论有几个特征长度集中在15到25个字、好评出现时间高度集中、用词高度雷同。代码层面可以加一个重复度检查from collections import Counter def find_suspicious(comments: list, min_len: int 15, max_len: int 25, rate: float 0.1) - set: counter Counter() for c in comments: if min_len len(c) max_len: # 用整句做key近似模板评论 counter[c] 1 total len(comments) return {c for c, n in counter.items() if total 0 and n / total rate}这段逻辑很简单统计短好评里重复出现的句子占比超过阈值就标为可疑。问题是同一个真实爆款商品也可能出现大量“质量不错”的真实评价阈值要按商品单独调不能用全局比例一刀切。我见过更省事的做法是直接对比评分曲线和情感曲线评分没降但情感均值突然上升大概率是刷单高峰到了。5. 把结果变成可用的评价体系情绪走势与维度对比5.1 按时间聚合情感得分看活动结束后的口碑回落模型把每条评论打出情感得分后最常见的落地动作是按时间聚合看情绪走势。电商运营关心的是大促期间评价量暴涨但情感得分有没有跟着涨活动结束两周后真实使用反馈开始出现得分是否回落这些都需要把时间维度切到周或月来观察。# 假设df已经有评论时间和情感得分列得分区间0~1 df[评论周] df[评论时间].dt.to_period(W) trend df.groupby(评论周)[情感得分].agg([mean, count]) # 只看每周评论量大于20的样本避免小样本抖动 trend trend[trend[count] 20] trend[mean].plot(kindline, figsize(10, 4))to_period(W)把时间归到自然周比直接resample(W)更不容易踩时区坑。agg([mean, count])同时得到均值评论量和周评论量是监控口碑的两个核心指标均值代表情绪高低count代表声量大小。只看均值不看count很容易翻车某周只有两条差评就把均值拉得很低加了count阈值过滤曲线才稳定。5.2 按商品维度对比同款不同店铺的情绪差异电商运营另一个高频需求是竞品对比。同一款商品在两个店铺卖评论情感分布可能完全不同一个被夸“正品、靠谱”一个被骂“假货、售后差”。情感分析要能按商品ID或店铺ID分组输出对比视图而不是只有一个全局均值。# 按商品ID分组统计情感均值、正向占比和评论量 pivot df.groupby(商品ID)[情感得分].agg( 均值mean, 正向占比lambda x: (x 0.5).mean(), 评论量count, ).reset_index() # 只看评论量超过50的商品排序后取前10 pivot pivot[pivot[评论量] 50].sort_values(均值, ascendingFalse) print(pivot.head(10))这段代码里lambda x: (x 0.5).mean()是把连续得分转成正向占比的惯用写法阈值0.5可以按模型输出范围调。注意两个坑一是评论量阈值在不同品类不能共用高单价商品一个月可能只有20条评论但每条都有价值二是如果同一商品ID跨了多个店铺分组要改成[商品ID, 店铺ID]一起groupby否则两个店铺的情感会混在一起。5.3 词云和占比图的中文字体细节情感分析结果展示时词云是最直观的形式但新手几乎都会在中文词云上翻一次车图出来全是方块。原因是matplotlib和wordcloud默认字体不支持中文必须显式指定中文字体路径import matplotlib.pyplot as plt from wordcloud import WordCloud # Windows系统常见字体路径之一 font_path C:/Windows/Fonts/simhei.ttf wordcloud WordCloud( font_pathfont_path, width800, height400, max_words100, background_colorwhite, colormapviridis, ).generate( .join(positive_words)) plt.figure(figsize(10, 5)) plt.imshow(wordcloud, interpolationbilinear) plt.axis(off)font_path指定SimHei黑体Linux服务器上没有这个字体要换成/usr/share/fonts/下已安装的中文字体或者用fc-list :langzh先查系统有什么。max_words100控制词云显示的词数词太密时降到50更清爽。词云的展示价值高但分析价值有限它只能告诉你高频词不能告诉你这些词和情感的相关性所以它永远排在得分分布图之后而不是之前。6. 验证模型不翻车的最快方法样本抽检与误判回收6.1 抽检脚本先看预测错误的样本长什么样模型训练完我不会只盯着准确率数字看而是先抽一批预测样本做人工复核。具体做法是随机采样200条测试集结果把模型输出和真实标签并排打印自己一条条过。这一步能最快暴露数据问题和标注问题比任何评估指标都直观import random random.seed(2024) sample_idx random.sample(range(len(X_test)), 200) for i in sample_idx[:20]: true_label y_test.iloc[i] if hasattr(y_test, iloc) else y_test[i] pred clf.predict(X_test[i])[0] if true_label ! pred: print(错误样本真实标签:, true_label, 预测:, pred)抽检的目标不是提高准确率而是找出模型的“错误模式”。如果错的全是带否定词的句子回第4章补否定合并规则如果错的全是某品类专有名词回去加自定义词典。我早期拿到情感分析源码时习惯直接跑全量数据看整体准确率后来发现准确率再高也解释不了业务问题。现在只看对错样本一条条看改一轮跑一轮最多三轮效果比闷头换模型好得多。6.2 误判回收把错误样本变成下一轮训练数据抽检发现的错误样本不要丢统一收集成一份hard_cases.csv内容包含原文、真实标签、预测标签、模型置信度。下一轮训练时把这些样本加进训练集相当于让模型重点学习它不擅长的边界。这个流程在工程上叫错误驱动学习在电商评论场景里非常管用尤其对“好评返现”“又恨又买”这类复杂样本比自己重新标注几千条新数据高效得多。误判回收有两个注意点一是每轮用同样的随机种子切分数据集否则你没法判断指标提升是数据变化带来的还是模型变化带来的二是硬样本不宜大批量直接复制加进训练集同一个错误样本重复10次会造成过拟合加一次就够了。我一般每轮最多增加500条硬样本配合验证集观察过拟合程度。模型稳定之后如果你手头的数据里还带评论图片下一步值得尝试的是往多模态情感分析方向走用视觉模型提取图片中的商品状态和文本情感得分做融合能捕捉到“图片里明显开裂但文字说还行”这种文本单模态完全抓不到的矛盾信号。评论情感分析这门技术从原理上讲深可以写到几十章但从落地价值讲把清洗、建模、验证这四步做扎实已经足够支撑大多数电商口碑监控场景。希望这篇笔记帮到你至少在解压那个rar包时能判断出什么值得看、什么可以直接跳过。本文还有配套的精品资源点击获取