ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Python电商评论爬虫与情感分析:从Requests到SnowNLP的完整实现

Python电商评论爬虫与情感分析:从Requests到SnowNLP的完整实现 简介基于Python构建的电商平台商品评论数据采集与情感分析系统面向电商运营、市场研究及数据分析学习者解决海量商品评论的自动抓取与情感倾向量化问题。压缩包共123个文件以7个Python源码脚本为核心配套37个CSV评论数据集、30张可视化图表、LSTM模型文件及Chromedriver驱动整体55.57MB数据与代码完整对应。已有117人学习浏览。系统覆盖淘宝、京东等平台的商品信息采集、多维度情感评分、饼图与趋势图展示内置分布式采集与自适应反爬策略包内还含历史数据备份与配置项便于本地调试和结果复现。读者可直接运行源码复现情感分析全流程并利用多类目中文评论数据开展迁移实验适合作为爬虫与NLP方向的实战参考。1. 为什么评论区值得爬一套能把评论自动变成数据的 Python 爬虫与情感分析方案打开任何一个电商商品页评论区几百条文本里藏着“质量怎么样、尺寸偏不偏、客服态度好不好”这些真实反馈但一条条读根本读不完。用 Python 爬虫把商品评论批量抓下来再做一轮情感分析把正负向评论的比例、关键词分布直接输出成图表这才是评论区的正确打开方式。这套方案适合三类人做竞品分析的运营、写课程设计的学生、想给电商选品做参考的个人买家。整体跑下来只需要 requests、pandas、SnowNLP 这几个库不需要登录不需要扫码半天时间能跑通一个最小可用版本。2. 爬虫篇用 requests 拆京东商品评论接口翻页与反爬参数怎么设2.1 先看清评论接口返回了什么电商平台的评论区大多是异步加载的页面里能看到评论但直接抓 HTML 拿不到完整数据。我一般先打开商品页按 F12 进入浏览器开发者工具切到 Network 面板再点一下评论区的“下一页”就能看到真正的数据请求地址。京东的评论接口是https://club.jd.com/comment/productPageComments.action返回的是 JSON里面包含评论内容、评分、昵称、追评、点赞数等字段。import requests url https://club.jd.com/comment/productPageComments.action params { productId: 100012043978, # 商品ID换成你要分析的商品 score: 0, # 0代表全部评论1差评2中评3好评 sortType: 5, # 5按时间排序6按推荐排序 page: 0, # 页码从0开始 pageSize: 10, # 每页条数 isShadowSku: 0, fold: 1, } headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36, Referer: https://item.jd.com/100012043978.html, } resp requests.get(url, paramsparams, headersheaders, timeout10) data resp.json() print(data.keys())这段代码是把接口请求参数完整拼出来注意page从 0 开始不是 1。京东接口的pageSize上限一般到 30超过会被拒绝。拿到data后先看键名重点盯住comments和maxPage这两个字段前者是当前页评论列表后者决定你最多能翻多少页。继续往下取评论正文之前先做一步容错处理。返回的数据里如果comments为空直接跳过这一页不要报错中断。还要注意有些评论会有afterContent字段也就是追评内容抓的时候一并带回来后面做情感分析时会多一个判断维度。2.2 翻页逻辑与请求频率控制翻页不能写死循环猛拉接口对频率有隐性限制拉得太快会返回空列表或者直接 403。我一般先把抓取逻辑写成一个循环每次拿到评论后随机停 2 到 4 秒并且把page从 0 递增到maxPage避免重复请求最后一页。import time import random all_comments [] max_page data.get(maxPage, 1) for page in range(0, max_page 1): params[page] page resp requests.get(url, paramsparams, headersheaders, timeout10) if resp.status_code ! 200: time.sleep(5) continue try: page_data resp.json() comments page_data.get(comments, []) except Exception: comments [] for item in comments: all_comments.append({ content: item.get(content, ), afterContent: item.get(afterContent, ), score: item.get(score), nickname: item.get(nickname, ), date: item.get(creationTime, ), }) if not comments: break time.sleep(random.uniform(2, 4))这里的循环做了三层保护状态码不是 200 时先睡 5 秒再继续解析失败时按空列表处理这一页没评论直接跳出。random.uniform(2, 4)的随机延时是必须的固定间隔反而容易被识别为脚本。抓到all_comments之后存进 pandas 的 DataFrame顺便按商品 ID 存一份 CSV后面情感分析直接读这份文件就行。有个细节要提醒评论接口返回的content偶尔会是空字符串这种记录建议保留但标记出来不要直接丢弃。因为空评论可能是用户只打分没写文字保留能保证评分分布统计是完整的。3. 情感分析篇SnowNLP 还是词典法先跑通再谈准确率3.1 为什么先用 SnowNLP 打底对中文电商评论做情感分析最省事的方案是 SnowNLPpip 安装后直接能用不需要训练模型也不需要准备标注数据。SnowNLP 内部是一个基于电商和社交媒体语料训练过的贝叶斯分类器输入一段文本输出一个 0 到 1 之间的情感概率越接近 1 表示越正向。from snownlp import SnowNLP text 物流很快包装严实用起来手感不错好评 s SnowNLP(text) print(s.sentiments) # 输出0到1之间的情感概率SnowNLP 的sentiments属性返回的是积极概率不是二分类标签。我一般以 0.6 为阈值大于等于 0.6 判为正向小于 0.4 判为负向中间段算中性。这个阈值要根据你的数据微调比如数码产品评论普遍用词克制阈值可以降到 0.55食品类评论情绪词多0.6 更稳。def analyze_sentiment(text): if not text or len(text.strip()) 2: return neutral s SnowNLP(text) score s.sentiments if score 0.6: return positive elif score 0.4: return negative else: return neutral这段处理逻辑把空文本和过短文本先排除掉避免“好”“差”这种单字判断失真。SnowNLP 对短文本特别敏感如果评论只有两三个字大概率会被判偏所以过滤长度是个必要步骤。3.2 词典法做第二票解决模型盲区SnowNLP 有一个明显短板它对带有反讽、转折、网络流行语的句子判断经常翻车。比如“这价格还要什么自行车”它可能会判成中性但实际是正向再比如“东西不错就是发货太慢”它容易只看后半句“发货太慢”就压到负向。这时候我习惯叠一个词典法做交叉验证也就是把评论分词后跟一个预置的正负向情感词表做匹配打分。import jieba positive_words {不错, 好评, 快, 喜欢, 满意, 赞, 值得, 实惠, 推荐} negative_words {差, 慢, 退货, 失望, 垃圾, 破损, 漏发, 客服, 贵} def lexicon_sentiment(text): words jieba.lcut(text) pos_score sum(1 for w in words if w in positive_words) neg_score sum(1 for w in words if w in negative_words) if pos_score neg_score: return positive elif neg_score pos_score: return negative else: return neutral词典法本身不复杂重点是词表要贴合你的业务。上面这份词表是我针对京东评论手写的种子词典只有 20 个词但句子里只要出现“破损”“漏发”这种强负向词SnowNLP 可能犹豫词典法会坚决判负两个方法一对齐结果就更可信。更完整的做法是把两类词表扩充到几百个词正负向各维护一个文本文件跑之前读进来。这里整理了一份两方法的能力对比方便根据数据特征选择对比维度SnowNLP词典法上手成本pip install 即用需要准备词表长句理解能感知整体语境只看词级匹配网络流行语容易误判词表里有就能识别可解释性黑匣子只给分数每个词都有据可查适合场景快速打标、批量初筛垂直行业精准判断实际项目里我的原则是先用 SnowNLP 全量跑一遍再对情绪分数落在 0.3 到 0.7 之间的糖果区评论用词典法做二次判断两边结论一致就采用不一致就归为中性。这样既保留模型泛化能力又让典型情感词不被模型淹没。3.3 批量打分并合并爬虫结果情感分析脚本要能直接吃上一章爬出来的 CSV逐条打标后新增三列情感分数、情感标签、判定来源。判定来源用来记录这条结果是模型出的还是词典法救回来的后续做准确率分析时能回溯。import pandas as pd df pd.read_csv(jd_comments.csv, encodingutf-8-sig) results [] for content in df[content].fillna(): s_prob SnowNLP(content).sentiments if len(content.strip()) 2 else 0.5 lex_label lexicon_sentiment(content) if s_prob 0.6: model_label positive elif s_prob 0.4: model_label negative else: model_label neutral if model_label in (positive, negative) and model_label ! lex_label and lex_label ! neutral: final_label neutral source conflict else: final_label model_label source model results.append({ content: content, snow_score: round(s_prob, 4), label: final_label, source: source, }) result_df pd.DataFrame(results) result_df.to_csv(jd_comments_sentiment.csv, indexFalse, encodingutf-8-sig)参数上注意两个点fillna()把空评论先补成空字符串再判断read_csv用utf-8-sig编码否则 CSV 里的中文在 Excel 打开会乱码。跑完后source列等于conflict的样本就是你最值得回头读一遍的评论通常也是模型砍不准的难啃骨头。4. 数据整合清洗脏数据、统计分布并输出可视化结果4.1 清洗与统计先看看整体评论长什么样情感标签打完之后不要直接进去画图先做一轮清洗和统计。第一步是去重同一用户对同一商品的重复评论偶尔会出现需要按商品 ID、昵称、评论内容三列联合去重。第二步是过滤无效评论长度小于 2 的、只含标点和表情的、还有“此用户未填写评价内容”这种占位文本都打上 invalid 标签。import pandas as pd import re df result_df.copy() df[content] df[content].astype(str).str.strip() df df.drop_duplicates(subset[nickname, content]) placeholder_patterns [此用户未填写评价内容, 默认好评, 无] df df[~df[content].isin(placeholder_patterns)] df df[df[content].str.len() 2] df df[df[label] ! invalid]清洗逻辑里我把占位文本用列表维护你也可以换成正则去匹配更多变体。str.len()过滤后留下来的评论才是真正有分析价值的样本。跑完清洗后输出一个简单统计总评论数、正向占比、负向占比、平均情感分这几个数字能快速判断这个商品的口碑热度。total len(df) positive_count len(df[df[label] positive]) negative_count len(df[df[label] negative]) neutral_count total - positive_count - negative_count print(f总评论数: {total}) print(f正向占比: {positive_count / total if total else 0:.1%}) print(f负向占比: {negative_count / total if total else 0:.1%})这里有个容易算错的地方正向占比的分母应该是清洗后的总数不是清洗前。如果抓回来 1000 条清洗后剩 800 条分母用 1000 会把所有比例的基数搞错对比不同商品时结论就失真了。4.2 用 pyecharts 画饼图和关键词分布把分析结果可视化统计分析只出数字还不够直观把这套流程接上 pyecharts直接输出 HTML 格式的可交互图表就能把结果分享给不懂代码的人看。pyecharts 的优势是生成 HTML 文件浏览器直接打开不需要额外部署服务。from pyecharts.charts import Pie, Bar, WordCloud from pyecharts import options as opts from collections import Counter import jieba label_counts df[label].value_counts() pie ( Pie() .add(, [list(z) for z in zip(label_counts.index, label_counts.values)]) .set_global_opts(title_optsopts.TitleOpts(title评论情感分布)) .set_series_opts(label_optsopts.LabelOpts(formatter{b}: {c} ({d}%))) ) pie.render(情感分布.html) positive_text .join(df[df[label] positive][content].tolist()) words [w for w in jieba.cut(positive_text) if len(w) 2 and w not in stopwords] counter Counter(words).most_common(30) wordcloud ( WordCloud() .add(, counter, word_size_range[20, 80], shapecircle) .set_global_opts(title_optsopts.TitleOpts(title正向评论关键词)) ) wordcloud.render(正向词云.html)stopwords 需要你自己维护一个停用词表把“这个”“可以”“但是”这类无意义词过滤掉。word_size_range控制词云里字号的范围词频越高的词会映射到更大的字号如果发现“好评”“满意”这类词占据绝对主导说明数据集本身偏向好评分析时要留意这个样本偏差。5. 避坑与常见问题电商评论爬虫与情感判断的五个翻车现场5.1 请求接口返回 403 或验证码页现象第一页能爬到翻到第三四页开始频繁出现 403甚至直接弹出滑块验证。原因请求频率过高或者请求头里缺少 Referer。评论区接口对 Referer 校验很严格不带商品页来源的请求会被识别为脚本。解决headers 里必须带上Referer: https://item.jd.com/商品ID.html并且把随机延时拉大到 3 到 5 秒。如果还是触发验证就加一层重试机制连续失败 3 次就暂停 30 秒再继续。5.2 评论总是停在第一页翻页后返回空列表现象第一页正常返回 30 条第二页开始comments是空数组但接口没有报错。原因page参数语义搞错。京东评论接口的首页是 page0如果从 page1 开始第二页会被当成第一页而真正的第一页已经被爬过了指向了一个不存在的页面偏移。解决page 从 0 开始递增循环次数用maxPage控制。另外pageSize不要超过 30超过会被服务端静默拒绝。5.3 SnowNLP 把明显的正向评论判成负向现象“东西不错发货也快就是包装有点简陋”被判成 negative但整句话主基调明显是正面的。原因SnowNLP 对转折结构处理不好句末的“包装简陋”在模型里权重覆盖了前面的“不错”。解决在模型判断之前先用规则把转折句切成两段只对主干部分做情感分析。常见做法是检测“但是”“不过”“就是”这类转折词把从句去掉再进模型同时把转折词后面的内容单独存下来做辅助参考。5.4 CSV 文件用 Excel 打开全变乱码现象CSV 文件用记事本打开正常用 Excel 打开全是乱码。原因Python 默认写入编码是 UTF-8而 Excel 打开 CSV 时默认按 GBK 解析两边编码不一致。解决写入 CSV 时统一指定encodingutf-8-sig这个编码会在文件开头写入 BOM 标记Excel 就能识别为 UTF-8。不要用encodingutf-8那是乱码的根源。5.5 商品评价数很多但爬到的评论量远少于显示值现象商品页显示有几万条评价但接口最多只能翻到 100 页左右。原因京东评论接口不是全量开放的未登录状态下普通商品通常只能访问最近的一部分评论更早的评论需要登录态。解决maxPage返回多少就爬多少不要尝试绕过限制。如果确实需要更全的历史评论要把登录后的 Cookie 拼进请求头但登录态接口的参数经常变动代码需要按实际情况调整。做课程设计或竞品分析时近几百条评论其实已经足够支撑情感分布结论。6. 验证与调优拿人工标注检验情感模型再决定要不要换方案6.1 准备一份小样本测试集量化模型到底准不准情感分析模型不能“感觉差不多就行”尤其要做报告或者交付给别人的时候准确率需要有据可查。我的做法是先从评论数据里随机抽 200 条人工逐条标注正向、负向、中性然后拿标注结果跟模型输出比对算出准确率、精确率、召回率这几项指标。200 条人工标注大约需要 20 分钟但能换来对这模型适配度的清醒认知。import random from sklearn.metrics import classification_report test_indices random.sample(range(len(result_df)), 200) test_set result_df.iloc[test_indices] # 模拟人工标注结果实际项目中这里是你自己读评论后填的标签 manual_labels [] for content in test_set[content]: # 人工读取 content 后手动判定示例代码直接复用模型标签 manual_labels.append(test_set.loc[test_set[content] content, label].iloc[0]) print(classification_report(manual_labels, test_set[label], target_names[negative, neutral, positive]))跑完classification_report后重点看 macro avg 这一行的 F1 值如果低于 0.7说明当前模型对你这批数据适配度不够需要走下面的调优步骤。这里要说明一点人工标注本身也有主观成分同一个评论不同人可能标出不同结果所以 200 条样本的人工标注尽量固定一个人完成避免标注标准漂移。6.2 方法调优的三种可选路径如果测试集 F1 偏低我给三个方向按投入成本从低到高排列。第一是扩充词典法的词表把你人工标注时设为目标但模型判错的 30 到 50 个词全部收进词表再跑一次测试集通常能提升 5 到 10 个百分点。第二是调整 SnowNLP 的阈值比如把 0.6 改成 0.55模型边界变化会直接影响中性区间的划分如果负向评论被漏判的多就把阈值整体下移。第三是换成预训练模型比如用 HanLP 或百度的情感分析接口效果更好但需要装额外的依赖、消耗更多资源适合数据量大且准确率要求高的场景。6.3 把这套流程沉淀成一个可复用脚本换商品直接跑代码写到这里最值得做的一件事是把爬虫、清洗、情感分析、可视化串联成一个入口脚本换商品时只改product_id一个参数。从那以后我每次接到新的商品分析需求都强制走一遍这个流程先爬 300 条评论看格式再跑情感分析出分布最后抽 50 条人工验证模型的判断。如果不做人工验证这一步之前踩过的那些坑大概率会在新数据上原样重现。这套代码里我也是把人工验证做成了一个固定的数据检查步骤希望你也能保留它——毕竟评论区语言的更新速度永远比模型训练语料快。希望这个流程对你手头的项目有用。本文还有配套的精品资源点击获取
返回列表