Python爬虫+情感分析实战:构建淘宝京东商品评价可视化系统 📅 发布时间:2026/9/14 1:26:50 👁 浏览次数: 简介面向本科毕业设计、高职期末大作业或Python数据分析方向课程实践这是一套完整的商品评价系统项目基于Python爬虫与情感分析实现以淘宝、京东商品评论为数据源覆盖爬虫采集、数据清洗、情感建模与结果可视化全流程。压缩包共103个文件主体为Python源码py和评论数据集csv并配有深度学习模型文件pt/lstmmodel、配置说明xml/txt/md、可视化结果图jpg/png/svg以及运行辅助工具exe/chromedriver整体约55.57MB目录结构便于按模块学习内含淘宝、京东多个商品类目的中文评论数据可直接用于模型训练与对比实验。目前已有218人学习下载。源码均经本地编译验证可运行评审分达到95分以上内容由助教审定难度适中既适合作为毕业设计直接交付也便于读者快速上手爬虫与情感分析实践可基于现有代码扩展采集渠道、优化模型或增加可视化展示。1. 一个“爬虫情感分析可视化”选题为什么值得照着做一遍在毕业设计列表里刷到“基于Python淘宝、京东爬虫及商品评论情感分析的商品评价系统”这类标题时内核其实非常固定用 Python 爬虫把淘宝、京东的商品与评论抓下来清洗后做中文情感分析最后用 Flask 或 Django 搭一个带图表的评价系统。技术栈里每一步都有成熟轮子但它仍然值得手写一遍因为难点不在“会不会调库”而在把数据、算法、页面三层串起来时爬虫采集字段怎么定、评论情感分怎么和星级互相印证、数据库和图表怎么对应。这篇内容就按这条主线拆开讲每个环节给出可复现的命令、参数和取舍理由。2. 淘宝、京东商品评论爬虫字段、反爬与并发设计2.1 抓取之前先把评论表结构定下来淘宝和京东的评论接口返回的都是 JSON字段命名差异不小但整理到业务层之后一张评论表基本能装下。常见做法是先把落库字段定死再回头写解析逻辑否则每改一次字段爬虫和页面都要跟着动。以商品评价系统为例商品侧至少保留商品标题、商品链接、店铺名、价格、总评论数评论侧保留评论用户、用户等级、评分、评论内容、购买属性颜色/尺码、评论时间、点赞数、追评内容。不需要一次抓全因为评论接口一般只给最近几页真正做情感分析时“评论内容”“评分”“评论时间”三个字段是核心。建表时字段长度要按平台习惯留余量评论内容在 MySQL 里用TEXT追评单独一列而不是拼进评论内容否则后续清洗分词时要反复切割。下面这份 SQL 是前期最容易直接抄过去用的版本CREATE TABLE product ( id INT AUTO_INCREMENT PRIMARY KEY, platform ENUM(taobao,jd) NOT NULL, product_id VARCHAR(64) NOT NULL, title VARCHAR(255) NOT NULL, shop_name VARCHAR(128) DEFAULT , price DECIMAL(10,2) DEFAULT 0, comment_count INT DEFAULT 0, url VARCHAR(512) DEFAULT , UNIQUE KEY uk_platform_product (platform, product_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE comment ( id INT AUTO_INCREMENT PRIMARY KEY, product_id INT NOT NULL, user_name VARCHAR(128) DEFAULT , rating TINYINT NOT NULL, content TEXT, sku_info VARCHAR(255) DEFAULT , comment_time DATETIME NULL, like_count INT DEFAULT 0, follow_up TEXT, raw_id VARCHAR(128) DEFAULT , UNIQUE KEY uk_raw (raw_id), KEY idx_product_time (product_id, comment_time) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;product_id用平台自己的商品 ID不要用自增主键当唯一标识因为爬虫断点续传时要靠它判断“这条商品抓过没有”。comment.raw_id是评论流水号不同平台规则不同可能是评论 ID也可能是“用户ID时间”的拼接值写解析逻辑时可以把接口返回的多个字段拼起来去重。uk_raw这个唯一索引是后面断点续爬的关键重复写入会被 MySQL 直接拒绝比在 Python 里维护一个 Set 更可靠。2.2 淘宝、京东反爬策略的差别在于数据从哪儿来很多入门教程喜欢展示requests.get一个商品详情页再拿正则抠数据。真实场景里淘宝的商品列表和评论加载基本都是 AJAX京东的商品价格和评价中心也拆成了独立接口直接抓 HTML 反而要处理大量动态渲染逻辑。网络爬虫原理是相似的先看浏览器 DevTools 的 Network 面板找到返回 JSON 的那个接口再用 requests 带上必要的 Header 去请求。两个平台的差异在于淘宝更依赖登录后的 Cookie未登录状态下评论接口的返回内容和完整性都会缩水京东相对容易拿到商品页和评论页的公开数据但部分价格字段同样需要登录身份。反爬设计的重点不是对抗而是降低对目标站点的访问压力。UA、Referer、Cookie 是三个必备 HeaderUA 用来标记浏览器环境Referer 要和当前商品页保持一致Cookie 则决定了登录态和部分风控策略。爬取频率建议按“单商品评论分页间隔 3-5 秒、整站并发不超过 5”的节奏控制一旦出现滑块验证或验证码说明频率已经过高正确做法是停止当前任务、等待一段时间而不是换个请求头继续试。提示触发滑块验证时先停掉整个采集任务而不是只跳过当前商品。滑块出现往往意味着当前访问环境已被风控关注继续运行只会让后续请求全部被拦截。2.3 并发设计选型requests 线程池比分布式实用爬虫并发设计到底哪个好要看数据规模。毕业设计的商品数量通常在几十到几百个评论总量在几万到几十万条之间单机多线程完全能在一个晚上跑完。分布式爬虫的优势在于跨机器调度、共享去重队列和故障转移但部署维护成本高单机版本反而更适合演示和答辩。方案调度复杂度去重方式适用数据量单机多线程低Python Set 数据库唯一键十万级评论Scrapy Redis中Redis 集合去重百万级 多机抓取全量分布式高消息队列 分布式锁持续增量采集下面是一个用 ThreadPoolExecutor 控制并发度的抓取模板评论接口的分页参数会放在请求地址里import time import random import requests from concurrent.futures import ThreadPoolExecutor, as_completed HEADERS { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64), Referer: https://item.jd.com/, } def fetch_comments(product_id, page): params {productId: product_id, page: page, pageSize: 20} resp requests.get(COMMENT_API, paramsparams, headersHEADERS, timeout10) data resp.json() return parse_comment_list(product_id, data) def crawl_product(product_id, max_page10): with ThreadPoolExecutor(max_workers3) as pool: futures {pool.submit(fetch_comments, product_id, p): p for p in range(max_page)} for future in as_completed(futures): rows future.result() save_to_db(rows) time.sleep(random.uniform(1, 3))max_workers3是并发上限不是越大越好线程数过高时接口响应会因为本机连接占满而变慢。random.uniform(1, 3)用于在每页之间加入随机间隔避免出现规律的请求节奏。COMMENT_API是 DevTools 里扒出来的真实评论接口地址不同平台对参数要求不同通常需要页码、商品 ID 和排序方式。单机方案跑完需要 2-3 小时中间断了就靠下一节的去重逻辑续上不需要从头开始。2.4 断点续爬用唯一索引兜住重复数据爬虫跑在个人电脑上很容易因为断网或页面超时中断。断点续爬要做两件事一是记录每个商品已抓取的最大页码二是依赖数据库唯一索引过滤重复评论。商品表的uk_platform_product和评论表的uk_raw已经把数据落库重启时只需要查询某个商品当前最大页码从下一页继续抓如果中途接口返回空列表说明已经翻到底直接把这个商品标记为完成即可。uk_raw的作用在重抓时体现得最明显同一页被重复请求后INSERT 会因为重复 key 失败程序里捕获IntegrityError并跳过不至于中断整个流程。3. 商品评论情感分析清洗、打分与模型对比3.1 先把“默认好评”和广告评论洗干净从平台抓到的评论文本比想象中脏得多。淘宝有大量“此用户没有填写评论”京东有大量系统自动生成的“好评”短句刷单行为会产生同一文案的重复评论商品推广文案则夹杂微信号和 URL。情感分析先要处理这些噪声否则词典或模型会把它们当作正常语句参与打分。清洗规则一般按四步走去 URL 和 HTML 标签、过滤内容长度小于 4 的短评、把相同文本的评论只保留一条、对“好评”“很好”“宝贝很好”这类模板句单独建黑名单词表。opencc 可以把繁体转简体全角符号转半角用 Python 的unicodedata即可下面这段清洗脚本可以直接融合到爬虫的解析函数里import re import unicodedata TEMPLATE_WORDS {此用户没有填写评论, 好评, 很好, 宝贝很好, 物流很快} def clean_comment(text): if not text: return text unicodedata.normalize(NFKC, text) text re.sub(r[^], , text) text re.sub(rhttps?://\S, , text) text text.strip() if text in TEMPLATE_WORDS or len(text) 4: return return textunicodedata.normalize(NFKC, text)把全角字母数字转成半角也把一些特殊空白统一掉TEMPLATE_WORDS是黑名单凡是完全命中的评论直接丢弃过滤长度小于 4 是为了筛掉“好用”“差评”这种信息量不足的短句。清洗后记得在入库或打分环节把空字符串标记为中性不要直接删行否则商品评论总量会对不上。3.2 情感词典、SnowNLP、BERT 三种方案怎么选清洗过后的评论文本可以用三种方式做情感分析情感词典打分、预训练模型推理、微调模型。情感词典法是把正向词、负向词、否定词、程度副词组合起来计算得分解释性强适合答辩时被追问“这个得分怎么来的”SnowNLP 这类预训练模型开箱即用但电商文本和它的训练语料分布差异较大经常把“到货快”判成中性BERT 微调效果最好不过需要上千条人工标注数据毕业设计工期往往不支持。多模态情感分析是把图片评论也纳入研判对纯文本系统属于扩展项可以先在整体架构上预留图片字段后续再接图像情感模型。方案优点缺点适用规模情感词典可解释、不依赖标注需要人工维护词表百级商品即可用SnowNLP / PaddleNLP推理快、开箱即用电商场景泛化弱大规模文本粗筛BERT 微调准确率高标注成本高、部署重千条以上标注数据情感词典法在真正落地时有个容易被忽略的细节词表必须包含电商高频词比如“速度快”“客服态度好”“做工细致”中的“快”“好”“细致”再配合“太”“很”“不”这类强度词和否定词才能从“不是太差”这类句子里得到正确结果。3.3 可解释的情感打分词典 否定词 程度副词用情感词典做打分核心是把一句话拆成“程度词情感词”“否定词情感词”“独立情感词”三种结构。下面这段代码直接用 jieba 分词后遍历逻辑简单但已经能覆盖大多数电商评论import math import jieba POS_WORDS set([好, 快, 满意, 喜欢, 划算, 精致]) NEG_WORDS set([差, 慢, 失望, 退货, 破损, 不好]) NEGATE set([不, 没, 别, 无]) ADVERBS {很: 1.5, 太: 1.8, 非常: 2.0, 比较: 0.8} def sentiment_score(text): words jieba.lcut(text) score 0.0 for i, word in enumerate(words): if word in ADVERBS and i 1 len(words): nxt words[i 1] if nxt in POS_WORDS or nxt in NEG_WORDS: score ADVERBS[word] * (1 if nxt in POS_WORDS else -1) elif word in POS_WORDS: score 1 elif word in NEG_WORDS: score - 1 elif word in NEGATE and i 1 len(words): nxt words[i 1] score - (1 if nxt in POS_WORDS else -1) return 1 / (1 math.exp(-score))词表在这里被简化成了几组示例真正使用时从公开的中文情感极性词典整理后导入即可。打分逻辑分成三层程度副词情感词优先计算遇到“很不错”会得到 1.5 分否定词情感词会把方向反转“不好”会变成 -1单独情感词兜底给 ±1。最后用 sigmoid 函数归一化到 0 到 1 之间方便和商品星级放在同一个坐标里展示。需要特别说明的是词典法不是算一个分数就结束还要对同一条评论做分句处理因为“质量不错就是物流太慢”这句话里正向和负向应该同时保留。3.4 用预训练模型做交叉校验纯词典法会遇到词典外词预训练模型会遇到电商语境偏移常见做法是让两者互相兜底。对每条评论同时计算词典得分和 SnowNLP 得分两者差值小于 0.2 时取均值如果差值大于 0.3说明存在难以自动判定的文本例如反问句、反讽或带有具体商品名的评论这种评论单独标记人工抽检时优先处理。实现时不需要额外写复杂算法用 pandas 合并两列得分即可。经过处理后最终商品评分既有业务解释性又比单一模型稳健。4. 商品评价系统Flask 页面、指标与可视化4.1 评价系统要交付哪些页面和指标评价系统不是把爬到的评论原样表格化而是围绕“商品值不值得买”提供可读指标。常见页面布局是首页一个商品检索框输入商品 ID 后进入详情页详情页上半部分是商品基础信息和平均情感得分下半部分是情感趋势折线、关键词词云、正面/负面评论对照列表。背后的核心指标只有三个评论总量、平均情感得分、正向/中性/负向占比。再往外延伸可以按 SKU 维度对比款式差评率或按时间维度看最近一周的情感走向。页面核心指标数据来源商品总览平均情感得分、好评率comment 表聚合趋势分析每日情感得分均值comment_time sentiment_score评论洞察高频正面词、负面词分词后的词频统计差评列表低分评论 TopNrating 或情感得分排序这些指标和基于情感分析的电商商品评论大数据挖掘方向一致但要注意情感分是描述性指标它和销量的关联只能说明有趋势不能顺手写成因果关系。演示时可以对比同店铺不同商品的情感分与销量数据但要明确这是相关性分析。4.2 用 SQL 把情感结果聚合到商品维度情感分析跑完后需要把每条评论的得分写回comment表再按商品维度聚合。下面这条 SQL 直接输出商品标题、评论总数、平均情感得分和正向评论占比SELECT p.title, COUNT(c.id) AS comment_total, AVG(c.sentiment_score) AS avg_sentiment, SUM(CASE WHEN c.sentiment_score 0.6 THEN 1 ELSE 0 END) / COUNT(c.id) AS pos_ratio FROM product p LEFT JOIN comment c ON p.id c.product_id GROUP BY p.id ORDER BY avg_sentiment DESC;sentiment_score是第 3 章算出的结果列写入后立刻可以参与统计。pos_ratio使用CASE WHEN统计正向评论比例阈值 0.6 不是固定值需要根据验证集的分布微调否则正向占比会整体偏高。如果想看趋势把分组条件换成DATE(c.comment_time)再对avg_sentiment做 7 日滑动平均曲线会更平滑。Flask 后端可以直接用原生 SQL 配合 pandas 输出 JSON不必为了这个体量引入重型 ORM。4.3 用 pyecharts 生成可视化图表可视化部分最省力的方式是用 pyecharts 在服务端生成图表再注入模板不用在前端手写 ECharts 初始化代码。下面是一个简化版接口from flask import Flask, render_template from pyecharts.charts import Line, WordCloud from pyecharts import options as opts def build_trend_chart(dates, scores): line Line() line.add_xaxis(dates) line.add_yaxis(情感得分, scores) line.set_global_opts(title_optsopts.TitleOpts(title评论情感趋势)) return line.render_embed() def build_wordcloud(words): wc WordCloud() wc.add(, words, word_size_range[20, 80]) return wc.render_embed() app Flask(__name__) app.route(/product/int:pid) def product_detail(pid): dates, scores load_trend_data(pid) words load_top_keywords(pid) return render_template( detail.html, trend_chartbuild_trend_chart(dates, scores), wordcloudbuild_wordcloud(words), )render_embed()会把图表以 HTML 片段形式注入模板省去了前端维护 ECharts 初始化代码的工作。word_size_range控制词云字号范围词频大的词自动放大加载接口返回的words需要用(词, 频次)元组列表。用这种方式Flask 只负责把数据库取出的数据传给图表函数图表函数负责把 option 拼好调试时也能单独运行图表函数看结果。4.4 情感分和星级互相补位矛盾样本单列商品星级有一个固有缺陷很多用户要么给全 5 星要么给 1 星中间档缺失严重。情感分可以作为星级的补充信号当星级为 5 但情感分低于 0.4 时这条评论应进入人工复核列表当星级为 1 但情感分高于 0.7也要警惕是否是差评中带正向表达。这个规则写成查询条件跑一遍全量评论把矛盾样本数统计出来。系统演示时如果数据显示“好评榜”和“差评榜”完全由星级决定评委大概率会追问情感分析的价值所以必须在页面上预留一个“星级与情感矛盾”的筛选器。展示时可以说明这个过滤器的逻辑既能体现工程的严谨性也能直接应对答辩提问。5. 打包交付依赖锁定、一键启动与结果验收5.1 把运行环境装进 requirements.txt毕业设计交付的不是一个人人都装好 Python 环境的教室依赖版本漂移是演示翻车的头号原因。提交前用pip freeze requirements.txt冻结本机版本安装时执行pip install -r requirements.txt。版本号要锁到能跑通的那一次不要只写包名。如果用了自定义数据库配置还需要在 README 里说明 MySQL 的初始化方式和启动顺序。5.2 一键初始化数据库并启动服务常见做法是写一个start.py把建库、建表、启动 Flask 串起来减少手动操作。启动命令和参数可以这样设计# 初始化数据库并启动 python start.py --init-db python start.py --host 127.0.0.1 --port 8000 # 如果没有本地 MySQL用容器替代 docker run -d -p 3306:3306 -e MYSQL_ROOT_PASSWORDyourpass mysql:8--init-db做两件事连接 MySQL 创建 database再按第 2 章的表结构建表。开发时可以直接连接本地数据库交付前把连接参数改成config.ini读取防止把个人密码写死在代码里。--host和--port控制 Flask 监听地址演示时固定用127.0.0.1:8000避免端口冲突。5.3 用抽样标注验证情感分析结果情感分析做没做对不能只看界面截图。建议从正负向结果里各随机抽 25 条人工标注“正向/中性/负向”然后和系统结果比对。50 条样本算出的一致率如果低于 80%优先补词典词而不是换模型因为电商评论里的高频差异词往往集中在“客服态度”“包装”“做工”这几个话题上。对星级和情感分矛盾的评论单独导出做案例能顺手发现“东西一般服务真好”这类双极性评论这类文本在词典法里会互相抵消需要按句子切分后再取重心。验证结束后把星级与情感分矛盾的评论单独建一张表后续重跑情感分析时先匹配这张表人工标注过的句子直接复用剩下的增量才交给模型这样能以很小的成本持续提高演示数据的可信度。本文还有配套的精品资源点击获取