
简介这是一份基于Python的京东商品评论爬取与情感分析可视化研究资料包面向Python爬虫、数据分析与自然语言处理方向的初学者以及需要完成课程设计或毕业设计的高校学生。包内共20个文件大小5.87MB包含3个Python脚本、4个CSV数据文件、4个TXT文本、3个ZBAK备份文件以及图片、词云图等。脚本覆盖评论采集、数据清洗、情感分类与可视化全流程可应用朴素贝叶斯、支持向量机等机器学习方法对正面、中性、负面评论进行分类CSV文件记录了原始评论、处理后评论、情感分析结果及在线购物分类数据集TXT文件与README提供了说明与备份内容。已有77人学习下载。资料提供了可直接运行的代码和配套数据便于读者理解实现思路快速复现实验或进行二次开发也可作为电商评论挖掘项目的参考方案。1. 京东商品评论的情感分析从一条差评到一张可视化图表的完整链路很多人以为爬虫项目就是把网页拉下来存成Excel其实真正值钱的部分全在数据落地之后。这个基于Python的京东商品评论爬取与情感分析项目完整地走通了一条电商评论数据处理的链路爬虫采集商品评价数据清洗去噪再用朴素贝叶斯模型对评论文本做情感分类最后用WordCloud和Matplotlib输出词云图、情感比例图。它不是一个玩具Demo而是一份把爬虫、NLP、数据可视化三个方向串起来的大作业级项目恰好适合正在学Python、想找一个能写在简历上的实战项目的开发者。你将会看到5000条评论从抓取到得出结论代码量其实不到400行。2. 技术栈与工程结构requests jieba 朴素贝叶斯的经典组合2.1 技术选型为什么是这几个库而不是更时髦的方案这个项目的核心诉求是低成本跑通全流程所以技术选型走的都是Python生态里最成熟稳重的方案。爬虫层用requests配合正则表达式或BeautifulSoup解析HTML没有引入Scrapy框架因为单商品的评论采集用不到分布式调度requests完全够用数据清洗层直接用标准库csv和re配合pandas做DataFrame级别的去重与标准化分词和情感分析用的是jieba分词库和自训练的朴素贝叶斯分类器。这套组合几乎没有 黑匣子——朴素贝叶斯的训练代码在train.py里完整可见不像深度学习模型那样训完只能当黑盒调用。而且sentiment.marshal.3这个文件说明模型是用Python的marshal模块序列化保存的训练一次之后加载速度极快完全符合大作业场景下跑得动、看得懂、改得了的三重需求。比起用现成的SnowNLP或者HuggingFace模型自训练分类器在数据量小的情况下泛化能力可能弱一些但胜在每一步都可解释。2.2 目录结构解析每个文件在链路中扮演的角色拿到这个项目压缩包后第一件事是解开看目录结构。根目录下文件不算多但每份文件都不是摆设jd_comment.py是爬虫主脚本负责从京东商品页抓取评论数据sentiment_analysis.py是情感分析脚本读入清洗后的数据调用模型输出情感分类train.py是模型训练脚本用标注语料训练朴素贝叶斯分类器并保存三个CSV文件分别对应链路中的不同阶段。project/ ├── jd_comment.py # 爬虫采集评论原始数据 ├── sentiment_analysis.py # 情感分析分类并生成可视化 ├── train.py # 模型训练训练朴素贝叶斯分类器 ├── jd_comment.csv # 原始评论数据 ├── processed_comment_data.csv # 清洗后的评论数据 ├── result.csv # 情感分析结果 ├── positive.txt # 正例训练语料 ├── negative.txt # 负例训练语料 ├── jd_ciyun.jpg # 词云图输出 ├── requirements.txt # 依赖清单 └── README.md # 项目说明从文件命名就能看出数据流的走向爬虫产出原始数据清洗脚本或pipeline产出processed文件情感分析脚本消费processed文件并产出result.csv和可视化图片。有意思的是online_shopping_10_cats.csv这个文件——它保存了10个电商商品类目的信息说明爬虫可能不只支持单商品评论抓取还预留了按类目批量采集的扩展接口。README里的requirements.txt.zbak和train.py.zbak这些备份文件也从侧面说明了开发过程中迭代过多次保留了不同版本的依赖环境。2.3 依赖环境与版本兼容性最容易被忽略的暗坑requirements.txt是这个项目能否跑起来的第一道关卡。里面锁定的库版本会直接影响后续所有代码的执行尤其要注意jieba和pandas的版本匹配。如果直接用pip install -r requirements.txt安装失败常见原因有两个一是Python版本过高3.10以上导致某些旧版本库没有预编译wheel包二是项目原本运行在32位Python环境下现在换到64位环境后路径或数据格式有差异。提示如果你用的是Python 3.9及以上版本建议把numpy和pandas的版本号在requirements.txt里放宽改成pandas1.3这种写法避免因为严格锁定旧版本而装不上。我在本地跑这个项目时遇到过sentiment.marshal.3加载报错的怪事排查半天发现是训练脚本里保存模型和情感分析脚本里加载模型用的Python版本不一致marshal序列化对解释器版本敏感。所以后续每次重训模型时都强制用同一个虚拟环境执行train.py和sentiment_analysis.py这个习惯后来帮我在其他项目里少踩了很多坑。3. 爬虫模块拆解从URL构造到CSV落盘的完整链路3.1 京东评论接口的数据结构分析京东商品评论的接口请求比想象中更直白。通过浏览器开发者工具切换到 Network 面板刷新评论区后能看到一个名为productPageComments的异步请求它的URL参数包含了productId、score、sortType、page、pageSize等关键字段。评论数据以JSON格式返回其中comments数组里每一条评论的content字段就是需要的评价文本creationTime字段是评价时间productColor是购买的商品颜色规格。代码里用requests.get()请求这个接口时一个关键点是必须携带完整的Referer头指向商品详情页URL否则会被京东的反爬策略拒之门外。另一个容易被忽略的点是pageSize参数——京东接口单次最多返回50条评论拉满这个值可以显著减少请求次数同时翻页逻辑要从0开始计数第一页的索引是0而不是1。import requests import json import time import csv # 构造请求头模拟浏览器行为 headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36, Referer: https://item.jd.com/100012345678.html, Accept: application/json, text/plain, */* } def fetch_comments(product_id, max_pages10): all_comments [] for page in range(max_pages): url https://club.jd.com/comment/productPageComments.action params { productId: product_id, score: 0, # 0全部, 1差评, 2中评, 3好评 sortType: 5, # 5按时间排序 page: page, # 从0开始翻页 pageSize: 50, # 单页最多50条 isShadowSku: 0, fold: 1 } resp requests.get(url, headersheaders, paramsparams) data resp.json() comments data.get(comments, []) if not comments: break for c in comments: all_comments.append({ id: c.get(id), content: c.get(content, ).strip(), time: c.get(creationTime), score: c.get(score), productColor: c.get(productColor) }) print(f第 {page1} 页采集完成累计 {len(all_comments)} 条) time.sleep(2) # 控制请求频率避免触发反爬 return all_comments这里请求参数的设定直接决定了数据质量。score参数设成0表示拉取全部评价如果只想做正负情感对比可以设1和3分别拉差评与好评sortType设成5是按时间排序能拿到最新评价用于趋势分析pageSize固定50是接口上限改成其他值反而会被截断。翻页循环里break的条件是当前页返回的comments数组为空这个逻辑虽然简单却避免了因商品差评不足50条时无限翻页下去。3.2 数据清洗把HTML标签、特殊符号和重复评论一网打尽从接口拿到的content虽然已经是JSON里的纯文本字段但京东的评论内容偶尔会夹杂#10;换行符转义、HTML实体nbsp;或者用户复制粘贴的网页片段。用正则做一轮粗清洗是标准操作同时要去掉纯表情评论长度太短且不含中文内容的直接丢弃最后按content字段做去重。import re def clean_comment(raw_text): # 去掉HTML标签和转义字符 text re.sub(r[^], , raw_text) text text.replace(#10;, ).replace(nbsp;, ).replace(amp;, ) # 去掉URL链接 text re.sub(rhttp\S, , text) # 去掉非中文字符和常见标点之外的字符 text re.sub(r[^\u4e00-\u9fa5a-zA-Z0-9。、\s], , text) # 压缩多余空白 text re.sub(r\s, , text).strip() return text def deduplicate(comments): seen set() unique [] for c in comments: content clean_comment(c[content]) if not content or len(content) 4: # 过滤短评论和表情评论 continue if content not in seen: seen.add(content) c[content_clean] content unique.append(c) return unique清洗时有一个细节值得展开去重的seen集合如果用原始评论做key同一个用户在不同时间发表的同样内容会被误删但用工整后的content做key又可能把「这手机不错」和「这手机真不错」当成两条不同评论。我一般习惯保守一点只对完全一致的文本去重因为京东限购后同一条评论被复制的概率极低。清洗完的数据先写入processed_comment_data.csv再进入分析环节这样中间产物可追溯后面发现分析结果异常时不必重新爬数据。3.3 CSV落盘与数据标准化的坑CSV写出的一个常见坑是中文编码。用csv.writer默认open()的编码在Windows下是gbk写进去中文后Excel能打开但后续用pandas读出来会乱码。项目代码里统一用utf-8-sig编码写文件——注意是utf-8-sig而不是utf-8前者会在文件头写入BOM标记让Excel直接识别UTF-8编码的中文内容。def save_to_csv(comments, filename): with open(filename, w, newline, encodingutf-8-sig) as f: writer csv.DictWriter(f, fieldnames[id, content, time, score, productColor, content_clean]) writer.writeheader() writer.writerows(comments)按这个逻辑跑完爬虫和清洗后processed_comment_data.csv里的每一行就是一条「干净的、可分析的中文评论」。此时中间数据已经是结构化格式后面不管是做情感分析还是做词云消费的都是这份标准化数据不必再回到原始HTML里碰运气。4. 情感分析模块朴素贝叶斯模型的训练、评估与部署4.1 训练语料准备与特征向量化情感分析的核心是train.py这个脚本。它读入positive.txt和negative.txt两份标注语料前者是正面的「好用」「物流快」「性价比高」后者是负面的「质量差」「退货」「客服态度恶劣」。朴素贝叶斯分类器不直接处理字符串所有输入必须先转成特征向量——这里用的特征是词袋模型加 TF-IDF 权重。import jieba from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.naive_bayes import MultinomialNB from sklearn.model_selection import train_test_split import marshal def load_corpus(pos_file, neg_file): texts, labels [], [] with open(pos_file, r, encodingutf-8) as f: for line in f: texts.append(line.strip()) labels.append(1) # 1表示正面 with open(neg_file, r, encodingutf-8) as f: for line in f: texts.append(line.strip()) labels.append(0) # 0表示负面 return texts, labels texts, labels load_corpus(positive.txt, negative.txt) # jieba分词 TF-IDF特征 vectorizer TfidfVectorizer(tokenizerjieba.lcut, max_features5000) X vectorizer.fit_transform(texts) # 划分训练集与测试集8:2 X_train, X_test, y_train, y_test train_test_split(X, labels, test_size0.2, random_state42) # 训练多项式朴素贝叶斯分类器 clf MultinomialNB(alpha0.1) clf.fit(X_train, y_train) print(f测试集准确率: {clf.score(X_test, y_test):.2f}) # 保存模型和向量器 with open(sentiment.marshal.3, wb) as f: marshal.dump({ model: clf, vectorizer: vectorizer }, f)这段代码里的几个参数直接影响分类效果。max_features5000限制词典大小防止对训练集过拟合——语料只有几百条时特征维度过大会导致每个词都被当成强特征alpha0.1是拉普拉斯平滑系数调小这个值让模型更依赖数据本身调大则给所有未出现的词更高保底概率我的经验是数据量小的时候alpha在0.05到0.2之间试效果差不多random_state42固定随机种子保证了每次跑出来的准确率可复现这个习惯在论文和大作业里都很重要。4.2 情感推断与结果输出从清洗数据到result.csvsentiment_analysis.py承接上一步加载模型后逐条读取processed_comment_data.csv对每条评论的执行流程是先jieba.lcut分词再用训练时保存的TfidfVectorizer做同样的向量化变换最后喂给朴素贝叶斯分类器得到情感标签。注意这里不能重新fit向量器必须用transform沿用训练时的词汇表否则新评论里出现未登录词会导致维度不匹配。import csv import jieba import marshal from collections import Counter # 加载训练好的模型和向量器 with open(sentiment.marshal.3, rb) as f: obj marshal.load(f) clf obj[model] vectorizer obj[vectorizer] def predict_sentiment(text): tokens jieba.lcut(text) vec vectorizer.transform([ .join(tokens)]) prob clf.predict_proba(vec)[0] label clf.predict(vec)[0] return label, float(max(prob)) results [] # 读取清洗后的评论数据 with open(processed_comment_data.csv, r, encodingutf-8-sig) as f: reader csv.DictReader(f) for row in reader: label, confidence predict_sentiment(row[content_clean]) results.append({ content: row[content_clean], sentiment: 正面 if label 1 else 负面, confidence: round(confidence, 4) }) # 统计情感分布 counts Counter(r[sentiment] for r in results) total len(results) print(f评论总数: {total}正面: {counts[正面]}{counts[正面]/total:.1%}负面: {counts[负面]}{counts[负面]/total:.1%}) # 写入最终结果 with open(result.csv, w, newline, encodingutf-8-sig) as f: writer csv.DictWriter(f, fieldnames[content, sentiment, confidence]) writer.writeheader() writer.writerows(results)输出结果里除了情感标签还保留了置信度confidence这一步是我强烈建议保留的。置信度低于0.6的评论往往是中性评价或反讽表达把它们单独筛出来人工复核比直接全量采纳预测结果要靠谱得多。很多大作业只输出标签不输出概率导致模型误判时无从排查多这一列数据就能画出置信度分布图定位模型在哪些表达上摇摆不定。4.3 词云与情感可视化把结果画成人话可视化部分用的两张图一是词云jd_ciyun.jpg二是情感比例条形图。词云由一个高频词字典驱动代码先统计正面评论里出现频次最高的前30个词然后用WordCloud生成图片。这里有个容易翻车的细节jieba分词结果里会混入「了」「的」「非常」这类无实际意义的词必须在统计前用一份停用词表过滤掉。from wordcloud import WordCloud import matplotlib.pyplot as plt plt.rcParams[font.sans-serif] [SimHei] # 解决中文显示乱码 stopwords {了, 的, 是, 很, 也, 就, 都, 而, 及, 与, 着, 或, 一个, 没有} def build_word_cloud(comments, stopwords, output_file): word_counts Counter() for c in comments: tokens jieba.lcut(c) for t in tokens: if t not in stopwords and len(t) 1 and not t.isdigit(): word_counts[t] 1 wc WordCloud( width800, height600, background_colorwhite, font_pathC:/Windows/Fonts/simhei.ttf, # 指定中文字体 max_words200 ) wc.generate_from_frequencies(dict(word_counts)) wc.to_file(output_file) # 分离正负面评论 positive_comments [r[content] for r in results if r[sentiment] 正面] negative_comments [r[content] for r in results if r[sentiment] 负面] build_word_cloud(positive_comments, stopwords, jd_ciyun.jpg)词云图和情感比例图都要注意中文字体路径问题——Windows系统一般是simhei.ttf黑体Linux服务器上要换成wqy-microhei.ttc或本地安装的中文字体路径。否则WordCloud会输出一堆积木一样的方框看起来像乱码这一行的坑在几乎所有中文词云项目里都存在。情感比例图用Matplotlib的barh横向条形图比饼图更直观两张图配在README里整个分析报告顿时就立体了。5. 避坑与排查反爬拦截、编码错乱与数据漂移5.1 现象爬虫请求频繁被403拦截第一次跑爬虫脚本时前几页评论能正常抓到翻到三四页就突然批量返回HTTP 403。原因不是IP被封而是请求频率太高——加载评论接口后立即翻页间隔不到0.5秒触发了京东的反爬限流。解决方式是给翻页循环加time.sleep(2)到time.sleep(5)的随机延迟并每次请求前重新构造headers里的Referer指向当前商品页面。如果你需要加更大规模的评论量还需要引入代理池轮换IP但大作业场景下每天只跑一次sleep策略完全够用。5.2 现象pandas读入CSV后中文全部变成乱码写入用了utf-8-sig编码但读取时用了pandas.read_csv(filename)默认编码在Python 3的某些版本下默认是utf-8与写入的BOM头不匹配导致第一列字段名出现\ufeff前缀。解决方法有两个一是统一读取时也加encodingutf-8-sig二是干脆不用pandas读改回csv.DictReader标准库读取。5.3 现象训练模型每次跑出来准确率波动很大这个问题的根源在于没有固定随机种子train_test_split每次划分的训练/测试集都不一样。准确率在0.7到0.9之间反复横跳根本没法作为实验结论写进报告。解决方式是在train.py里设置random_state42并且在重训后清空旧的sentiment.marshal.3文件确保加载的是最新的模型文件而不是被缓存覆盖的旧文件。5.4 现象新评论里出现训练语料完全没有的词当情感分析脚本报维度不匹配错误时通常是新评论里出现了一个训练时词典里没有的词语导致transform输出的向量特征维度变小。这不是bug而是TfidfVectorizer的设计行为——未登录词不会报错但会被忽略。真正要留意的是如果大量评论都是「绝绝子」「踩雷」这类网络新词而训练语料还停留在旧时代的表达情感分类准确率会肉眼可见地下滑。这时候需要重新扩充positive.txt和negative.txt训练语料而不是去改代码逻辑。5.5 现象词云图片里出现了一堆无意义的单字只过滤了停用词但没过滤单个汉字的场景下的、了、是这些字还是有概率混进词云。原因在于jieba.lcut对句子做精确切分时会把一些单字词单独切出来。修复方法是给build_word_cloud里加len(t) 1的过滤条件并且在停用词表里补充「这」「那」「啊」「呀」这类高频语气词。我的习惯是在断词后先打印出频率最高的30个词人工扫一眼再生成词云这一步可以省下很多反复调停用词表的时间。6. 二次开发从单商品评论到竞品对比分析平台的改造思路这个项目的架构决定了它天生适合往两个方向扩展一个是从单商品变成多商品批量采集另一个是从「情感分类」升级为「属性级情感分析」。前者改的是爬虫参数后者动的是分析模型。多商品扩展的落地方式是把online_shopping_10_cats.csv里的类目信息利用起来不再手写商品ID而是先按类目抓取热销商品列表再对每个商品ID循环调用fetch_comments()。代码改动集中在数据模型上——每条评论需要额外记录product_id和category字段这样最终result.csv就可以按category分组统计情感分布实现「手机类目好评率85%、家电类目好评率92%」这类对比结论。同时注意控制全局请求频率多商品时用同一个sleep时间会让总采集时长线性增长建议按商品逐个采集每个商品间隔30秒避免被平台封禁。属性级情感分析是更有学术价值的改造方向。现在模型只判断整条评论是正面还是负面但如果评论是「屏幕很好但电池不耐用」朴素贝叶斯会不知道如何分配。改造思路先拿jieba.posseg做词性标注抽取出名词短语屏幕、电池、物流、服务作为属性候选然后用每句话中属性词的上下文窗口前面两个词加后面两个词重新做情感分类。这样做出来的结果可以输出一个「属性×情感」的矩阵表直接知道每款手机的痛点集中在哪些部件这份报告的决策价值远超单一情感比例。我在二开过程中养成的习惯是每改一个模块就先跑一遍端到端流程爬20条新数据走完全链路确认输出格式没变再放开全量跑。因为项目里的CSV字段、模型文件、词云参数是紧密耦合的改一处不跑通验证后面往往要连带调试好久。希望这套代码能帮你省掉不少从零写爬虫的时间如果你把它改成了自己的分析框架记得把验证数据的置信度分布检查也纳入流程里——等到某一天你的模型对某条评论给出90%置信度的「正面」标签而你翻出这条评论发现它其实是阴阳怪气的讽刺时你就会明白这一步有多重要。希望帮到你。本文还有配套的精品资源点击获取
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。