ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

京东商品评论情感分析:数据去重与文本预处理全流程实战

2026/9/15 22:00:55 拓冰建站 浏览量
京东商品评论情感分析:数据去重与文本预处理全流程实战 做京东商品评论情感分析的时候很多人一上来就调LSTM、上BERT结果数据还没洗干净跑出来的准确率惨不忍睹。我接过不少这类项目也和做毕业设计的同学聊过发现真正拉开差距的往往不是模型多高级而是数据处理那几步有没有做扎实。尤其是“数据去重复”这个环节看着不起眼实际影响非常大——一条好评被重复爬了十几次模型训练时就把这条样本的权重放大了十几倍情感分类自然会被带偏。这篇博客就围绕京东商品评论这个场景把数据去重和文本预处理整个流程拆开讲透再延伸到情感分析建模的选型和实操最后聊聊这套流程怎么迁移到景区舆情分析这类项目里。内容适合三类人正在做NLP课程设计或毕业设计的同学刚接触电商评论分析想搭一套完整流程的工程师还有从数据角度想了解评论预处理的运营和分析师。我会把每一步为什么这么做、坑在哪里、代码怎么写都讲清楚能直接照着抄作业。1. 数据去重不止是去掉重复行这么简单1.1 评论数据里的重复是怎么产生的京东商品评论数据不会乖乖躺在数据库里等你下载多数场景下是爬虫抓的、或者平台导出的。这两种渠道都会带来大量重复数据。爬虫重复最常见的原因有三个。第一断点续爬时没有维护好去重队列同一页被爬了多次。第二翻页参数有问题比如按时间排序时新评论插入导致分页错位后面的页面会反复包含前面的评论。第三接口超时后重试第一次其实提交成功了但客户端认为失败又请求了一次于是同一条评论被保存了两遍。平台导出也会重复比如导出了全量数据后又增量导出或者同一个SKU对应多个父商品ID子商品页面上的评论互相重叠。还有一种更隐蔽的重复——近似重复。同一条评论内容基本一样只是中间多了一个空格、一个表情符号或者“京东”和“jd”这种大小写差异。这些肉眼看起来是重复的但字符串比较时hash值完全不同普通去重根本发现不了。1.2 去重的层次精确去重、语义去重与业务去重去重不能一把梭要先分清楚你要处理的是哪一层重复。第一层是精确去重。最简单有效的方案是先对文本做统一标准化再计算哈希值用哈希集合去重。标准化至少包括把全角字符转半角英文字母统一小写删除所有空白字符包括换行和Tab删除HTML标签。做完这些标准化后再来计算MD5或者SHA1重复数据的哈希值就能对上了。import hashlib import re def normalize_text(text): # 全角转半角 text text.replace(, ,).replace(。, .).replace(, !) text text.replace(, ?).replace(“, ).replace(”, ) # 转小写 text text.lower() # 去掉HTML标签 text re.sub(r[^], , text) # 去掉所有空白字符 text re.sub(r\s, , text) return text.strip() def md5_dedup(df, text_col): seen set() keep_index [] for idx, text in enumerate(df[text_col]): norm normalize_text(str(text)) h hashlib.md5(norm.encode(utf-8)).hexdigest() if h not in seen: seen.add(h) keep_index.append(idx) return df.iloc[keep_index]第二层是近似去重。有些重复内容标准化后仍然不完全相同比如“这个商品非常好用”和“这个商品非常 好用”中间多了空格标准化能解决但“很好”和“挺好的”这种语义近似、字面不同的情况就需要SimHash或MinHash这类指纹算法来判断。SimHash的思路是把文本转成64位的指纹两个指纹的汉明距离小于等于3就认为是近似重复。第三层是业务去重。电商评论里有很多“同一用户、同一商品、相似时间、高度相似内容”的评论这种不只是数据重复还可能是刷单或恶意评论。对这种数据我会结合user_id、item_id、评论内容三列做联合判断比如同一用户对同一商品在24小时内发布了多条内容高度相似的评论就只保留其中一条。业务去重没有标准答案核心是要想清楚你的分析目标是什么——如果是做情感分布统计那么一个用户重复刷的好评会严重高估正向比例必须处理掉。1.3 去重顺序与注意事项很多人习惯先做清洗再去做重其实先做粗去重效率更高。先按原始文本的MD5去一遍数据量能降下来一大半后面做标准化、分词这些耗时操作就轻松很多。然后再做标准化后的去重最后根据业务规则去重。这个顺序不绝对但我实测下来能省不少内存和时间。去重时保留哪一条也有讲究。如果评论带时间字段一般保留第一条或时间最早的那条因为后续评论可能被修改过。如果评论带辅助信息比如追加评论、点赞数、回复数那么要保留信息最全的一条。还有些场景需要保留评分和评论的关联这时候不能只看文本重复就删还要校验评分字段是否一致。我之前就遇到过文本完全一样但评分一个是5星一个是1星的情况这种属于恶意评论或系统异常需要单独打标而不是简单去重。2. 文本预处理情感分析之前的“洗菜”环节2.1 文本清洗正则、去HTML、去广告京东评论的原始文本里噪声种类很多。爬虫导出的评论经常带着HTML标签比如span classcolor-goods这种有些评论里嵌入了“此用户未填写评价内容”的占位符还有那种“商品不错就是快递慢下次还会再来推荐购买【京东自营】”里带品牌后缀的推广语。清洗时我会按顺序做这几件事先去掉HTML标签再去掉URL、邮箱、电话号码然后处理特殊符号和表情最后清理无意义的空白字符。表情符号这里有个坑中文情感分析里表情是强特征比如“好评”和“好评”情感强度完全不同直接删掉会损失信息。更好的做法是把表情符号映射成文字标签比如[微笑]、[生气]或者保留为单独特征喂给模型。def clean_comment(text): text re.sub(r[^], , str(text)) text re.sub(rhttps?://\S|www\.\S, , text) text re.sub(r\d{11}, , text) # 手机号 text re.sub(r[〔〕【】《》], , text) text re.sub(r[ \t], , text).strip() return text清洗完要做一步“空值判断”。很多评论清洗后只剩一个空字符串或者只剩下“此用户未填写评价内容”这类占位符这些都要统一填充为缺失值或直接删除。不删的话后面分词得到空列表建模时会造成维度混乱。2.2 中文分词与停用词中文文本没有空格分词所以要用jieba这类工具。京东商品评论有两个特点一是商品名和品牌词非常多比如“iPhone 15 Pro Max”、“美的”、“海尔”默认词库切得稀碎二是评价领域的口语化表达特殊比如“性价比高”、“物流快”、“客服态度好”。针对这两个特点我会准备自定义词典。把商品品类词、品牌词、评价术语加进去让jieba能正确识别。词典格式很简单一行一个词还可以指定词频和词性iPhone 15 Pro Max 10 n 性价比高 5 nz 物流快 3 a 客服态度 3 n 京东超市 10 n停用词表我常用哈工大停用词表和百度停用词表再叠加自己整理的电商领域停用词。注意停用词不能盲目删尤其是否定词和转折词。“不”“没”“别”这类词删了“不好”会变成“好”“不喜欢”会变成“喜欢”情感极性直接反转。正确做法是不要把否定词放进停用词表而是在分词后做否定词处理比如把“不 喜欢”合并成“不喜欢”再作为整体特征。2.3 拼音、谐音与网络用语的处理这部分是京东评论预处理里最花时间的环节也是最容易出彩的地方。电商评论里“绝绝子”、“yyds”、“666”、“辣鸡”、“集美们”这些词的字面意思和真实情感完全不一致。如果让模型直接学小样本下很难学对。我的做法是建立一个领域情感映射表把这些网络用语映射到规范表达或者直接赋予情感倾向。比如“yyds”映射为“永远的神”或直接标记正向“辣鸡”映射为“垃圾”标负向“666”根据上下文可能是正向也可能中性需要人工判断。映射表可以手工维护也可以从语料里用词频和共现信息挖掘候选词再人工筛选。这块工作很琐碎但对最终效果提升巨大。emo_slang_dict { yyds: 永远的神, 绝绝子: 特别好, 辣鸡: 垃圾, 集美们: 姐妹们, 666: 厉害 } def replace_slang(text): for k, v in emo_slang_dict.items(): text text.replace(k, v) return text3. 情感分析模型怎么选从词典到LSTM的实战对比3.1 为什么先做规则基线拿到干净数据后不要急着训练深度学习模型。先搭一个基于情感词典的规则模型把它当作基线能帮你快速判断数据质量也能给后面的复杂模型提供对比参考。规则模型的核心是情感词典最常用的是BosonNLP情感词典它每个词都带情感分值正负向都有比较适合电商场景。再叠加HowNet词典和自建的电商领域情感词表比如“质量好”2、“发货慢”-1、“售后差”-2。计算方式就是把一条评论分词后把命中的情感词分值累加得到情感得分。import jieba from collections import defaultdict sentiment_dict defaultdict(float) # 加载词典 # with open(boson_emotion.txt, encodingutf-8) as f: # for line in f: # word, score line.strip().split() # sentiment_dict[word] float(score) def rule_predict(text): words jieba.lcut(text) score sum(sentiment_dict.get(w, 0) for w in words) return 1 if score 0 else (0 if score 0 else -1)规则模型能跑通之后先人工抽100条看结果。如果规则模型的错误集中在某类表达上比如反语、程度副词、转折句那就说明预处理和词典还有优化空间而不是急着调模型。这一点很多人会忽略上来就调LSTM结果模型学到的噪声比信号还多。3.2 LSTM中文文本情感分析的实战配置数据量不大几千到几万条的情况下LSTM是一个性价比非常高的选择。相比BERT这类大模型LSTM在CPU机器上也能训练而且参数可控适合做毕业设计和工程落地。我的标准流程是分词 → 转成序列 → Embedding层 → BiLSTM层 → Attention池化 → 全连接输出。分词后要限制序列长度京东评论一般不超过100个字pad到50或80就够。这里有个经验值先统计你样本的评论长度分布取95%分位数作为max_len而不是拍脑袋定一个数。from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Embedding, LSTM, Dense, Bidirectional, Dropout model Sequential([ Embedding(input_dimvocab_size, output_dim128, input_lengthmax_len), Bidirectional(LSTM(64, return_sequencesTrue, dropout0.2)), Bidirectional(LSTM(32, return_sequencesFalse, dropout0.2)), Dense(64, activationrelu), Dropout(0.3), Dense(3, activationsoftmax) # 负/中/正 ]) model.compile(optimizeradam, losssparse_categorical_crossentropy, metrics[accuracy])这里的关键参数有三个。一是embedding维度小语料128就够不用太大。二是LSTM单元数64到128之间一般够用再大容易过拟合。三是dropout至少0.2否则训练集准确率上去了验证集一塌糊涂。另外类别要是做二分类正向/负向最后一层激活函数换sigmoidloss换binary_crossentropy。3.3 数据不平衡处理与评估商品评论的情感分布天然不平衡好评占多数。如果你的目标是把“差评”识别出来而差评只占10%那么模型即使全部预测好评准确率也有90%看起来不错实际上一点用没有。处理不平衡有三个常用手段。一是欠采样从多数类里随机抽一部分适合数据量大的场景。二是过采样用SMOTE对少数类生成合成样本但文本数据直接SMOTE容易产生不自然的句子我更推荐对文本向量化之后再用SMOTE或者干脆用类权重。三是修改loss函数的class_weight告诉模型对少数类更敏感。class_weight {0: 1.0, 1: 1.0, 2: 3.0} # 差评权重加大 model.fit(X_train, y_train, epochs10, batch_size64, validation_data(X_val, y_val), class_weightclass_weight)评估指标一定要看F1不能只看准确率。二分类场景下我习惯同时打印精确率、召回率和F1并且按分类维度看混淆矩阵。差评的召回率低就说明大量真正差评被漏掉了要增加差评权重或补充差评语料精确率低则说明模型把很多中好评误判成了差评要看看是不是预处理里否定词处理的锅。4. 从京东评论到景区舆情这套流程还能怎么复用4.1 场景迁移商品评论与景区舆情的异同很多同学看到“基于Python的景区舆情情感分析系统设计与实现——以云南热门景区为例”这类题目会觉得陌生其实整套流程和京东评论分析高度相似。都是文本数据 → 去重 → 清洗 → 分词 → 情感分类差别主要在数据源和语义空间。数据源变了噪声类型就变了。景区评论主要来自微博、大众点评、携程和马蜂窝微博文本带用户和话题标签大众点评的评论有LV等级和商家回复这些都要在清洗阶段处理。另外景区评论里大量出现地名、景点名、交通方式比如“大理”、“玉龙雪山”、“拼车”、“高反”自定义词典要相应调整。语义空间也大不相同。商品评论里的“大”可能指尺寸大景区评论里的“大”可能指景区面积大也可能是“性价比不高”的省略说法。情感词表要重新拉一遍像“人多”、“排队”、“风景好”、“出片”这些景区特有表达都要补充进词典。4.2 一个完整系统的模块划分与实现思路如果要做成一个“系统设计”我建议按五个模块拆数据采集模块、数据预处理模块、情感分析模块、可视化报表模块、管理后台模块。数据采集可以写爬虫或者接API预处理模块把去重、清洗、分词封装成独立函数情感分析模块提供规则模型和LSTM模型两种选择可视化用FlaskECharts展示舆情趋势、情感占比、热门话题词云。系统架构上有一点要提醒预处理模块必须设计成可以重跑的。因为舆情数据是持续更新的每次抓完新数据就要对全量数据重新预处理如果预处理逻辑分散在不同脚本里后面维护就是一场灾难。我会把预处理流水线写成类似Spark的transform链每个环节一个函数输入输出都是DataFrame方便串起来也方便单独调试。def preprocess_pipeline(df): df df.pipe(dedup_exact) df df.pipe(dedup_business) df df.pipe(clean_text) df df.pipe(replace_slang) df df.pipe(tokenize_and_filter) return df4.3 毕业设计与工程落地的差异化建议这个流程既可以做毕业设计也可以做小规模工程应用两者的侧重点完全不同。毕业设计更看重分析链路完整性和对比实验。建议先做基于词典的情感分析再做基于LSTM的模型最后对比两者在不同评价维度上的表现这样论文里能讲出故事。如果能把“数据去重”单独作为一个研究点分析不同程度去重对情感分类准确率的影响那就有创新点了也比单纯堆模型高级得多。工程落地则更看重稳定性和可解释性。线上跑情感分析模型时你不可能每来一条评论都重新跑一次预处理全流程要把词表、映射表、模型参数固化下来做成离线更新加在线预测的模式。还有一点工程场景里规则模型仍然有它的位置比如“一键投诉”这种高敏感评论宁可查得严一点也不能漏掉。5. 实际操作中踩过的坑与排查技巧5.1 去重时踩过的坑第一个坑是全角半角符号导致hash不一致。同样是逗号中文评论里可能是“”也可能是“,”标准化时忘了处理的话两条内容相同的评论hash值完全不同精确去重直接失效。排查方法很简单去重之后随机抽100条重复率比较高的评论文本肉眼看看是不是有很多“看起来一样但没被去掉”的数据。第二个坑是URL参数不同导致的近似重复。京东商品链接后面带着一堆跟踪参数评论里有人发链接时你会看到同一链接被爬下来版本很多。处理方式是在清洗阶段把URL统一替换为[url]占位符这样去重时不会被URL细节干扰。第三个坑跟业务相关文本相同但情感标签不同。有些促销评论比如“很好但是快递慢”会被系统拆成两条一条评分高一条评分低。这种数据去重时不能光看文本要联合其它字段判断。我踩过这个坑之后去重逻辑里加了“同一文本评分不同则不合并”的规则。5.2 预处理时踩过的坑分词结果里出现空字符串和纯空格这是最常见的bug。原因通常是正则清洗时把两个汉字之间的空格删掉了但句首句尾的空格没删干净或者原文本里就有大量换行。解决方法是分词后统一过滤[w for w in jieba.lcut(text) if w.strip()]但要注意别把“不”这种单字词滤掉所以过滤条件要写清楚。停用词删除时误删“不”也是个高频坑。之前我用一份网上常见的停用词表里面居然有“不”这个字。结果“不好”变成“好”“不太行”变成“太行”情感完全反了。后来我审查停用词表时凡是可能改变情感极性的词一律排除。编码问题在中文NLP里永远存在。京东导出数据常见GBK编码但爬虫抓到的可能是UTF-8。读取DataFrame时如果没指定编码一堆乱码会让正则清洗完全失效。我的建议是所有数据入库前统一转成UTF-8并且写一个编码检测函数自动处理。5.3 情感分析时踩过的坑短文本过拟合是我遇到最多的问题。京东评论平均长度不到30个字LSTM很容易把训练集背下来验证集却一塌糊涂。解决方法是增强数据简单有效的是回译增强把中文评论翻译成英文再翻译回来能产生大量语义一致的变体。另一个方法是在Embedding层做dropout而不是只在全连接层做。还有数据泄漏这个坑和去重直接相关。如果训练集和验证集是从同一份数据里随机切分的但没有先做全局去重那么一条评论可能在训练集和验证集里各出现一次模型评估结果虚高。正确做法是先全量去重再做训练验证切分。这个点我在帮人看代码时发现很多人都会犯而且不容易察觉因为准确率看起来很高但上线后效果大幅缩水。写在最后的几个实操心得回头再看整个京东商品评论情感分析流程我最大的感受是数据去重和文本预处理不是“前戏”而是决定项目成败的主战场。我自己刚开始做的时候拿到数据就急着分词、跑模型结果模型效果差也不知道问题出在哪。后来养成一个习惯每次建模之前先把去重率、清洗前后样本量变化、分词后的词频分布这几项统计打印出来一目了然。还有一个小技巧分享给大家在预处理阶段就把所有“中间产物”保存下来。比如去重后的数据保存一份清洗后的保存一份分词后的再保存一份。这样做的好处有两个——复现问题方便写报告和论文也有素材。很多人跑完整个流程模型效果不好想排查都不知道从哪里下手其实问题往往就出在中间某一步。最后无论是做商品评论还是景区舆情核心思路是通用的先理解数据从哪来想清楚哪些噪声会伤害你的分析目标再用合适的工具把数据整理干净最后才轮到模型登场。这个过程没有捷径但每一步都有方法可循希望这篇内容能帮你少踩几个坑。