电商评论情感分析实战:基于Python词典法与LDA主题挖掘的完整流程
2026/9/24 22:23:09 网站建设 项目流程

简介:面向电商数据分析与自然语言处理初学者的完整Python项目,以京东美的产品评论为真实样本,演示从原始评论文本到情感极性判别、主题挖掘的经典流程。资源共22个文件,核心为Python源码和CSV汇总数据,同时配有多个中间结果TXT文本,覆盖评论采集汇总、中文分词、自定义词典与停用词过滤、正负面情感分类、LDA主题分析等关键环节,并附有导入模块说明,方便读者快速还原运行环境。压缩包整体仅18.11MB,结构清晰,从原始数据、处理脚本到结果输出逐步递进,适合用于课程设计、毕业设计或电商评论挖掘入门实践。目前已有4038人学习下载,读者可参照源码理解基于词典与统计的情感分析思路,也能迁移到其他产品评论数据进行复现和扩展,实用性强。

1. 电商产品评论情感分析:这份Python源码包能直接复现的完整流程

做电商数据分析的人应该都有同感:评论区是一座金矿,但也是一座垃圾山。几万条评论堆在那里,人工一条条看根本不现实,而单纯按星级判断用户态度又会被「空调质量很好但是安装师傅态度差」这种中评带偏。我拿到这份「电商产品评论数据情感分析Python源码」时,第一反应是看它到底走的是哪条技术路线——是训练神经网络,还是基于词典的经典情感分析。看完目录里的文件命名和中间产物,确认了这是一套基于情感词典 + LDA主题挖掘的完整流程,数据用的是京东美的空调评论。整套代码跑通后,你能得到三样东西:清洗好的语料、正负面情感分类结果、以及正负面评论各自的主题词分布。适合正在做文本挖掘课程设计、毕业设计,或者刚接触NLP想找一份完整可运行项目来拆解的人。

2. 数据预处理与词典配置:从原始评论到可计算的分词结果

2.1 目录结构与每个文件的定位

解压这份源码包之后,你会看到一堆txt文件和一个code.py。第一眼会有点懵,但如果按处理阶段把它们排一下,逻辑非常清晰。我把它们分成四组:原始数据、中间产物、词典资源、最终结果。

原始数据是meidi_jd.txtmeidi_jd_neg.txtmeidi_jd_pos.txt。前者是所有评论的汇总原始文本,后两个是把原始评论按「负面/正面」人工或规则分好类的参考集合。meidi_jd_neg_cut.txtmeidi_jd_pos_cut.txt是这两个集合分词后的版本,供后续LDA直接使用。

中间产物是meidi_jd_process_1.txtmeidi_jd_process_2.txtmeidi_jd_process_3.txt三个文件,对应三步处理流程:第一步是数据清洗,去掉重复、无效字符和过短文本;第二步是分词并去掉停用词;第三步是情感打分。

词典资源是myDict.txtstoplist.txt,前者是自定义领域词典,解决「美的」这类品牌词和产品专有名词被错误切分的问题;后者是停用词表,过滤掉「的」「了」「就」这类无实际语义的词。

最终结果是meidi_jd_process_end_正面情感结果.txtmeidi_jd_process_end_负面情感结果.txtmeidi_jd_pos_cut_LDA_result.txt,分别是情感分类后的正负面语料,以及对正面评论做LDA主题建模后输出的话题词分布。huizong.csv把所有评论和情感得分汇总成一张表,方便后续在Excel里做统计。

2.2 清洗与分词脚本:jieba加载自定义词典的写法

code.py是整个项目的核心脚本,预处理部分我拆出来看,主要做了三件事:读取原始评论、用正则清洗脏数据、用jieba分词并过滤停用词。核心代码如下:

