拓冰建站拓冰建站
首页 / 资讯中心 / 正文

基于Python的京东教辅书销售数据分析系统设计与实现

打开毕设选题文档那几天我其实特别焦虑。学校给的题目列表翻来覆去都是图书管理系统、商城系统、某某管理系统要么就是网上已经被写烂了的电影推荐、用户画像。后来我换了个思路不去纠结系统本身而是想找一个有真实业务场景、有数据可挖、还能把整个数据链路串起来的题目。最后锁定了基于Python的京东教辅书销售数据分析系统。这个项目本质上就是用Python爬取京东教辅类商品的公开销售数据经过清洗和分析之后通过Web界面把销量、价格、评价、出版社等维度的规律展示出来。它特别适合计算机、大数据、电商相关专业的毕业设计因为技术栈覆盖了爬虫、数据库、pandas数据处理、可视化、Flask Web开发几乎所有高校课程里学过的核心点都包含了。而且教辅书这个细分方向比泛泛的电商数据分析更有辨识度答辩时能讲的东西也多。如果你也正在纠结毕设选题或者想找一个难度适中、能自己完整做下来又不容易撞题的方向这篇文章就从我的实际项目出发聊聊整个系统的设计和实现过程包括踩过的坑和答辩时被问到的问题。1. 为什么会选京东教辅书销售数据分析当题目1.1 选题背后的业务逻辑教辅书是电商平台里非常特殊的一个类目。它既有普通商品的通用属性价格、评价、店铺又有很强的教育行业特征年级、科目、教材版本、出版社、出版年份。这些属性组合起来能做的分析维度非常丰富。举个简单的例子每年的开学季前后小学奥数、中考真题、高考模拟卷这类商品的搜索量和销量会出现明显的脉冲式变化。如果只做全品类电商分析这种规律会被平均掉但如果聚焦教辅书就能看到清晰的周期性。这种细分领域里能说出具体业务洞察的项目写论文、做答辩都比泛泛的电商数据分析更有说服力。我当时定下这个题目后先在京东上随便搜了几个关键词确认数据是可获取的商品标题、价格、评价数、店铺、出版信息这些都展示在页面上而且数量足够做统计分析。数据是真实业务数据这一点对毕设的含金量非常重要。1.2 毕设选题的三条冷门经验我给正在选题的同学提三条建议都是实际经验。第一条看数据可得性。不要选一个东拼西凑都凑不到1000条真实数据的题目。哪怕数据再漂亮一旦答辩老师要求你现场演示原始数据拿不出来就很尴尬。京东、淘宝这类电商平台的数据量天然充足教辅书搜索页一页几十个商品翻几十页就能攒下几千条。第二条看技术栈完整性。毕设不是工作不需要用到分布式、大数据引擎那种重型技术但一定要能串联起来。爬虫采集、数据清洗、数据分析、可视化展示这四件事覆盖了本科阶段最核心的课程内容。如果这个项目还能带一个Web界面用来展示结果演示效果和评分都会明显提升。第三条看差异化。如果你要做数据分析方向别选某电商平台销售数据分析这种大而空的题目一定要加一个细分场景。教辅书、宠物用品、户外装备只要加上限定词论文的同类研究对比部分就好写得多答辩时也不容易跟其他同学撞题。我见过太多人直接去下载网上的免费python源码大全跑通之后换皮交上去。结果答辩时老师问一句这个图表数据怎么来的就答不上来。数据链路自己亲手跑通的痛苦和价值是换皮项目完全无法替代的。2. 系统总体设计与技术选型先把数据链路搭通2.1 四层架构从采集到展示谁管谁本系统的整体架构分成四层数据采集层用Python写爬虫从京东搜索页和商品详情页采集教辅书信息。数据存储层采集结果统一存到MySQL维护商品信息表、用户表、采集记录表。数据处理与分析层用pandas做数据清洗、特征提取和指标计算用jieba做评价关键词提取。可视化与Web展示层Flask提供后端接口前端用ECharts渲染图表页面采用Dashboard布局。架构一句话概括就是爬虫往MySQL里喂数据pandas从MySQL里取数据算指标Flask把指标以JSON形式返给前端前端把JSON画成图表。这四层之间通过标准SQL和JSON接口解耦哪一层出问题都能单独排查。2.2 为什么是Python Flask MySQL ECharts这个组合是经过对比后才确定的不是随便选的。Python本身不用多说爬虫和数据分析生态最强。Flask选择它的原因很简单够轻。毕设项目不需要多用户、权限体系、后台管理那些重型功能Flask写一个路由就是一个接口前后端分离也方便。如果用Django框架自带的东西很多但学习成本更高在答辩时如果被问到里面的权限中间件怎么实现的反而容易露怯。MySQL存结构化商品数据非常合适。教辅商品信息本质上是关系型数据字段固定表之间有明确关联。MongoDB这类NoSQL虽然在爬虫项目里也常见但对毕设而言学生和老师更熟悉MySQLSQL查询写起来也直观。为了后续分析的方便我将每个商品做成一行记录字段包括sku、标题、店铺、出版社、价格、评价数、好评率、采集时间等。pyecharts是Python生成ECharts图表的库核心价值在于我可以在Python端直接生成图表的option配置传给前端渲染不用手写大量JavaScript。这在毕设开发阶段能节约非常多时间。2.3 数据库表设计字段不要拍脑袋要想到后面怎么分析表设计是最容易偷懒但最不能偷懒的一环。我最初只是把页面上看到什么就存什么结果后期做分析时发现出版社信息在列表页根本没有必须进详情页二次采集流程被打乱了。所以表设计一定要先从分析需求反推。建表SQL如下CREATE TABLE book_product ( id INT PRIMARY KEY AUTO_INCREMENT, sku VARCHAR(32) NOT NULL UNIQUE COMMENT 商品SKU, title VARCHAR(255) NOT NULL COMMENT 商品标题, link VARCHAR(255) COMMENT 商品链接, shop VARCHAR(128) COMMENT 店铺名称, publisher VARCHAR(128) COMMENT 出版社, price DECIMAL(10,2) COMMENT 售价, commit_num INT COMMENT 评价数, good_rate DECIMAL(5,2) COMMENT 好评率(%), publish_date VARCHAR(32) COMMENT 出版时间, grade VARCHAR(16) COMMENT 年级, subject VARCHAR(16) COMMENT 科目, version VARCHAR(32) COMMENT 教材版本, source_keyword VARCHAR(32) COMMENT 来源搜索词, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;注意里面的grade、subject、version三个字段它们是数据分析的重要维度是在清洗阶段从标题里拆出来的采集阶段先置空。sku字段加上唯一索引这样增量更新时可以用INSERT ... ON DUPLICATE KEY UPDATE一个语句完成存在则更新不存在则插入。3. 数据采集京东教辅书爬虫设计与反爬应对3.1 明确采集范围和字段优先级我设定的采集范围是在京东搜索教辅书、初中教辅、高中教辅、小学教辅、一课一练、中考真题、高考模拟等关键词每个关键词采集前30页左右的商品列表。最终有效数据量控制在3000条以上这个规模已经足够做各类统计分析了。字段优先级上第一优先级是sku、标题、价格、评价数因为它们是后续分析的基础第二优先级是店铺、出版信息第三优先级是好评率、出版时间这类不一定每个页面都有、需要单独处理的字段。列表页能抓到sku、标题、价格、店铺、评价数。出版信息、好评率、出版时间在详情页或参数页才有所以我做了一步详情页补采从列表页拿到sku列表再批量抓取详情页。这个过程会显著增加请求量一定要控制频率。3.2 列表页爬虫实现选择器永远要有容错京东搜索页的结构会随着改版变化不要指望一个月前别人博客写的选择器现在还完全有效。我实现时采用了选择器 空值容错的策略确保页面结构小改时程序不会直接崩溃。import requests from bs4 import BeautifulSoup import time import random HEADERS { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36, Accept-Language: zh-CN,zh;q0.9 } def fetch_list(keyword, page): url fhttps://search.jd.com/Search?keyword{keyword}page{page} resp requests.get(url, headersHEADERS, timeout10) soup BeautifulSoup(resp.text, html.parser) lis soup.select(#J_goodsList li.gl-item) items [] for li in lis: sku li.get(data-sku) if not sku: continue title li.select_one(.p-name em) price li.select_one(.p-price i) commit li.select_one(.p-commit a) shop li.select_one(.p-shop a) items.append({ sku: sku, title: title.text.strip() if title else , price: price.get(data-price) or price.text if price else None, commit: commit.text.strip() if commit else 0, shop: shop.text.strip() if shop else , url: fhttps://item.jd.com/{sku}.html }) return items这里面有几个处理细节值得说清楚。第一li.get(data-sku)拿不到sku的直接丢弃因为后续详情页全靠它。第二价格字段优先从>def parse_commit(text): if not text: return 0 text text.replace(, ).strip() if 万 in text: return int(float(text.replace(万, )) * 10000) if not text.isdigit(): return 0 return int(text)3.3 反爬应对不是对抗是克制京东的反爬机制主要是验证码、IP频率限制、账号风控。作为一个毕设项目我的原则不是破解反爬而是让请求频率低到不会被反爬体系注意到。实际采用方案同一关键词翻页之间随机sleep 1.5到3秒不同关键词切换之间sleep 5到8秒。请求头只设置User-Agent和Accept-Language伪装真实浏览器但不堆砌多余请求头。连续请求失败超过3次就终止当前关键词采集记录日志人工判断后再继续绝不自动无限重试。采集时间拉长到几小时分多次运行。这里要特别说明一下合规问题。京东商品页的搜索结果是公开信息爬取公开商品信息用于个人学习研究本身没有越界。但必须控制节奏、不采集用户个人数据、不用于商业用途。论文里也要写遵守robots协议和数据使用规范这句话这是答辩时的基本职业素养。另外京东页面有一些地方提供了>from sqlalchemy import create_engine engine create_engine(mysqlpymysql://root:123456localhost:3306/jd_books?charsetutf8mb4) df pd.DataFrame(items) df.to_sql( book_product, engine, if_existsappend, indexFalse, methodmulti, chunksize200 )注意sku设置了唯一索引但是to_sql遇到重复主键时会整批报错。后来我改用了一条原生SQL做增量更新先把数据写入临时表再用INSERT ... ON DUPLICATE KEY UPDATE合并到主表。这个方案在毕设场景下足够稳定也比先delete再insert安全得多因为不会因为中途失败而把历史数据全删掉。4. 数据清洗与预处理脏数据才是数据分析的必修课4.1 原始数据常见的四个坑采集完的数据直接分析一定会翻车。我第一批数据跑出来就发现了四类典型问题。第一重复数据。同一个商品会被不同搜索关键词反复采到比如初中教辅和七年级教辅会出现大量相同的sku。第二缺失值。价格、出版社、好评率都有可能缺失尤其是第三方店铺的书籍很多没有标注出版社。第三异常值。有一些商品价格是0.01元明显是商家低价引流或者价格采集失败。还有一些评价数高达几十万需要确认是不是刷单或爆款造成的极端值在统计时不能简单放过。第四文本数据里包含了大量营销词比如正版包邮学霸必备秒杀。这些词用词云做人气分析时毫无意义必须在分析前剔除。4.2 清洗流程pandas一行一行把数据捋干净清洗逻辑我用pandas实现核心代码不长但每一步背后都有讲究。import pandas as pd df pd.read_sql(SELECT * FROM book_product, engine) # 1. 去重同一个SKU只保留最新采集的一条 df df.sort_values(update_time, ascendingFalse).drop_duplicates(sku, keepfirst) # 2. 价格转数值低于1元的视为异常抛弃 df[price] pd.to_numeric(df[price], errorscoerce) df df[df[price] 1.0] # 3. 评价数转数值缺失的补0 df[commit_num] df[commit_num].fillna(0).astype(int) # 4. 出版社缺失值用“未知出版社”填充而不是直接删行 df[publisher] df[publisher].fillna(未知出版社) # 5. 好评率缺失值用全样本中位数填充 df[good_rate] pd.to_numeric(df[good_rate], errorscoerce) df[good_rate] df[good_rate].fillna(df[good_rate].median())关于好评率的填充我一开始用的是均值后来发现好评率分布是左偏的大量商品集中在95%到100%之间用均值填充会把缺失值拉得偏低而中位数更接近常态值。这个细节在论文数据预处理章节里写出来反而是加分项。4.3 从商品标题里拆出年级、科目、教材版本教辅书标题的典型格式是2025版初中数学七年级上册人教版教材同步练习这行文本里包含学段、年级、科目、版本四个分析维度。用正则加关键词匹配就能拆出来。import re def extract_features(title): grade subject version grade_pattern re.compile(r(小学|初中|高中)?(一|二|三|四|五|六|七|八|九)年级) m grade_pattern.search(title) if m: grade m.group(0) subject_dict [语文, 数学, 英语, 物理, 化学, 生物, 历史, 地理, 政治, 科学] for sub in subject_dict: if sub in title: subject sub break version_dict [人教版, 北师大版, 苏教版, 外研版, 鲁教版, 沪教版, 湘教版, 冀教版, 译林版] for ver in version_dict: if ver in title: version ver break return grade, subject, version df[[grade, subject, version]] df[title].apply( lambda x: pd.Series(extract_features(str(x))) )这个模块就是论文里的特征工程也是老师和答辩评委最容易提问的部分。你需要能讲清楚不是用机器学习模型而是基于教辅书标题的领域知识用规则提取特征。这样做的好处是解释性强坏处是覆盖面有限有些标题没有明确的人教版字样就只能留空。5. 核心指标设计与数据分析模块让数字说话5.1 教辅书业务指标体系怎么定数据分析不是把图生成出来就完事而是要先定义要看什么、为什么看。我围绕教辅书这个场景设计了六类核心指标。指标类别具体指标业务含义热度指标评价数、收藏数如有近似衡量商品的销售热度价格指标售价、价格带分布、平均价格判断消费者接受的价格区间口碑指标好评率、中差评率判断商品质量与用户满意度时间指标出版年份、上架天数分析新旧教辅的销售表现结构指标年级/科目/出版社占比分析市场细分结构竞争力指标各出版社Top10榜单判断哪些出版社占据用户心智这里有一个必须解释清楚的地方京东商品页不会直接展示销量字段公开可见的是评价数。所以整个分析中用评价数作为销量热度的近似代理指标。这个近似并不是完全准确因为很多用户买了之后不给评价但它在横向比较同一个品类里的商品热度时仍然有参考价值。我建议你在论文里也如实写出这个指标替代逻辑这比隐藏细节要稳妥得多。5.2 价格带分析低价未必高销量价格带是教辅书分析里最直观的维度。我按0-20元、20-50元、50-100元、100元以上把商品分成了四组统计每个价格带的平均评价数、商品数量和平均好评率。bins [0, 20, 50, 100, float(inf)] labels [0-20元, 20-50元, 50-100元, 100元以上] df[price_band] pd.cut(df[price], binsbins, labelslabels, rightTrue) result ( df.groupby(price_band, observedTrue) .agg( total(title, count), avg_commit(commit_num, mean), avg_rate(good_rate, mean) ) .reset_index() )跑出来的实际结论很有意思0-20元的商品数量很多但平均评价数反而低于20-50元区间。这说明教辅书不是越便宜越好卖20-50元这个价位段可能是家长和学生更信任的正版教辅主力价位。这个洞察写进论文比单纯贴一张柱状图有价值得多。5.3 出版社与年级、科目分布分析出版社分析可以联合年级和科目一起做形成交叉分析。比如在初中数学品类里哪几个出版社的评价数明显领先在高考真题品类里哪些出版社的新品更多。实现方式是用pandas的groupby加sort_valuestop_publisher ( df[df[subject] 数学] .groupby(publisher) .agg(total(title, count), avg_commit(commit_num, mean)) .sort_values(avg_commit, ascendingFalse) .head(20) .reset_index() )这一步生成的表格和图配合可视化层做一张Top出版社横向条形图非常直观。另一个值得做的分析是年级热度分布小学、初中、高中三个学段里哪个年级的商品数量最多、评价数最高。实测结果一定是初中和高中阶段的数据量明显高于小学尤其是九年级和九年级以下这与中考、高考的压力直接相关。5.4 评价关键词分析可选的加分模块京东的评价详情文本采集难度大、反爬严格所以我的系统里做了一个折中方案不采集具体评价内容而是通过商品的标题词频和教辅场景关键词做一层内容热度分析。方法很简单把所有清洗后的标题拼成一个长文本用jieba分词过滤停用词和包邮正版等通用营销词然后统计关键词词频。import jieba from collections import Counter text .join(df[title].tolist()) words jieba.lcut(text) stop_words {包邮, 正版, 现货, 新款, 全套, 初中, 高中, 小学} filtered [w for w in words if len(w) 2 and w not in stop_words] word_freq Counter(filtered).most_common(30)这个模块虽然不是严格意义上的情感分析但能反映出当前教辅市场上真题同步专项训练思维等概念的流行度在答辩展示时是一个很有话题性的图表。6. 可视化与Web系统实现让分析结果能演示、能落地6.1 Dashboard页面规划不是堆图表而是讲故事做毕设系统最容易犯的错是把一堆图表堆在首页看起来炫实际没有逻辑。我的页面规划遵循总—分—细的故事线。总览页核心指标卡片总商品数、平均价格、平均评价数、Top出版社 价格带分布柱状图 年级占比饼图。商品分析页教辅商品数据表格支持按年级、科目、出版社筛选排序用分页降低数据量。价格分析页价格带与评价数关系图可以联动选择学段。出版社分析页出版社榜单横向条形图 词云。数据管理页显示采集状态、数据量统计预留数据更新的触发按钮。这样的好处是答辩演示时你能讲出一条完整逻辑先看整体盘子有多大再看价格带和年级分布然后穿透到出版社和具体商品。这套叙事比我这里有一堆图强太多。6.2 Flask后端与前端图表对接后端我用Flask提供JSON数据接口前端用ECharts渲染。这个前后端分离结构在答辩时容易被问到你是怎么实现前后端交互的所以要非常清楚。后端路由示例from flask import Flask, render_template, jsonify from sqlalchemy import create_engine import pandas as pd app Flask(__name__) engine create_engine(mysqlpymysql://root:123456localhost:3306/jd_books?charsetutf8mb4) app.route(/) def index(): return render_template(index.html) app.route(/api/price_band) def price_band(): sql SELECT price_band, COUNT(*) AS total, AVG(commit_num) AS avg_commit FROM book_product GROUP BY price_band df pd.read_sql(sql, engine) return jsonify({ categories: df[price_band].tolist(), total: df[total].tolist(), avg_commit: df[avg_commit].round(1).tolist() })前端只需要在页面里写一个简单的fetch或者$.getJSON把接口返回的数据塞进ECharts的option即可。这里我推荐把option的series部分用接口数据动态赋值避免写死数据这样筛选功能才能联动。如果不想前后端分离也可以直接用pyecharts生成HTML片段嵌入模板from pyecharts.charts import Bar from pyecharts import options as opts bar Bar() bar.add_xaxis(categories) bar.add_yaxis(平均评价数, values) bar_html bar.render_embed() return render_template(dashboard.html, chart_htmlbar_html)render_embed()返回的是完整的ECharts初始化脚本放到模板的div中就能显示开发效率非常高。缺点是图表间的交互联动会比较繁琐所以我实际用的是前一种JSON接口ECharts的方式。6.3 关键功能模块登录、筛选联动、数据明细系统不能只是一个图表集合还要有系统该有的功能模块。我做了一个轻量的用户登录模块密码用werkzeug.security的哈希函数处理不用明文存储。数据库里建一张user表存用户名和password_hash。筛选联动是演示时的重点。用户在页面上选择科目数学和年级初中点击查询前端通过Ajax请求带参数接口fetch(/api/price_band?subject${subject}grade${grade}) .then(res res.json()) .then(data updateChart(data));后端拿到筛选条件后拼SQL时加上WHERE条件再重新聚合数据返回。这个交互每次能真实地改变图表而不是前端做假筛选答辩时很有说服力。数据明细表格我用的是一个简单的前端表格库配合后端分页接口。这里注意表格数据量大的时候千万不要一次性全部加载到页面里否则浏览器会卡。我在后端接口里加了limit和offset参数用MySQL的LIMIT实现分页。7. 系统测试与论文写作毕设答辩前的最后冲刺7.1 功能测试与性能测试的实用思路很多毕设文档里的测试章节都是凑字数但系统测试其实是最容易拉开差距的地方。我的实际测试分成三块。第一块是数据采集的稳定性测试。让爬虫从早晨跑到下午记录每个关键词的采集耗时、成功条数、失败条数、被反爬中断的次数。这部分数据直接写进论文系统测试章节用表格呈现说服力极强。第二块是接口响应时间测试。给MySQL的sku字段加了唯一索引后通过/api/price_band这类接口返回时间一般都在100ms以内。我用浏览器开发者工具测了几个核心接口记录响应耗时并和优化前的对比。第三块是功能边界测试。重点测了筛选条件为空、价格范围不含任何数据、搜索关键词没有结果这几种情况确保前端不报错、页面有友好提示。这个测试点可以在答辩时主动说出来展现你的测试思维。7.2 论文结构重点写分析模块而不是爬虫这个系统相关的论文我建议章节结构如下摘要与关键词第一章 绪论选题背景、国内外研究现状、研究意义第二章 相关技术介绍Python、Flask、MySQL、爬虫技术、ECharts第三章 需求分析功能性需求登录、数据采集、数据分析、可视化、非功能性需求数据量、响应时间第四章 系统设计总体架构、数据库设计、模块设计第五章 系统实现采集模块实现、清洗模块实现、分析模块实现、Web展示实现第六章 系统测试测试环境、测试用例、测试结果第七章 总结与展望写作时还有一个关键点把爬虫这个词替换成数据采集把反爬绕过表述为访问频率控制。这能体现你对数据合规的理解。爬虫相关技术可以写实现细节但不要写如何绕过验证码、如何伪造签名这类内容一是敏感二是完全没必要毕设的数据量靠正常频率就能采够。7.3 答辩必问问题的应答思路答辩是所有毕设的最终考试下面几个问题是我遇到的或同学遇到的提前准备好答案。问题一数据量有多大数据是真实的吗回答思路公开了采集时间范围、采集关键词、有效条数解释评价数是公开字段数据真实但仅代表采集时段。问题二为什么用评价数代替销量如实承认这是代理指标并解释在电商公开数据中评价数是可观测且稳定的热度指标。同时补充如果后期能拿到订单数据可以换成真实销量。问题三你这个数据清洗最大的困难是什么回答价格缺失和好评率缺失的填充策略以及标题中教材版本提取的规则覆盖不全。这两个具体问题能证明你是真正动手做过。问题四如果京东页面改版了你的爬虫怎么办回答修改选择器和解析逻辑将采集模块设计为可配置模块化替换。如果时间允许可以现场演示修改选择器后重新采集。问题五你的系统有什么可以改进的地方回答可以引入更细粒度的评论情感分析接入图表间的自动联动以及把数据采集做成定时任务。注意说改进点不是承认缺陷而是展示你在思考边界。很多同学在答辩前最担心被问倒是爬虫会不会有问题但你只要把合规使用、频率控制、研究目的这三点讲清楚并且论文中没有教唆性内容这个环节完全可以平稳通过。做完整个系统我最深的感受是毕设选一个自己真正愿意深挖的细分场景比选一个听起来高大上但只能靠造数据支撑的题目省心太多了。你在清洗数据时发现的每一个异常分析时得出的每一个结论都会变成论文里的真实素材。如果时间有限优先把数据分析结果和可视化图表做得漂亮、有业务洞察演示效果比单纯的系统多几个按钮重要得多。最后再分享一个小技巧答辩前把爬虫采集、数据清洗、图表展示这一条完整链路在本地跑通录一段小视频备用万一现场网络出问题这段视频能救回大部分印象分。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门