京东评论情感分析实战:150行Python代码读懂NLP全流程
发布时间:2026/10/1 17:58:34 作者:尧图编辑部 阅读量:1,286

简介“电商-京东评论数据情感分析”是一份基于Python的NLP实战资源面向数据分析初学者与自然语言处理入门者聚焦电商评论的分词、关键词提取和情感倾向判断。压缩包共8个文件大小7.7MB包含主程序.py、Jupyter Notebook交互式笔记、CSV数据集、HTML分析报告、两张词云图和SimHei.ttf中文字体能够覆盖从数据读取、清洗、分析到结果输出的完整流程。目前已有234人学习下载。项目代码环环相扣先用Pandas/Numpy读取处理数据经文本去标点、去停用词等预处理后调用jieba分词再通过TF-IDF或TextRank提取关键词最后进行情感打分并借助词云图展示评论高频词与消极评论分布。读者可获得可运行的Python脚本与Notebook既能一步不差地复现示例也可将约150行评论数据替换为自有数据迁移应用到其他电商平台。整体代码结构清晰注释完整适合作为第一个完整的情感分析练手项目。1. 京东评论数据情感分析150行代码里的完整NLP流程拿到这份“京东评论数据情感分析”资源时我本以为又是一份包装过度的课程设计解压后看到约150行主代码、一个CSV数据文件、两张词云图和配套的HTML/Notebook反而觉得踏实了。这是一个典型的中文电商评论NLP小项目用Python走通了从CSV读取、数据清洗、jieba分词、TF-IDF关键词提取到基于情感词典打分的全流程最后用词云做可视化输出。如果你是正在学数据分析或NLP的从业者想看看真实电商评论长什么样、情感分析在没训练复杂模型时能做到什么程度这份代码值得花一小时逐行拆一遍。它能解决的核心问题是用最轻量的方式把一堆杂乱的中文商品评论变成“正面/负面”倾向结论并可视化呈现高频词和两类评论的特征差异。2. 数据集与代码结构先看清手里有什么牌2.1 解压后有哪些文件每个文件是干什么的这份压缩包里的文件清单很清楚我建议按“数据 → 主代码 → 输出结果”的顺序来看。首先是京东评论数据.csv这是原始语料记录了用户对商品的文本评价。其次是京东评论数据情感分析.py约150行是整个分析的主程序独立可运行。京东评论数据情感分析.ipynb是同名Notebook版本适合在Jupyter里逐步调试每一步的输出都能直接看到。京东评论数据情感分析.html是Notebook导出后的静态报告方便不装Python环境的人直接查看代码和结果。两张PNG词云图——词云图.png和消极评论词云.png——是程序跑完后的可视化产物此外还附了SimHei.ttf中文字体这是为了让词云和图表里的中文不乱码。文件里那个.ipynb_checkpoints目录是Jupyter自动保存的备份可以直接忽略。我的习惯是先把.py文件从头到尾读一遍再开着Notebook对照跑一遍因为.py文件是最终整合好的逻辑Notebook更适合定位每一步中间结果。2.2 依赖库与运行环境准备项目核心依赖是Pandas、NumPy、jieba和Matplotlib。Pandas负责读CSV和做DataFrame级的数据筛选、去重、计数NumPy在后缀计算和统计部分打辅助jieba是中文分词的绝对主力Matplotlib配合WordCloud生成词云。如果你跑的是Windows环境SimHei.ttf就是用来解决中文字体缺失问题的。我建议用Anaconda建一个干净的Python 3.8左右的环境来跑命令如下conda create -n jd_analysis python3.8 conda activate jd_analysis pip install pandas numpy jieba matplotlib wordcloud提示wordcloud库在Windows上偶尔会出现安装失败如果pip装不上可以从Christoph Gohlke的whl页面下载对应Python版本的包离线安装。装完依赖后把SimHei.ttf放到当前工作目录或字体目录下然后开始逐段跑主程序。Pandas版本不用太新1.x就够太新的2.x在某些旧代码的append用法上会报错如果发现代码里用了DataFrame.append需要手动改成pd.concat。2.3 先跑一遍看全流程输出是什么样我第一次跑完这份代码终端依次输出的是CSV加载的行数、经过清洗后剩余的有效评论数、分词后的前N个词、全部评论的关键词Top20、正负情感评论分别的数量最后弹出词云图窗口。整个过程不到30秒是个典型的小数据量分析流水线。跑通之后建议做一件事把CSV文件用Excel或Pandas打开肉眼浏览前50条评论你会很快建立起对这份数据风格的感知——哪些是真实用户吐槽哪些是默认好评哪些是刷单痕迹明显的重复文本。这对接下来的清洗和情感分析参数理解很有帮助因为我们做的所有文本处理本质上都在和这类“不干净的真人语言”打交道。3. 数据清洗与中文分词情感分析的真正地基3.1 清洗规则去重、去空、去无效值很多初学数据分析的人拿到CSV就直接做词频统计结果词云里全是“的”“了”“京东”“东西”这类词根本看不出情感倾向。这份代码的第一道工序就是清洗。Pandas读入数据后先做空值删除和完全重复评论的去除这一步看似简单实际影响很大。电商评论里大量存在“此用户未填写评价内容”这类默认文本以及“好评好评好评好评”这种重复刷出来的内容它们如果在语料里占比过高会严重拉偏情感分布。import pandas as pd df pd.read_csv(京东评论数据.csv, encodinggbk) print(原始数据条数:, len(df)) # 删除全空行和评论内容为空的行 df df.dropna(subset[评论内容]) # 去掉完全重复的评论 df df.drop_duplicates(subset[评论内容], keepfirst) print(清洗后数据条数:, len(df))这里要特别说明encodinggbk。这个参数是从实战里试出来的电商平台导出的CSV文件用Excel打开时默认可能是ANSI编码而Python在Windows下读取这种文件时指定gbk才能避免UnicodeDecodeError。如果你拿到的是UTF-8编码的文件这个参数会直接报错那时改成utf-8即可。dropna的参数subset指定只在“评论内容”这一列检查空值。drop_duplicates的keepfirst表示保留第一条重复记录这是比较保守的做法——如果同一条评论出现在不同商品下它可能对应不同的购买场景全删了反而损失信息。3.2 去停用词与文本正则清理停用词表是中文文本处理少不了的配套资源。代码里通常内置了一个列表或者从外部文件加载里面包含“的”“了”“是”“在”“我”“你”等高频但无语义贡献的虚词。处理流程是先把评论文本中的非中文字符全部剔除再把分词后落在停用词表里的词过滤掉。import re import jieba stopwords set() with open(stopwords.txt, r, encodingutf-8) as f: for line in f: stopwords.add(line.strip()) def clean_text(text): # 只保留中文字符去掉标点、数字、英文字母 text re.sub(r[^\u4e00-\u9fa5], , str(text)) return text df[cleaned] df[评论内容].apply(clean_text) def cut_words(text): words jieba.lcut(text) # 过滤停用词和单字词 words [w for w in words if w not in stopwords and len(w.strip()) 1] return words df[words] df[cleaned].apply(cut_words) print(df[words].head())re.sub(r[^\u4e00-\u9fa5], , str(text))这条正则的核心含义是把字符串里所有不在\u4e00-\u9fa5这个Unicode中文字符区间的字符全部替换为空。之所以保留单字词过滤是因为中文里大量单字词“好”“差”“快”脱离了语境很难判断情感而且词云展示时单字视觉权重很低。jieba的lcut方法返回的是列表和cut的生成器模式不同这里直接拿到列表便于后续操作。注意我在过滤条件里只保留了长度大于等于2的词这意味着“好”“坏”这类单字评价会被滤掉这会丢失一些极短评的情感信息——这是本项目的一个近似处理如果你的业务场景需要区分“好”和“不好”这里得调整。3.3 分词模式选择精确模式还是全模式jieba支持精确模式、全模式和搜索引擎模式。日常情感分析和关键词提取建议用精确模式也就是jieba.lcut的默认行为它按最合理的方式切分比如“不喜欢”会切成“不/喜欢”两个词而不是“不喜/欢”。全模式会把所有可能的词都切出来比如“中华人民共和国”会变成“中华/华人/人民/共和/共和国”虽然召回率高但噪声大不适合情感分析。# 精确模式分词默认 words_precise jieba.lcut(这款手机外观漂亮但电池不耐用) print(words_precise) # 输出: [这款, 手机, 外观, 漂亮, 但, 电池, 不耐用] # 全模式分词 words_full jieba.lcut(这款手机外观漂亮但电池不耐用, cut_allTrue) print(words_full) # 输出: [这款, 手机, 外观, 漂亮, 但, 电池, 不耐, 耐用]这组对比很有价值。精确模式保留了“不耐用”这个整体词全模式却把它拆成了“不耐”和“耐用”后者在情感打分时会把“不耐”当成否定词把“耐用”当成正面的逻辑就乱了。所以情感分析场景下精确模式几乎是唯一正确选择。另外如果评论里有大量人名、品牌名、专业术语建议提前用jieba.add_word把它们加入自定义词典比如“荣耀X50”“骁龙8Gen3”这类词默认可能被切碎加入词典后就会作为整体保留。4. 关键词提取与情感评分从词到结论的两条路4.1 TF-IDF关键词提取找出每类评论的“代表性词汇”关键词提取主要有两种思路一种是TF-IDF一种是TextRank。TF-IDF的核心逻辑很简单一个词在某一类评论里出现得越多TF高在其他类评论里出现得越少IDF高就越能代表这一类。jieba.analyse模块封装好了TF-IDF实现不用自己算权重。import jieba.analyse # 合并所有评论为一个长文本 all_positive_text .join(pos_df[cleaned].tolist()) all_negative_text .join(neg_df[cleaned].tolist()) print(正面评论关键词 TOP20:) for kw, weight in jieba.analyse.extract_tags(all_positive_text, topK20, withWeightTrue): print(kw, round(weight, 4)) print(负面评论关键词 TOP20:) for kw, weight in jieba.analyse.extract_tags(all_negative_text, topK20, withWeightTrue): print(kw, round(weight, 4))extract_tags的三个核心参数分别是待提取的文本、topK返回前多少个关键词、withWeight是否返回权重值。权重本身是TF-IDF的综合得分数值越大代表该词在文本中的区分度越高。注意我这里先按情感标签把评论分成正负两个DataFrame再分别做关键词提取这样得到的Top词就带上了“正面评论喜欢说什么”和“负面评论在抱怨什么”的对比信息。从实战角度看正面的关键词通常落在“质量”“不错”“速度”“性价比”负面的则会出现“垃圾”“差劲”“退货”“售后”等。两组词并列看你不需要机器学习模型就能直观感知消费者的核心诉求方向。4.2 TLDRTextRank算法在短文本上的表现TextRank是另一种常见提取方案它基于词共现图计算节点权重类似PageRank的思路。和TF-IDF的无监督统计不同TextRank不依赖语料库的全局统计只依赖当前文本内部的词关系所以对单篇短文本效果尚可。# TextRank方式提取 for kw, weight in jieba.analyse.textrank(all_negative_text, topK20, withWeightTrue): print(kw, round(weight, 4))jieba.analyse.textrank的默认参数allowPOS只保留词性为名词、动词、形容词的词。这在很多场景比TF-IDF更合理因为提取出的关键词更“像人话”。但要注意textrank底层需要先对文本做词性标注处理速度比TF-IDF慢一个量级。对于几千条评论耗时影响不大但如果是几十万量级建议还是优先用extract_tags。实际上我在拆解这个项目时发现代码里两种方法都有应用。我的建议是TF-IDF用来做正负评论的对比关键词TextRank用来做单条长评论的摘要提取。如果你的语料是客服工单这类长文本TextRank值得做调参尝试电商短评场景TF-IDF效率更高、结果也更稳定。4.3 情感词典打分正面词加分、负面词减分情感分析部分是这份代码的核心但它没有上深度学习模型而是走了一条经典的“情感词典”路线。原理是维护一个正面词表和一个负面词表遍历评论分词结果每命中一个正面词记1命中一个负面词记-1累加得分判断正负。positive_words set([好, 不错, 满意, 喜欢, 快, 实惠, 漂亮, 赞]) negative_words set([差, 垃圾, 慢, 退货, 坑, 烂, 失望, 差评]) def sentiment_score(words): score 0 for w in words: if w in positive_words: score 1 elif w in negative_words: score - 1 return score df[score] df[words].apply(sentiment_score) # 得分0记为正面0记为负面0记为中性 df[label] df[score].apply(lambda s: 正面 if s 0 else (负面 if s 0 else 中性)) print(df[label].value_counts())这个打分函数非常直白score初始为0遍历一条评论的所有分词命中正面词加1命中负面词减1。最后按照总分判断整条评论的情感倾向。阈值就是0意味着如果一条评论里正面词和负面词数量相等会被归为中性。这种做法的优点是解释性强你能清楚知道某条评论为什么被判为负面——因为里面出现了“垃圾”和“退货”两个负面词。缺点也很明显它无法处理“不怎么样”“太慢了反而显得耐用”这种带否定、转折或反讽的复杂句式。这是情感词典方法的天然天花板不是这份代码的缺陷。如果你在真实业务中需要更高的准确率可以引入否定词表如“不”“没”“别”做邻近词反转或者用SnowNLP、BERT模型来替代。我一般会在跑完这个基础打分后做一步验证随机抽取50条正面、50条负面评论人工核对准确率。这份数据上的经验结果大概是80%左右的准确率对于快速调研足够了如果客户需要更高精度就得换方案。5. 可视化呈现与常见坑词云图不是Spyder里双击就能弹出来的5.1 正确配置SimHei字体让词云不再方块乱码Matplotlib和WordCloud在Windows环境下默认字体不含中文字符直接画词云会出现满屏方块。解决方案就是配置项目自带的SimHei.ttf。import matplotlib.pyplot as plt from wordcloud import WordCloud import numpy as np font_path SimHei.ttf def generate_wordcloud(text, filename): wc WordCloud( font_pathfont_path, width800, height600, background_colorwhite, max_words200, max_font_size150 ) wc.generate_from_text(text) plt.figure(figsize(10, 8)) plt.imshow(wc, interpolationbilinear) plt.axis(off) plt.savefig(filename, dpi300, bbox_inchestight) plt.close() # 必须关闭否则多次运行内存堆积font_path是WordCloud的中文字体兜底方案必须在创建WordCloud对象时指定。max_words200控制词云里最多显示的单词数max_font_size150限制最大字号防止“好”“不错”这类高频词占据整个画布。interpolationbilinear是让词云边缘更平滑的常见做法不加的话文字边缘锯齿感较重。plt.close()很关键在循环生成多个词云图时如果不关闭figure对象运行次数多了会造成内存警告。生成词云前要把所有目标文本合成一个长字符串wc.generate_from_text(text)的入参是字符串而不是分词列表这是一个新手极易踩的坑——把列表传进去会报AttributeError。合成的常见做法是用空格连接所有词 .join(all_words)。5.2 全流程运行顺序与参数联动这份代码的运行顺序有讲究清洗 → 分词 → 关键词提取 → 情感打分 → 词云。每个环节的输出都会影响下游结果改前一步的参数后面的图表就会跟着变。比如你把停用词表里加上“手机”“电脑”这类商品名词云里的高频词会大幅减少反倒让“质量”“物流”浮上来你把情感词典扩充进“性价比”“颜值”这类词正负评论的比例就会变化。python 京东评论数据情感分析.py直接运行主脚本如果中途没有报错工作目录下就会生成两张词云图。Ipython Notebook版本里按ShiftEnter逐格运行可以看到每一步的DataFrame结构、词频统计和中间输出。我见过很多人在plt.show()之后程序暂停不退出这是正常现象关闭弹出的图像窗口后程序才会继续执行。5.3 避坑指南5个高频问题的现象、原因与解决第一个坑是读取CSV报编码错误。现象是最常见的UnicodeDecodeError: gbk codec cant decode或UTF-8报错原因就是文件实际编码和你指定的编码不一致。解决方法是先打开文件确认编码再用对应编码读取——如果你在Windows上双击CSV正常打开内部大概率是GBK在Mac/Linux下导出的UTF-8文件用GBK读必报错。稳妥做法是写一个自动检测函数with open(京东评论数据.csv, rb) as f: raw f.read() encoding utf-8 if b\xef\xbb\xbf in raw or b\xe4\xb8\xad in raw else gbk第二个坑是jieba分词时自定义词被切碎。现象是“荣耀手机”被切成“荣耀”和“手机”“保护壳”变成“保护”和“壳”原因是没有把品牌名/产品名加入自定义词典。解决方法是调用jieba.add_word(荣耀X50)或维护一个自定义词典文件在启动时分批加载。第三个坑是WordCloud生成时找不到字体。现象是FileNotFoundError: font_path指向了一个不存在的路径。原因是在Linux服务器上运行时当前目录没有SimHei.ttf。解决方法是把字体放到/usr/share/fonts/目录下并显式传入绝对路径或者用matplotlib.font_manager把字体注册进全局字体列表。第四个坑是情感评分把“退货”算成负面词但用户其实在夸退货快。这是词典方法的典型局限现象是部分明显正面的评论被判负面。原因是情感词典缺乏领域上下文。解决办法是业务导向地扩充词典比如“退货快”“售后好”里的“快”“好”应该抵消“退货”的负面影响——一个初见成效的做法是用否定词表加窗口翻转逻辑把“不差”由负面改成正面。第五个坑是不平衡类导致的情感偏向。现象是评论数据本身大部分是好评负面词云几乎没什么可提取的内容。原因是电商平台刷单或用户习惯性只给好评。解决方法是先做类分布统计如果负面样本太少关键词提取和词云都没有统计意义此时要么扩大采集范围要么放弃正负对比只做整体情感倾向分析。6. 进阶用法把静态分析转成可复用的小工具6.1 封装成函数库让CSV路径和词典文件变成参数项目跑通只是第一步我会把主脚本里的分析流程封装成一个类这样以后换一个数据源或调整词典时不用改大量代码只改参数即可。这个过程一般半小时完成收益却是长期的。class JDSentimentAnalyzer: def __init__(self, csv_path, encodinggbk, stopwords_pathstopwords.txt): self.df pd.read_csv(csv_path, encodingencoding) self.stopwords self._load_stopwords(stopwords_path) self.positive_words set([好, 不错, 满意, 喜欢, 快, 实惠, 漂亮, 赞]) self.negative_words set([差, 垃圾, 慢, 退货, 坑, 烂, 失望, 差评]) def _load_stopwords(self, path): words set() with open(path, r, encodingutf-8) as f: for line in f: words.add(line.strip()) return words def load_and_clean(self): self.df self.df.dropna(subset[评论内容]) self.df self.df.drop_duplicates(subset[评论内容], keepfirst) self.df[cleaned] self.df[评论内容].apply( lambda x: re.sub(r[^\u4e00-\u9fa5], , str(x)) ) def segment(self): self.df[words] self.df[cleaned].apply( lambda text: [w for w in jieba.lcut(text) if w not in self.stopwords and len(w.strip()) 1] ) # 后续可以继续添加compute_score、extract_keywords、plot_wordcloud等方法封装之后你可以直接这样调用analyzer JDSentimentAnalyzer(新数据.csv, encodinggbk) analyzer.load_and_clean() analyzer.segment() analyzer.compute_score() analyzer.plot_wordcloud(整体词云.png)这个封装的价值在于情感词典、停用词表、正则规则都被固定成了默认参数业务上只需换CSV路径就能完成对新数据源的分析。回到判断一条新评论的情感时可以定义一个predict方法对单条评论走同样的清洗和分词流水线然后用词典打分返回结果——这就是一个极简的情感分析接口了。6.2 抽取特征构造二维矩阵做正负分类对比再进阶一步可以用刚刚提取的关键词构造特征矩阵做正负评论的可视化对比。比如把“物流”“质量”“价格”“售后”设成四类业务维度词统计每类评论中各维度词出现的频率用柱状图对比。dimension_words { 物流: [物流, 快递, 配送, 收货], 质量: [质量, 做工, 耐用, 结实], 价格: [价格, 性价比, 便宜, 贵], 售后: [售后, 退货, 客服, 服务] } def count_dimension(words): counts {} for dim, keywords in dimension_words.items(): counts[dim] sum(1 for w in words if w in keywords) return counts df[dim_count] df[words].apply(count_dimension) pos_dim df[df[label] 正面][dim_count].apply(pd.Series).sum() neg_dim df[df[label] 负面][dim_count].apply(pd.Series).sum()这里pd.Series把字典展开成四列sum()按列累计得到每个维度的总词频。然后你就能画出一张对比图正面评论里“物流”出现次数高负面评论里“售后”和“退货”明显突出。这种维度对比比单纯词云更有业务解释力也是你在简历上可以多写一句的亮点把原始评论转化成了可量化的体验维度分析。从那以后我做任何一个文本分析项目都会强制走一遍“先画单条时间线、再上词云、最后做维度对比”的完整流程宁可多花半小时也绝不在语义判断上只看一张图。希望帮到你。本文还有配套的精品资源点击获取