import re import jieba # 读取停用词表,构建停用词集合 stopwords = set() with open('stoplist.txt', 'r', encoding='utf-8') as f: for line in f: stopwords.add(line.strip()) # 加载自定义领域词典,确保"美的""变频"这类词不被切碎 jieba.load_userdict('myDict.txt') # 清洗函数:去空白、去URL、去特殊符号、去除过短文本 def clean_text(text): text = re.sub(r'<[^>]+>', '', text) # 去HTML标签 text = re.sub(r'https?://\S+', '', text) # 去URL text = re.sub(r'[^\u4e00-\u9fa5a-zA-Z0-9]', ' ', text) # 保留中文、英文、数字 return ' '.join(text.split()) # 分词 + 去停用词 def cut_words(text): words = jieba.lcut(text) # 精确模式分词 return [w for w in words if w.strip() and w not in stopwords] with open('meidi_jd.txt', 'r', encoding='utf-8') as f: lines = [line.strip() for line in f if line.strip()] cleaned = [clean_text(line) for line in lines] # 过滤清洗后长度小于2的评论,减少无效样本 cleaned = [c for c in cleaned if len(c) >= 2] with open('meidi_jd_process_1.txt', 'w', encoding='utf-8') as f: for c in cleaned: f.write(c + '\n') with open('meidi_jd_process_2.txt', 'w', encoding='utf-8') as f: for c in cleaned: words = cut_words(c) f.write(' '.join(words) + '\n')

这段代码的逻辑并不复杂,但有两个地方值得注意。jieba.load_userdict('myDict.txt')必须在第一次分词之前调用,否则词典不生效。很多人喜欢在代码中间才想起来加载自定义词典,jieba已经完成了初始化,结果是词典里的词全部无效。另一个是jieba.lcutjieba.cut的差别:lcut直接返回列表,省去list()转换;cut返回生成器。在数据量大的时候cut更省内存,但小项目里lcut写起来更顺手。

正则清洗这里,[^\u4e00-\u9fa5a-zA-Z0-9]会把标点符号、表情符号全部替换成空格,然后split()把连续空格压掉。这一步对中文评论特别重要,因为评论区经常混入各种特殊符号,不去掉会影响分词质量。

2.3 自定义词典的格式与选词策略

myDict.txt是这份资源里很有价值的一个文件。它的格式是每行一个词,可以带词频和词性,比如「美的 1000 nz」表示「美的」是品牌名,词频参考值1000。jieba也支持不带词频的写法,直接写词就行。

这个词典里应该包含哪些词?我的经验是三个方向:品牌词、产品词、场景词。品牌词像「美的」「格力」「奥克斯」,产品词像「变频」「压缩机」「静音」,场景词像「安装师傅」「售后」「送货」。这些词如果不在词典里,jieba常常会把它们切开,比如「变频空调」可能被切成「变频」和「空调」,这不会导致程序报错,但会让后续的词频统计和LDA主题词出现碎片化。

有一个很实际的坑:词典文件必须是UTF-8编码,不能带BOM头。BOM是藏在文件开头的三个不可见字节,Windows记事本保存UTF-8文件时会自动加上。jieba读取带BOM的词典有时会忽略第一行,导致第一个词失效。我用Notepad++或VS Code打开myDict.txt另存为「UTF-8无BOM」格式,能避免这个玄学问题。

另外,词典不是越大越好,要把精力放在高频词上。你可以先不做任何词典配置跑一遍分词,把结果里被切碎的高频词收集起来,再补充进myDict.txt。这样迭代两三次,分词效果就稳定了。

3. 情感打分实现:词典法判定正负面的核心逻辑与代码

3.1 情感词典法的基本原理

这套代码没有用深度学习模型,而是用了情感词典打分的方式,这对小数据集来说是合理选择。原理很简单:准备一份正面情感词表和一份负面情感词表,扫描分词后的评论,每命中一个正面词加一分,命中一个负面词减一分,最后累计得分决定这条评论的情感倾向。得分大于0判为正面,小于0判为负面,等于0放入中性。

这个方法最明显的优点是快、可解释、不依赖GPU。每条评论为什么被判为正面或负面,你直接把命中的情感词列出来就能向别人解释清楚。缺点是难以处理否定结构和程度副词,比如「不怎么样」里的「不」和「怎么样」会被分开计算,如果「怎么样」不在情感词表里,「不」也被停用词过滤掉了,那这个表达就完全丢失。这是词典法的通病,不指望它做到完美,但用在一线业务场景里做整体趋势判断是够用的。

