京东评论情感分析实战:jieba分词+TF-IDF+词典法150行搞定
发布时间:2026/10/1 11:29:44来源:尧图网络
简介面向Python数据分析与自然语言处理入门者这份资源以京东电商评论为数据源演示了从文本清洗到情感判别的完整流程。项目代码约150行覆盖Pandas数据读取与清洗、Numpy数值计算、jieba中文分词、TF-IDF/TextRank关键词提取以及基于情感词典的评分规则可对每条评论输出正面、负面或中性的判断适合想通过真实业务数据练习文本挖掘的学习者。压缩包共8个文件约7.7MB除主程序与CSV评论数据外还包含Jupyter Notebook交互文档、HTML结果报告、两张词云图以及中文字体文件其中词云图直观呈现高频词和消极评论的聚集特征HTML报告便于快速浏览分析结论。目前已有234人学习。通过该实战项目读者能掌握评论文本分析从数据预处理、分词、关键词提取到情感打分与可视化展示的完整链路并理解如何用Python串联这些环节为后续更复杂的自然语言处理任务打下基础。1. 京东评论数据情感分析150行脚本能先拿下这些活儿做电商评论数据分析的人基本都遇到过这个需求手里躺着几千条京东评论想知道用户到底在夸什么、骂什么最好再给个好评率、差评率。京东评论数据情感分析想解决的就是这件事。一套约150行的Python脚本把分词、关键词提取、情感分析三个环节串起来直接从原始评论产出品类关键词Top清单和正负面占比。这个方案不依赖人工标注不训练深度学习模型半小时内能跑完几千条。适合电商运营、爬虫转数据分析、以及刚入门NLP的工程师拿来当基线和冷启动方案。后续就算要换更重的情感模型这套脚本产出的分数也可以作为特征或伪标注不算白做。下面我把它拆开讲每个环节的参数怎么设、哪里会翻车都会说到。2. 数据清洗与jieba分词评论变成词序列前要做的三件事2.1 京东评论数据长什么样清洗脚本先写三行实际接触到的京东评论数据大多是从后台导出或按商品页抓下来的CSV常见字段包括评价内容、追评内容、评分1-5星、颜色尺码、下单时间。真正参与分析的往往只有评价内容和追评内容其他字段用来做分组维度比如按SKU看差评分布按周看口碑变化。拿到数据的第一个动作不是分词而是清洗。评论里混着HTML标签、商品规格占位符、京东表情占位符“[开心]”“[鼓掌]”以及一堆无用标点。如果直接丢给分词这些噪声会变成关键词顶部的高频词情感打分也会被“好评”“赞”这类占位符带偏。我一般先把清洗函数固定在脚本最前面全部文本处理都走这一条通道。import re def clean_text(text: str) - str: text str(text) text re.sub(r[^], , text) # 去掉HTML标签 text re.sub(rhttps?://\S, , text) # 去掉链接 text re.sub(r\[[^\]^\[]\], , text) # 表情占位符先替换为空格 text re.sub(r[^\u4e00-\u9fa5a-zA-Z0-9。], , text) # 只保留中英文、数字和常见标点 text re.sub(r\s, , text).strip() # 压缩连续空白 return text逻辑很简单三个正则做完大部分事情。关键在第三个正则我保留了中文感叹号“”和问号“”这两类标点在情感分析里是有用的不能顺手删掉。如果你用的是追评加首次评价合并分析先把两列拼接再清洗拼接时加一个空格分隔符避免评论结尾词连接追评开头词产生新词。补充一个编码细节从Excel导出的CSV经常带UTF-8 BOM直接用pandas读会出现第一列列名带“\ufeff”。读取时建议写pd.read_csv(path, encodingutf-8-sig)这个参数能同时兼容BOM和无BOM文件。如果你拿到的是GBK编码的TXT先转成UTF-8再进清洗通道不要在清洗函数里反复猜编码。2.2 用jieba做中文分词的最小调用自定义词典和停用词表Python中文分词工具里最常用的就是jieba分词处理京东评论这种几十字内的短文本足够。jieba默认词典偏通用领域会把“漫步者”“翻车”“不太好”这类电商词和口语词切得七零八落。所以我会先读入一个自定义词典再读停用词表。停用词表的作用不是删掉所有词而是把“京东、东西、这个、一个、真的、很”这类高频但对分析没有区分度的词压掉。import jieba import jieba.analyse jieba.load_userdict(jd_userdict.txt) # 文件每行格式词 词频 词性 # 漫步者 50 nz # 手感 80 n # 不掉帧 60 vn with open(stopwords_general.txt, encodingutf-8) as f: STOP_WORDS set(f.read().split()) def cut_words(text: str) - list: return [w for w in jieba.lcut(text) if w not in STOP_WORDS and w.strip()]load_userdict是jieba加载领域词典的入口每行可以带词频和词性。词性建议写上后面关键词提取的allowPOS过滤会依赖它。cut_words返回list因为后面情感打分需要逐词遍历用lcut比用cut生成器更直观。停用词表不用贪大先把“京东、物流、快递、东西、这个、真的”这些必现词放进去等看完第一批关键词输出再补。2.3 先打印5条看分词结果再全量跑我见过不少同事数据加载完直接全量分词跑完发现词典没生效回头重新跑一遍。分词是黑匣子里最前端的一环这一环错了后面关键词和情感分数全部跟着偏。所以每次改完词典和停用词先抽样打印几条人工看一眼切得对不对。def debug_words(df, n5): for i in range(n): t clean_text(df.loc[i, 评价内容]) print(t) print(cut_words(t))如果打印结果里出现“漫步”“者”这种切错的词去补自定义词典如果出现“这个”“京东”这类泛词去补停用词表。京东评论还有个特点是短评特别多很多用户只写“很好”“一般”“差”这类文本分词没有难度难点在于“还行吧”“也就那样”“不太行”这种口语词典和停用词要专门覆盖。“还行”如果不入正向词典“也就那样”不入中性表达库情感打分就会漏判。3. 关键词提取用TF-IDF把几千条评论压成Top关键词3.1 为什么要先做关键词提取以及为什么选TF-IDF关键词提取承接分词结果目标是把几千条评论压缩成一张短清单这批商品用户讨论最多的是什么。常见做法是TF-IDF因为它在短文本上稳定、可解释、速度快。TF-IDF衡量一个词在当前文档里出现的次数以及它在全部语料里分布得有多“稀有”。那些在所有评论里都高频的泛词会被降权而“续航”“发热”“掉漆”这类只在特定问题里高频的词会被顶上来。TextRank是另一个常用方案它通过词共现图给关键词排序不依赖外部语料。但京东评论大多是一行几十字的短文本词与词之间的共现关系稀疏TextRank跑出来的结果不稳定还容易把“质量”“东西”这种连坐词排到前面。所以我的习惯是单条评论关键词用TF-IDF全量评论的关键词也用TF-IDFTextRank先不考虑。这里有一个常见误用把关键词提取只应用在单条评论上然后取平均。单条评论只有几十个字TF-IDF算出来的权重噪声很大正确做法是把全体清洗后的评论拼接成一个大文本再对这个大文本做一次extract_tags。这样提取出来的是整个评论区层面的关键词而不是某一条评论的局部词。3.2 extract_tags最小调用和三个必调参数import jieba.analyse jieba.analyse.set_stop_words(jd_stopwords.txt) def extract_keywords(corpus_text: str, topK: int 10) - list: return jieba.analyse.extract_tags( corpus_text, topKtopK, withWeightTrue, allowPOS(n, vn, v, a, nz), )corpus_text是把所有评论清洗后拼接成的大文本。实际上线时我会按SKU或按品类分组拼接而不是整个店铺混在一起否则不同商品的特征词会互相稀释。topK控制返回词数一般取10到20withWeightTrue时返回(词, 权重)列表方便后续排序和画图allowPOS是词性过滤只保留名词、动名词、动词、形容词和专名儿化音、副词、语气词直接不过来。参数默认值我常用的值说明topK2010-20取前K个关键词withWeightFalseTrue返回权重用于排序和可视化allowPOS()n/vn/v/a/nz过滤词性去副词和语气词还有一套更进阶的操作用jieba.analyse.set_idf_path(jd_idf.txt)替换默认IDF。文件每行格式是“词 空格 权重”权重越大表示该词越稀有。默认IDF来自通用语料在电商场景里“客服”这种词会被压得很低如果你想让“客服”在售后场景关键词里露头可以单独训练一套京东评论IDF。训练方式不复杂把全量评论按商品分组用sklearn.feature_extraction.text.TfidfVectorizer拟合后导出词权重再转成jieba的IDF格式。但这一步对于150行的脚本来说属于可选项前期跑通流程时先跳过。3.3 分品类关键词差异这个环节怎么用来巡检商品关键词提取的价值不在技术本身而在业务判断。同一个脚本跑不同品类关键词差异非常明显。我拿几个典型品类的示例数据看过输出大致是下面这个形态品类提取出的Top5关键词手机续航、发热、拍照、手感、屏幕婴儿奶粉日期、包装、溶解、囤货、验证吸尘器吸力、噪音、续航、尘盒、滚刷表格里的词是示意真实数据会带品牌名和型号。注意“续航”在手机和吸尘器里都出现但语境完全不同这也是为什么不能只做全店关键词提取要分SKU或分品类跑。运营拿到这种清单能快速定位某批次商品是被“发热”刷屏还是被“包装破损”刷屏比人工翻3000条评论高效得多。关键词提取的另一个用途是反哺情感分析。把高频关键词和情感分数做交叉就能看到“续航”这个词出现的评论里平均分是正还是负。这个交叉表可以导成Excel作为给运营的日报素材。4. 情感分析打分与阈值标定词典法为什么是先落地的选择4.1 不上模型先跑词典法成本和可解释性决定情感分析部分很多人第一反应是训练一个文本分类模型。但在京东评论这个场景里标注数据要现做模型要调参还要处理类不均衡对150行的脚本来说负担太重。常见做法是先用词典法准备正负情感词表、程度副词表、否定词表按规则给每条评论打分。词典法有三个实在的好处。第一可解释一条评论得了负分你能明确指出是哪个词扣的分运营问起来答得上第二冷启动快词表从一个通用种子词集开始边跑边补一两天就能把覆盖率补到够用第三它产出的分数是连续值后续如果换BERT这类模型这个分数可以作为额外特征或伪标注不会白做。缺点也很明显“太差了”这种带否定词和程度副词的组合需要规则覆盖这块最容易翻车会在第5章细说。如果你手头没有情感词表常见方案是用开源的中文情感词典做种子比如大连理工的情感词汇本体库然后跑一遍全量评论把高频但不在词表里的词捞出来人工判断后加进去。电商场景建议单独维护一张业务情感词表至少包含质量、客服、速度、包装、售后、性价比、味道、颜色、尺码这些高频对象词对应的情感倾向。4.2 情感打分实现极性词、程度副词、否定词三件事打分规则的核心就一句话在分词后的词序列里遇到正向词加分遇到负向词减分加多少减多少由它前面的程度副词和否定词决定。我常用的实现如下。POS_WORDS set(open(pos_words.txt, encodingutf-8).read().split()) NEG_WORDS set(open(neg_words.txt, encodingutf-8).read().split()) DEGREE_WORDS { 非常: 2.0, 特别: 2.0, 很: 1.5, 挺: 1.3, 有点: 0.6, 稍微: 0.5, 太: 1.8, 极其: 2.0, } NEGATION_WORDS {不, 没, 别, 无, 莫, 未, 不是, 不会, 不太, 不怎么} def sentiment_score(text: str) - float: words cut_words(clean_text(text)) score 0.0 for idx, w in enumerate(words): weight 1.0 if idx 0: if words[idx - 1] in DEGREE_WORDS: weight * DEGREE_WORDS[words[idx - 1]] if words[idx - 1] in NEGATION_WORDS: weight * -0.8 if w in POS_WORDS: score 1.0 * weight elif w in NEG_WORDS: score - 1.0 * weight return score这段代码里POS_WORDS和NEG_WORDS是每行一个词的情感词表DEGREE_WORDS是程度副词的加权系数系数可以按语感调。否定词表需要特别注意这里放的是“不、没、别、无”这类单字词也要放“不是、不会、不太”这类双字词因为jieba会把“不是”切成一个完整的词如果你只放“不”那么“不是很好”里的“不是”就不会触发否定逻辑。权重计算只处理当前词前一个词不向前找两个词。这么做是为了避免“非常不”这种双重否定被错误累加。如果你想让“非常不好”这种句式完全正确需要再加一条规则当程度副词和否定词同时出现在极性词前时按“否定词优先”处理也就是权重变成负的同时吞掉程度副词的加成或保留它。150行脚本里我会选简单方案程度副词和否定词都看当前词的前一个词如果它恰好是“非常不”这种组合实际切词后“不”会紧贴“好”所以多数情况下逻辑能跑对。4.3 用评分字段标定阈值而不是拿评分当训练标签京东评论自带1-5星评分这是一个弱标注信号。常见做法不是直接拿它当训练标签而是用来观察情感分数的分布是否合理。先用打分函数给所有评论打分按评星分组看平均值。df[clean] df[评价内容].fillna().map(clean_text) df[emotion_score] df[clean].map(sentiment_score) stats df.groupby(评分)[emotion_score].describe() print(stats)跑出来的统计表大致长这样数值是示意实际取决于你的词表覆盖度评分评价数平均情感得分正分占比1星223-2.112%2星118-0.821%3星1540.243%4星2901.368%5星12152.486%看到这张表你会发现一个规律1星和5星的情感分区分得很开但2星和3星之间有重叠。这是正常的因为“评分”和“文字表达”本来就不是完全对应。有人给2星但评论写得客气有人给3星但文字很冲。阈值该选多少不是拍脑袋定的而是看统计表里3星平均分落在哪。比如上表3星平均分是0.2我会把阈值设为大于0.5判正向小于-0.5判负向中间判中性。如果你的数据集里1星平均分只有-45星平均分只有1说明词表对负向词覆盖不足先去补词表而不是硬调阈值。4.4 150行脚本的函数边界怎么把整个流程压缩在一个文件里标题里说约150行不是故意压缩代码而是这个流程本身不需要写太长。我一般按五个函数组织整个脚本load_comments负责读CSV和字段合并clean_text负责清洗cut_words负责分词和停用词过滤extract_keywords负责全量关键词提取sentiment_score负责单条情感打分最后在main里调用并汇总输出。函数之间只传递清洗后的文本不互相掺和。# 函数清单 # load_comments(path) 读取CSV合并评价内容与追评 # clean_text(text) 清洗单条文本 # cut_words(text) 分词并过滤停用词 # extract_keywords(text) 全量关键词提取 # sentiment_score(text) 单条情感打分 # summary(df) 按SKU汇总正负面比例写出Excel这样的结构意味着你可以把情感打分整段替换成SnowNLP或一个微调的BERT模型不影响清洗和关键词部分。150行不是终点是一个可以继续长高的骨架。5. 京东评论情感分析的6个常见坑与排查清单5.1 泛词霸榜关键词前十个全是“京东、东西、物流”现象跑完关键词提取Top10里六个是“京东、东西、物流、快递、商品”真正反映商品问题的词被挤到后面。原因停用词表太小没有覆盖电商场景通用词这些词出现频率高但文档分布也广TF-IDF的降权作用被词频抵消。解决先把平台词和通用词补进停用词表京东、东西、物流、快递、商品、评价、购物、订单、客服、服务。然后检查allowPOS是否生效如果发现动词太多把allowPOS收窄为(n, vn, a, nz)。再不行就在关键词提取前做一次词频截断对词频排前100的候选词手动看一遍把不具区分度的词加进停用词表。这步是半人工的第一次跑的时候需要10分钟。5.2 否定词误翻“不是很好”被判成正分现象情感打分函数把“不是很好”识别为正向原因是jieba把“不是”切成一个词“很好”在正向词表里而否定词表里只有“不、没、别”没有覆盖“不是”。原因单字否定词覆盖不了双字否定词加上打分规则只回看前一个词导致否定逻辑没触发。解决否定词表扩展为{不, 没, 别, 无, 莫, 未, 不是, 不会, 不太, 不怎么, 没啥}。同时要注意“不太”和“不”的词性不同“不太满意”应该被弱化为负向不能直接翻转为正向。更稳妥的做法是只对“极性词前一个词是否定词”做翻转不要做全局负号处理否则“不好”变成“好”的权重语义就反了。5.3 程度副词被忽略“非常差”和“差”得一样的分现象“差”和“非常差”打出来的分数相同导致差评严重程度无法区分。原因打分循环没有处理程度副词或程度副词和极性词之间隔着标点和空格权重没有乘上去。解决词序列里先回看前一个词判断是否在DEGREE_WORDS中。注意不要跨标点加权比如“非常。差”是两个句子不能把“非常”的权重作用到“差”上。如果分词后“非常”“差”之间夹着“的”“地”这类修饰词可以在回看窗口扩大到两个词但要小心误伤。我的习惯是先只回看一个词等统计完错误样本再决定要不要扩大窗口不要一开始就把规则写复杂。5.4 表情占位符被当成噪声丢掉现象评论内容里大量“[开心]”“[鼓掌]”“[失望]”被清洗成空格导致情绪强烈的评论被判成中性。原因清洗函数把所有[...]都当噪声去掉了没有区分是商品规格占位符还是表情占位符。解决不要简单删除改成映射。把京东常见表情替换成语义词后再进入分词和打分流程。EMOJI_MAP { [开心]: 开心, [鼓掌]: 赞, [赞]: 赞, [失望]: 失望, [差评]: 差, [生病]: 差, } def clean_text(text: str) - str: for k, v in EMOJI_MAP.items(): text text.replace(k, v) # 然后继续走原有正则清洗如果你的数据里表情占位符特别多建议专门收集一份京东表情对照表这个表比情感词表还容易积累跑一遍全量评论就能统计出高频占位符。注意京东的表情占位符不一定按文档原样导出抓包数据里可能带编码清洗前先输出20条原始文本看一眼格式再定正则。5.5 重复评论和刷单内容推高关键词权重现象某商品评论区被几十条“好评质量好”这类同文案刷屏关键词提取结果里“好评”排第一情感分数也被拉高整体口碑失真。原因重复文本没有去重TF-IDF和情感平均都被重复样本主导。解决清洗后先做精确去重df.drop_duplicates(subset[clean])。如果对方是改标点重复再做归一化去重去掉所有标点和空格后比较归一化文本。注意追评和首评内容相同不一定是刷单用户可能追评时重复一句“质量好”这个按业务口径决定要不要去重。对于150行的脚本我一般只做精确去重和三字符以上相同归一化去重不做相似度聚类性价比最高。5.6 阈值拍脑袋把0当正负分界线结果中评好评混在一起现象情感分数分布整体偏移0附近聚集了大量“还行”“一般”“凑合”把0作为正负分界线导致这些中性表达被随机分到正或负。原因词典法分数受词表覆盖度和语料分布影响没有绝对零点。解决用4.3节的评分分组统计来标定。看3星平均分落在哪把中性区间设成“3星平均分 ± 0.5”。如果3星平均分是0.2那么正阈值0.7负阈值-0.3如果3星平均分是-0.5正负阈值整体下移。阈值这个事一开始看着像玄学实际上有星评这种弱标签做参照就能把主观判断变成可复现的统计口径。6. 上线前最后一步人工校验、阈值校准与结果可视化6.1 抽50条人工标注先用混淆矩阵给脚本泼盆冷水改完一轮参数后不要急着看整体统计先抽30到50条评论人工标注正、中、负三类再和脚本结果对比。这一步能挡掉大部分规则Bug。简单实现可以直接比对不需要引入完整混淆矩阵库。def evaluate(y_true, y_pred): pairs list(zip(y_true, y_pred)) correct sum(a b for a, b in pairs) print(f准确率: {correct / len(pairs):.2%}) # 打印错例按错误类型分组便于补词表 for a, b, text in sample_data: if a ! b: print(f标注 {a} / 预测 {b}: {text})抽样要注意覆盖各星评不要只在1星里抽。我一般每个档位抽10条共50条。如果准确率低于70%先看错例集中在哪类。高频错误一般是词典缺词把错例里的情感词补进正负词表能补一轮就涨几个点。阈值不要在这一步调先把词表补齐再看分布。6.2 把情感得分和关键词做成交叉报表运营才能真正用起来脚本的最后一步不是打印一个准确率而是输出一个可交付的报表。我习惯把每条评论的SKU、评分、情感得分、预测类别、Top关键词合并成一个Excel再用pyecharts按星评画一个正负面占比柱状图。这个图能直接放进周报比一串数字直观得多。from pyecharts.charts import Bar from pyecharts import options as opts bar ( Bar() .add_xaxis([1星, 2星, 3星, 4星, 5星]) .add_yaxis(负面占比, [0.78, 0.60, 0.31, 0.15, 0.08]) .set_global_opts(title_optsopts.TitleOpts(title各星评情感分布)) ) bar.render(emotion_bar.html)数据列可以换成自己统计出来的真实占比。关键词和情感分数的交叉表也值得做把按SKU提取的Top关键词和平均情感得分拼在一张表里运营能直接看出“吸力”这个词是不是在和“噪音”一起出现也能看出这类消费电子评论里“性价比”对整体口碑的带动作用。这其实是美团、淘宝商家后台那些评论分析工具背后的基本套路自己用脚本实现一遍后续再上平台化的报表系统思路是通的。我第一次做这套脚本时第一个版本就翻车在否定词翻转上把“不太满意”打成正向。后来养成一个习惯每次改词表、改权重、改阈值之前先抽50条样本定好基准改完再跑同一批样本对比。有了这条基准线后面所有调参都有据可依不再是凭感觉试。这套约150行的流程价值在于用最低的成本把评论数据变成可用的结论来源等跑顺了再往上加模型、加报表都不迟。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网