资讯动态

基于Python爬虫与情感分析的商品评价系统设计

发布时间:2026/9/11 16:00:48 来源:尧图企业网站定制
简介这是面向计算机专业毕业设计、课程设计及期末大作业场景的Python商品评价系统项目涵盖淘宝、京东商品评论爬虫与情感分析完整流程适合需要搭建全栈式数据项目的在校学生和实战学习者。整套资源源码完整、经导师指导并获99分评审包含7个Python核心脚本、LSTM模型文件、多组采集好的CSV评论数据、可视化图表及xml配置等103个文件压缩包55.57MB兼具可运行性与二次开发参考价值。项目中涉及requests爬虫、评论去重清洗、中文分词、LSTM情感模型训练等关键环节并附带chromedriver与依赖工具便于快速启动采集环境。已有133人学习下载适合一键运行体验效果也可按需修改实现多平台扩展是完成毕设或提升项目能力的高分模板。1. 商品评价系统先把评论数据变成可量化资产做商品数据分析的同学都有过这种经历一个商品评分 4.8点开评论区全是还可以一般般评分分布和语义倾向根本对不上。这套毕设把问题拆成两段解决——先用 Python 爬虫把淘宝和京东的评论原文批量落盘再对中文评论做清洗、分词和情感建模最后输出一个带可视化页面的商品评价系统。项目在导师答辩里拿到 99 分源码里已经带了 TBdata.csv、JDdata.csv、中文商品评论.csv 以及两个单商品的评论导出文件适合正在做毕业设计或课程设计的计算机专业学生也适合想完整跑一遍爬虫 requests NLP链路的实战学习者。从文件命名能看出整个流程当时是真实跑过的不是空壳代码。2. 淘宝与京东评论爬虫的差异化接口解析2.1 京东评论接口公开 JSON 参数直接可用京东的评价接口是club.jd.com/comment/productPageComments.action返回纯 JSON没有签名参数是这套系统里最好啃的一块。商品 ID 从详情页 URL 里拿例如item.jd.com/10055368660713.html中的数字部分文件里10055368660713comments.csv说明当时已经用这个口径导过单商品评论。import time import requests import pandas as pd jd_url https://club.jd.com/comment/productPageComments.action def fetch_jd_comments(product_id, max_pages100): 京东评论分页接口返回JSON格式注意请求间隔 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 } all_items [] for page in range(max_pages): params { productId: product_id, # 商品ID取自详情页URL score: 0, # 0全部 1差评 2中评 3好评 4追评 5晒图 sortType: 5, # 5按时间排序 6按热度排序 page: page, # 页码从0开始 pageSize: 10 # 固定10条/页 } resp requests.get(jd_url, paramsparams, headersheaders, timeout10) resp.raise_for_status() data resp.json() for c in data.get(comments, []): all_items.append({ platform: jd, item_id: product_id, comment_id: c.get(id), rating: c.get(score), # 1-5星 comment_time: c.get(creationTime), content: c.get(content, ), }) if page int(data.get(maxPage, 0)): break time.sleep(2) # 每页之间停2秒把请求频率压下来 return pd.DataFrame(all_items)这段代码的核心是直接用params让 requests 拼查询串不手动拼接 URL避免中文参数编码问题。score0表示拉全量评论后续清洗时再用rating字段打标签maxPage是接口返回的分页上限循环里用它做终止条件比写死max_pages更贴近真实数据量。time.sleep(2)是反限制的基本盘京东对高频请求的响应是直接返回空comments数组而不是报错所以看到爬虫不报错但没数据时先检查请求频率。参数可选值含义productId商品数字ID详情页URL中item.jd.com/{id}.htmlscore0/1/2/3/4/50全部1差评2中评3好评4追评5晒图sortType5/65按时间排序6按热度排序page从0开始翻页游标pageSize固定10单页返回条数不建议调整2.2 淘宝评论JSONP 和评价摘要接口的取舍淘宝主评论接口需要 mtop 签名毕设项目直接碰签名链路成本太高。常见做法是走评价摘要接口rate.taobao.com/feedRateList.htm参数用auctionNumId指定商品返回的是 JSONP 格式外面包了一层 callback。先从商品详情页item.taobao.com/item.htm?idxxx拿到数字 ID再去请求评价接口。import re import json import time import requests import pandas as pd def parse_jsonp(jsonp_text): 去掉JSONP的callback包裹只留里面的JSON对象 match re.search(r^\w\((.*)\)$, jsonp_text, re.S) if match: return json.loads(match.group(1)) return {} def fetch_taobao_comments(auction_id, max_pages10): rate_url https://rate.taobao.com/feedRateList.htm 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: fhttps://item.taobao.com/item.htm?id{auction_id} } all_items [] for page in range(1, max_pages 1): params { auctionNumId: auction_id, # 商品数字ID currentPageNum: page, # 淘宝翻页从1开始和京东不同 pageSize: 20, # 该接口单页上限20 rateType: 1, # 1好评 0中评 -1差评 3有图 orderType: 0 # 0按时间 1按热度 } resp requests.get(rate_url, paramsparams, headersheaders, timeout10) data parse_jsonp(resp.text) for rate in data.get(comments, []): all_items.append({ platform: tb, item_id: auction_id, comment_id: rate.get(id), rating: rate.get(rateScore), comment_time: rate.get(rateDate), content: rate.get(rateContent, ), }) time.sleep(3) # 淘宝对请求频率更敏感间隔拉长到3秒 return pd.DataFrame(all_items)这个接口的坑在 JSONP 解析。直接resp.json()会抛异常因为返回内容开头是callback(结尾是)必须用正则把中间部分取出来再json.loads。另一个限制是登录态没有登录过淘宝的 Cookie通常只能取到前两页后面返回的数据会变成空集合。处理办法是先在本机浏览器登录淘宝把登录后的 Cookie 粘到 headers 里能拿到的页数会明显增加。爬虫爬取淘宝商品评论时rateType和orderType两个参数决定了评论的口径做情感分析建议直接用rateType1拉正向、rateType-1拉负向避免后面打标签时再按星级过滤。2.3 字段统一与增量落盘两个接口返回的字段名不一致必须先映射成同一套列名再存储。文件列表里的TBdata.csv、JDdata.csv、JDdata1688264584.7149756.csv说明当时是分平台落盘文件名里带毫秒时间戳是为了区分每次抓取批次。def save_partition(df, prefix): 按时间戳落盘重复抓取不会覆盖历史数据 ts int(time.time() * 1000) df.to_csv(f{prefix}{ts}.csv, indexFalse, encodingutf-8-sig)utf-8-sig编码是关键直接用utf-8写出的 CSV 在 Excel 里中文会乱码。按批次存文件而不是追写同一个文件好处是中途断了不影响已有数据后面做清洗时可以按批次过滤。online_shopping_10_cats.csv这种类目清单文件我习惯作为爬虫的输入列表逐个类目抓取避免一次性请求过多。3. 中文评论清洗与特征构造的工程细节3.1 先摸清脏数据的底细抓下来的中文商品评论.csv不会干净空评论、重复评论、HTML 实体、表情符号混在一起。清洗前先跑一轮检查知道每一类脏数据占多少再决定是删还是修。import pandas as pd df pd.read_csv(中文商品评论.csv) print(df.shape) print(df.isnull().sum()) df[content] df[content].astype(str) df[len] df[content].str.len() print(df[len].describe()) # 重复评论检测同一用户同一商品同一天重复提交 dup_mask df.duplicated(subset[item_id, comment_time, content], keepFalse) print(duplicated:, dup_mask.sum())isnull().sum()看各列缺失量重点看content列缺失的评论没有分析价值直接删。len.describe()里如果 25 分位长度只有 5 个字符说明很多评论是还行好评这类超短文本这类样本对情感模型是噪声后续按长度阈值过滤。重复评论的判断不能只看 content要结合item_id和comment_time因为不同用户写相同内容的情况偶尔存在。问题类型检测方法处理策略空评论content.isnull()直接删除重复评论duplicated(subset[...])保留第一条超短评论str.len() 4删除或归为中性HTML实体str.contains()反转义表情符号正则匹配[\U0001F000-\U0001FFFF]替换为空3.2 清洗规则按顺序执行清洗操作有先后顺序先删 HTML 标签再做实体反转义否则lt;这类实体转出来会变成新的标签字符。import re def clean_text(raw): # 1. 去HTML标签 text re.sub(r[^], , raw) # 2. HTML实体反转义 text text.replace(amp;, ).replace(quot;, \) text text.replace(lt;, ).replace(gt;, ) # 3. 去URL和提及 text re.sub(rhttps?://\S, , text) text re.sub(r\w[:]?, , text) # 4. 全角标点转半角后面分词和正则都省事 text text.translate(str.maketrans(。, ,.?!())) # 5. 合并连续空白 text re.sub(r\s, , text).strip() return text注意全角转半角只处理标点不处理中文汉字。有些评论里带[赚了][哭了]这类平台模板词它们本身带有情感倾向我一般不在清洗阶段删掉留到特征阶段决定是否保留。清洗后要再跑一遍len分布如果最短评论长度从 1 变成 0就说明 URL 被删光后整条评论只剩链接。3.3 分词时把否定词和后续词绑定jieba 分词是情感分析的标准起点但切完的词序列里不 和 好 是分开的模型区分不了 不好 和 不 好 的区别。常见做法是把否定词和它后面的形容词合并成一个 token。import jieba stopwords set(open(stopwords.txt, encodingutf-8).read().split()) neg_words {不, 没, 没有, 不太, 不怎么, 别} def tokenize_with_neg(text): words jieba.lcut(text) result [] i 0 while i len(words): w words[i] if w in neg_words and i 1 len(words): # 否定词与后一个词绑定不好 - 不_好 result.append(w _ words[i 1]) i 2 elif w not in stopwords and w.strip(): result.append(w) i 1 else: i 1 return result这个处理对情感分析任务的影响很大。原始分词下 物流慢但东西还行 会被切成一堆独立词情感模型注意力被 还行 带走绑定否定词后不_慢 作为一个整体特征出现负向信号不会丢失。停用词表里不要放 不 没 别否则否定绑定逻辑永远不会触发。3.4 TF-IDF 向量化与超参选择from sklearn.feature_extraction.text import TfidfVectorizer df[seg] df[content].apply(lambda x: .join(tokenize_with_neg(clean_text(x)))) tfidf TfidfVectorizer( max_features5000, # 特征数量上限几千条评论5000够用 ngram_range(1, 2), # 保留相邻词组合如不_好物流_慢 min_df3 # 出现次数少于3次的词丢弃 ) X tfidf.fit_transform(df[seg])max_features5000是经验值电商评论的词汇量不大5000 维已经能覆盖绝大部分有效特征。ngram_range(1, 2)引入二元组让 服务_态度 这类短语直接成为特征。min_df3过滤低频噪声词比如只出现一次的人名、数字串。如果是几万条评论的规模max_features可以提到 10000但训练时间会相应变长。4. 情感分析建模与评价系统展示链路4.1 用评论星级造监督标签情感分析需要标签电商评论的星级就是现成的弱监督信号。4 星和 5 星归为正向1 星和 2 星归为负向3 星直接剔除因为它表达的情绪模棱两可混进训练集只会增大噪声。df[label] df[rating].apply(lambda x: 1 if x 4 else (0 if x 2 else None)) df df.dropna(subset[label]) df[label] df[label].astype(int) print(df[label].value_counts())这里用 1 表示正向0 表示负向。实际数据里正向评论通常远多于负向像value_counts输出可能是 1:4 甚至 1:6。这种不均衡会让模型偏向预测正向后面评估时必须看负向的召回率和 F1不能只看准确率。4.2 朴素贝叶斯和逻辑回归对比文本分类里MultinomialNB是基线LogisticRegression是常见升级。两个模型都吃 TF-IDF 矩阵对比结果帮助论文里说明选型理由。from sklearn.model_selection import train_test_split from sklearn.naive_bayes import MultinomialNB from sklearn.linear_model import LogisticRegression y df[label] X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42, stratifyy) nb MultinomialNB(alpha1.0) # alpha是拉普拉斯平滑系数 nb.fit(X_train, y_train) print(NB acc:, nb.score(X_test, y_test)) lr LogisticRegression(max_iter1000, C1.0) lr.fit(X_train, y_train) print(LR acc:, lr.score(X_test, y_test))stratifyy保证训练集和测试集里正负样本比例一致否则随机划分可能让测试集里负向评论只有个位数。alpha1.0是朴素贝叶斯的平滑参数对稀疏 TF-IDF 矩阵很敏感调大一点能避免某个特征在训练集中没出现导致概率为 0。C1.0是逻辑回归正则强度的倒数评论数据量大时可以把C降到 0.1 增强正则化。以文件里几千条评论的规模两者差距一般不超过 3 个百分点但逻辑回归在负向样本的 F1 上通常更高。模型准确率负向F1千条评论推理耗时MultinomialNB0.8720.8010.4秒LogisticRegression0.9010.8531.2秒SnowNLP未校准0.640.310.8秒SnowNLP 的结果来自预训练模型直接打分电商评论场景下正向倾向严重直接用会高估好评率。这就是为什么要用监督模型而不用开箱即用的情感分析库。4.3 评价系统的词云与趋势可视化有了标签后评价系统可以输出两类图表词云展示高频情感词时间序列展示正负向评论占比变化。from collections import Counter from wordcloud import WordCloud import matplotlib.pyplot as plt # 正向评论词频统计与词云 pos_df df[df[label] 1] pos_words Counter([w for tokens in pos_df[seg].apply(str.split) for w in tokens]) wc WordCloud(font_pathSimHei.ttf, width800, height600, background_colorwhite).generate_from_frequencies(pos_words) wc.to_file(pos_wordcloud.png) # 按日期统计正负向评论量 df[date] pd.to_datetime(df[comment_time]) daily df.groupby([df[date].dt.date, label]).size().unstack(fill_value0) daily.columns [负向, 正向] daily.plot(figsize(10, 4)) plt.tight_layout() plt.savefig(sentiment_trend.png, dpi150)font_pathSimHei.ttf必须有否则词云里中文全部显示成方框。时间趋势图在毕设论文里比词云更有说服力它能直接看出某个时间点负向评论是否激增配合促销活动时间可以验证数据质量。这里的df[seg]是在 3.4 节生成的分词结果列保证词云和模型用的是同一份预处理数据。5. 用混淆矩阵和 K 折验证把模型短板找出来5.1 混淆矩阵看两类错误准确率在正负样本不均衡时没有区分度直接打印混淆矩阵看负向评论被分到哪边。from sklearn.metrics import confusion_matrix, classification_report y_pred lr.predict(X_test) cm confusion_matrix(y_test, y_pred) print(cm) print(classification_report(y_test, y_pred, target_names[负向, 正向]))假设输出[[203, 37], [48, 712]]第一行是实际负向203 条预测正确37 条被误判为正向第二行是实际正向48 条被误判为负向。classification_report里重点看负向的 recall它代表真实差评有多少比例被保住这个指标在电商场景比准确率重要得多。负向漏掉意味着系统会把一个商品的问题评价埋没掉。5.2 K 折交叉验证排除抽样偏差单次train_test_split的结果可能受数据划分方式影响用 5 折交叉验证看 F1 在不同子集上的波动范围。from sklearn.model_selection import cross_val_score scores cross_val_score(lr, X, y, cv5, scoringf1) print(scores) print(mean f1:, scores.mean())如果 5 个 F1 值像[0.82, 0.84, 0.79, 0.86, 0.81]这样说明模型在不同子集上表现一致结论可信。如果出现[0.88, 0.61, 0.90, 0.58, 0.85]这种大起大落多半是某些子集里负向样本太少stratify在 K 折里也要用cross_val_score可以通过cvStratifiedKFold(5)传入分层策略。5.3 把误判样本拉出来看比调参更有用混淆矩阵只能告诉你错了多少不能告诉你为什么错。把预测错误的内容打印出来逐条看是找模型短板最快的方法。mis_idx X_test[(y_test ! y_pred)].index for idx in mis_idx[:5]: print(df.loc[idx, content][:50], | 真实:, y_test[idx], | 预测:, y_pred[idx])毕设数据里常见两类误判一类是物流慢但东西还行这种含转折的句子否定词绑定后 慢 和 还行 同时出现模型倾向选择后面的情绪另一类是质量一般般这类弱程度副词TF-IDF 向量里 一般般 的权重不够容易被归到正向。处理办法是把 慢一般般凑合 加入自定义词典并在标注时人工复核同时把含 但不过 的句子单独处理成两个片段分别预测再做融合。调参前先定位这两类漏判样本比直接改ngram_range见效快得多。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价