3.2 情感打分函数与正负面判定

meidi_jd_process_3.txt就是打完分的结果文件,每行对应一条评论及其情感得分。我重新组织了一份便于你理解的打分代码,结构与原项目一致:

# 正面情感词表与负面情感词表,实际项目中建议扩充到500词以上 pos_words = set(['好', '满意', '不错', '值得', '喜欢', '赞', '快', '安静', '省电']) neg_words = set(['差', '失望', '问题', '退货', '噪音', '慢', '投诉', '坏了', '漏水']) def sentiment_score(words): pos_count = sum(1 for w in words if w in pos_words) neg_count = sum(1 for w in words if w in neg_words) return pos_count - neg_count results = [] with open('meidi_jd_process_2.txt', 'r', encoding='utf-8') as f: for line in f: words = line.strip().split() score = sentiment_score(words) results.append((line.strip(), score)) # 按得分划分正负面 pos_lines = [line for line, score in results if score > 0] neg_lines = [line for line, score in results if score < 0] with open('meidi_jd_process_3.txt', 'w', encoding='utf-8') as f: for line, score in results: f.write(f'{line} [score={score}]\n') with open('meidi_jd_process_end_正面情感结果.txt', 'w', encoding='utf-8') as f: f.write('\n'.join(pos_lines)) with open('meidi_jd_process_end_负面情感结果.txt', 'w', encoding='utf-8') as f: f.write('\n'.join(neg_lines))

这里有几个参数值得你关注:情感词表的覆盖度决定了打分的上限。原项目里正负面词表比较精简,如果你要跑自己的数据集,建议从网上找一份通用情感词典,比如知网的情感分析用语集,再结合业务场景人工补充。另一个是得分阈值的设定,原代码把等于0的评论归入中性,不参与正负面分析,你可以把阈值改为>=1才算正面、<=-1才算负面,这样能过滤掉一批只有一两个情感词命中的边缘样本。两者取舍取决于你对精度的要求。

3.3 三个处理阶段的中间产物怎么对账

很多人在跑完代码后会有个疑问:过程文件有好几个,怎么确认处理流程没跑偏?我的习惯是对账行数。统计原始评论数,再统计process_1的行数,两者误差应该在合理范围内。process_1是清洗后的结果,如果发现少了很多行,很可能是清洗时把长评论误删了,检查一下len(c) >= 2这个阈值是不是设得太激进。然后对比process_2process_1的行数,分词阶段理论上不会丢行,如果行数不同,说明出现了空行被过滤的逻辑。

情感打分后的正负面数量加起来,应该大致等于process_1的总行数,剩下的差额是中性评论。如果你发现正面评论数量是负面的几十倍,先别急着高兴,很可能是情感词表里有高频词被误判了。我之前跑一个手机评论项目时,把「性价比」这个词加进了负面词表,因为有词法分析把它的后半部分「比」关联到比较类负向词,结果大量正面评论被分到了负面组。检查你的正负面比例是否符合业务直觉,这是一个非常重要的合理性验证。

4. 主题挖掘:用LDA从情感结果里找用户真正的槽点

4.1 LDA的输入输出与代码实现

情感分析只告诉你用户是满意还是不满意,但用户到底在满意什么、不满意什么,需要主题模型来回答。这份源码里对正负面评论分别做了LDA(Latent Dirichlet Allocation),输出结果在meidi_jd_pos_cut_LDA_result.txt里——文件命名已经明说了,是对正面分词结果(pos_cut)做的LDA分析。LDA的作用是把一堆文档自动归纳成若干个主题,每个主题由一组高频词构成。

from gensim import corpora, models # 读取正面评论的分词结果,每一行是一条"文档" with open('meidi_jd_pos_cut.txt', 'r', encoding='utf-8') as f: texts = [line.strip().split() for line in f if line.strip()] # 去掉只在一条评论里出现的词,减少噪音 from collections import defaultdict frequency = defaultdict(int) for text in texts: for token in text: frequency[token] += 1 texts = [[token for token in text if frequency[token] > 1] for text in texts] # 构建词袋和TF-IDF向量 dictionary = corpora.Dictionary(texts) corpus = [dictionary.doc2bow(text) for text in texts] # 训练LDA模型 lda = models.LdaModel(corpus, num_topics=5, id2word=dictionary, passes=20, random_state=42) # 输出每个主题的前10个词 for idx, topic in lda.print_topics(num_words=10): print(f'Topic {idx}: {topic}')

