☰
电商评论情感分析Python实战:从清洗到建模全流程解析
2026/10/1 3:27:18 网站建设 项目流程

简介:面向自然语言处理与数据科学学习者,这份电商产品评论情感分析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", engine="openpyxl") # 只保留情感分析相关字段,避免无关列干扰 df = df[["评论ID", "商品ID", "评分", "评论内容", "评论时间"]] # 去重:同一条评论内容重复出现时只保留一次 df = df.drop_duplicates(subset=["评论内容"]) # 过滤空评论与无意义短文本 df = df[df["评论内容"].notna() & (df["评论内容"].str.len() >= 2)] # 时间字段规范化,用于后续按周/月聚合情绪走势 df["评论时间"] = pd.to_datetime(df["评论时间"], errors="coerce") df = df.dropna(subset=["评论时间"])

这段代码的逻辑是先把宽表收窄成五列,再做内容去重和空值过滤,最后统一时间格式。活儿很基础,但这里有两个参数值得细说:subset=["评论内容"]只按内容判重,如果同一段话出现在不同商品ID下会被保留,这对区分“同文案多商品”很有用;to_datetime里的errors="coerce"会把无法解析的时间变成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(r"https?://\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", encoding="utf-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_size=0.2, random_state=42 ) # 逻辑回归做二分类,max_iter调大避免收敛警告 clf = LogisticRegression(max_iter=500) clf.fit(X_train, y_train) print(classification_report(y_test, clf.predict(X_test)))

TfidfVectorizer默认按空格分词,所以分词后要用" ".join(words)把词列表拼成空格分隔的字符串。这一步是新手最容易漏的,直接传原始中文句子进去,它会把整句话当成一个词。max_iter=500不是越大越好,只是让默认的lbfgs求解器有足够迭代次数收敛,小数据上经常看到默认max_iter=100直接报ConvergenceWarning。random_state固定是为了调参时每次对比公平,不固定的话两次运行准确率可能差一两个点,你会误以为是参数生效了。

3.3 预训练模型路线:用transformers做中文情感分类

如果数据量到几十万条、预算也允许,我会上预训练模型。中文场景最常见的做法是用transformers库加载一个开源中文情感分类模型做迁移学习。电商短文本任务里,模型不需要微调也能有不错效果,直接调用pipeline就能出结果:

from transformers import pipeline # 加载中文情感分类pipeline,device=0表示用GPU,没有GPU就删掉这个参数 classifier = pipeline( "sentiment-analysis", model="IDEA-CCNL/Erlangshen-Roberta-330M-Sentiment", device=0 ) result = classifier("物流很快,包装也很严实,客服态度一般") print(result) # 输出是标签和置信度,例如:[{'label': 'positive', 'score': 0.97}]

pipeline调用门槛低,如果环境没配GPU,直接去掉device=0让它在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(kind="line", 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("均值", ascending=False) 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_path=font_path, width=800, height=400, max_words=100, background_color="white", colormap="viridis", ).generate(" ".join(positive_words)) plt.figure(figsize=(10, 5)) plt.imshow(wordcloud, interpolation="bilinear") plt.axis("off")

font_path指定SimHei黑体,Linux服务器上没有这个字体,要换成/usr/share/fonts/下已安装的中文字体,或者用fc-list :lang=zh先查系统有什么。max_words=100控制词云显示的词数,词太密时降到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包时,能判断出什么值得看、什么可以直接跳过。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询