这里有几个参数是调出来的经验值。num_topics=5表示把评论归纳成5个主题,这个数字不是拍脑袋定的,而是分别跑了5、8、10、15个主题后,人工看主题词的可解释性确定的。主题数太多会出现多个主题高度重叠,主题数太少则会把「质量」和「安装」混在一起。passes=20是LDA迭代次数,太小模型不收敛,太大训练时间成倍增加。中等规模的数据集(几千条评论)20次足够。random_state=42是一个容易忽略但极其重要的参数,不加这个参数每次运行结果都不同,你会以为是代码有bug,其实就是随机性问题,固定随机种子后结果可复现。

4.2 主题数选择的经验方法

选择主题数是LDA建模里最玄学的一环,但可以用两个办法降低玄学程度。第一个是困惑度曲线,横向比较主题数为3到20时模型的困惑度,找到曲线拐点。第二个更实用,直接跑出结果后人工看主题词的连贯性,如果某个主题下的10个词里有两三个明显不相关,说明主题数太多或太少,调整后再跑。

对这份数据来说,跑出来的主题大致会对应:安装服务(安装、师傅、收费、态度)、产品性能(制冷、静音、变频、省电)、物流配送(送货、速度、包装)、客服售后(售后、客服、退换)、价格(性价比、活动、优惠)。如果你发现结果里出现「的、了、是」这种词,说明停用词表没覆盖全,回到第2章的流程去补停用词。LDA对输入很敏感,前面任何一步偷懒都会在主题结果里暴露出来。

4.3 从主题词回看业务含义

拿到主题词之后,要把它和情感判断结合起来看才有意义。正面的主题词集中在「静音、省电、制冷效果好」,说明产品的核心卖点是符合用户预期的;负面主题词如果集中在「安装收费、送货慢」,说明问题出在服务环节而不是产品本身,这对电商运营来说是重要的决策依据。整套资源跑完的最终产出,不是一堆txt文件,而是一份「用户到底在满意什么、不满意什么」的结构化洞察。

5. 避坑与排查:编码、否定词、随机种子五类高频翻车现场

这一章总结我在运行和复现这套源码时遇到过的典型问题,按「现象 → 原因 → 解决」的格式写清楚,希望能节省你排查的时间。

5.1 读取txt文件报UnicodeDecodeError

现象:用Python直接open('meidi_jd.txt')读取时报UnicodeDecodeError: 'gbk' codec can't decode

原因:Windows系统默认编码是GBK,而这份资源里的txt文件是UTF-8编码。open()没指定编码时用了系统默认编码,遇到UTF-8里的中文字符就会解码失败。

解决:所有open调用统一加上encoding='utf-8'。如果你要把结果在Excel里打开,注意用Excel的「数据—自文本」导入并选择UTF-8编码,直接双击打开会乱码。我在跑通流程后把所有文件的编码统一成了UTF-8,避免后续处理反复踩坑。

5.2 自定义词典加载了但分词没变化

现象:myDict.txt里明明加了「美的」这个词,分词结果还是被切成「美」和「的」。

原因:jieba.load_userdict()调用时机不对,加载前jieba已经用了默认词典完成了初始化缓存。还有一种可能是因为缓存,jieba默认会缓存上次的分词结果,修改词典后直接重跑,它优先用了缓存。

解决:示例代码把load_userdict放在第一次分词之前,但如果你是在交互式环境或者Jupyter里测试,之前已经执行过分词,需要重启kernel。或者在调用前加一行jieba.initialize()强制重新初始化。另外,检查词典文件第一行是否有BOM头,用VS Code底部状态栏确认编码是「UTF-8」而不是「UTF-8 with BOM」。

5.3 「不推荐」被拆成「不」和「推荐」,情感极性反转

现象:一条评论写着「不推荐购买」,情感打分却是正面,因为在情感词表里命中了「推荐」。

原因:词典法不做否定句处理,否定副词「不」「没」「无」在停用词阶段被过滤掉了,剩下「推荐」被当成正面证据。

解决:最省事的方法是看把「否定词+情感词」的组合加入情感词表,比如建一个带否定的负面词表,包含「不推荐」「不满意」「不行」「不好用」。更完备的做法是在打分前加一层否定检测,遍历分词结果,如果当前词是「不」「没」「别」且下一个词是情感词,就把得分取反。这个改动不复杂但对结果影响很大,值得花时间做。

5.4 LDA每次运行结果不一样,前后对不上

现象:同一个脚本连续跑两次,meidi_jd_pos_cut_LDA_result.txt里输出的主题词不一样。

原因:LDA在预计的迭代中引入了概率采样,如果没有固定随机种子,每次运行的初始状态都不同,最终收敛到的最优解也在合理范围内有波动。

解决:在LdaModel初始化时加random_state=42,或者random.seed(42)。这是让实验可复现的基本操作。如果你在论文或课程设计里要展示结果,建议固定随机种子,不然导师让你重新跑一遍你会发现结论文本都对不上。

5.5 情感得分全为0,正负面结果文件是空的

现象:process_3里所有评论的得分都是0,正面和负面结果文件内容为空。

原因:情感词表与分词结果匹配不上。比较常见的是情感词表里存的是带词性的格式,比如「好/a」,而分词结果是纯词,匹配时永远不相等。或者情感词表编码错误,读取时内容乱码。

解决:写一个十行的测试脚本,载入情感词表后打印前20个词看看内容是否正常。然后取一条明确是好评的评论,打印出分词结果,人工检查词表里有没有对应情感词。比如分词结果是「静音 效果 好」,而词表里只有「安静」没有「好」,那这条的得分当然是0。发现问题后扩充词表即可。

6. 结果验证与复用进阶:把模型从美的空调扩展到任意品类评论

拿到这份源码跑通流程只是第一步,怎么验证结果可靠、怎么把流程复用到自己的数据集上,才是这份资源真正的价值所在。

先聊验证。我建议你从原始评论里抽300条人工标注正负面,再拿代码跑出来的情感分类结果做对比,算准确率。300条看起来不多,但人工标注一小时能完成,却能把整个流程的可信度拉高一个量级。计算方式很简单:

# 假设人工标注结果和模型预测结果分别存在两个文件中 with open('human_label.txt', 'r', encoding='utf-8') as f: human = [line.strip() for line in f if line.strip()] with open('model_label.txt', 'r', encoding='utf-8') as f: model = [line.strip() for line in f if line.strip()] correct = sum(1 for h, m in zip(human, model) if h == m) accuracy = correct / len(human) print(f'准确率: {accuracy:.2%}')

这一步能帮你客观评价词典法在你自己的数据上的表现。如果准确率低于75%,不要急着换模型,先去检查情感词表的覆盖度和否定词处理,这两个是影响词典法效果的主要因素。我在一次复用中准确率只有68%,排查后发现是那批评论里大量使用方言词和网络梗,比如「绝绝子」「YYDS」,扩充词表后准确率提升到了81%。

再聊复用。把这份源码从美的空调扩展到其他品类,需要改动的有三处:自定义词典、情感词表、停用词表。以手机评论为例,自定义词典里要加入品牌词(华为、小米、iPhone)和产品词(骁龙、续航、快充、曲面屏)。情感词表要做减法更要看场景词,像「掉电快」里的「快」在空调场景是正面(制冷快),在手机场景可未必是正面。这是词典法最大的短板,但也是它的可控之处——每个词都可以人工审查。

最后聊我自己的使用习惯。每次跑这类文本分析项目,我会强制自己走三遍流程:第一遍原样跑通拿基线,第二遍修改词典和停用词优化结果,第三遍验证评估后固化参数。一套流程走完,你手里的输出就不仅是一份源码了,而是一套能适应自己数据特征的完整工具。希望这次拆解能帮你在复现的路上少踩几个坑。

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

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

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

立即咨询