每年到这个时间点,总能看到一批人被毕业设计折腾得够呛。如果你手上摊着"Python基于得物商品销售数据的可视化分析与推荐系统"这个题目,那恭喜,它其实是一个被包装得很好的课题:爬虫拿数据、Django做后端、协同过滤跑推荐、ECharts出可视化,再塞一个基于大模型的agent做智能问答,一条线能把电商数据链路从头摸到尾。这篇文章适合正在做毕设选题、或者想练手电商数据全流程的人,我会按自己实际做过的项目顺序,把这个题目的完整拆解、落地步骤和踩坑记录讲清楚。
1. 项目全貌与选型思路:这个课题到底值不值得做
1.1 从标题拆解出完整的业务链路
先别看这个标题长,把它拆开其实就五个模块:得物类潮流电商平台的商品数据、数据分析、可视化、协同过滤推荐、大模型agent。把它们串起来,对应的是一条非常完整的业务数据链路——数据采集与清洗、指标体系建设、统计分析、推荐算法、智能交互。
我做的时候,第一反应是"要不要把每个模块都做得很重",后来发现完全没必要。毕业设计的核心不是单项技术多深,而是能不能讲清楚一条完整的故事线。整条链路的起点是商品数据集,包含品牌、价格、销量、尺码、上架时间、收藏数这些字段;中间经过数据清洗和建模,一方面输出给可视化大屏做全局分析,另一方面输出给协同过滤算法,构建用户和商品之间的行为关系;最后再由大模型agent把"查数据"这件事变成自然语言对话,比如问一句"500到1000元价格带卖得最好的篮球鞋是哪几款",它能听懂、能查出结果、还能给出解释。
这套设计最讨喜的地方在于:每个模块都有独立的工作量,但又不是各干各的。推荐系统的结果可以反哺到可视化里,可视化的指标口径又可以作为agent回答用户问题时的"知识约束"。你答辩的时候,只要能把"数据从哪来、经过了什么处理、在哪个模块被用掉"这条线讲清楚,基本就能接住老师的问题。
1.2 Python + Django:为什么说这是毕业设计的"稳妥牌"
技术选型上,Python + Django是一个几乎不会出错的组合。Python本身覆盖了数据采集、清洗、算法建模的全部需求,pandas、numpy、scikit-learn这些库都是现成的,不用在多个语言之间切换。Django的优势则是"开箱即用"四个字:自带ORM、Admin后台、用户认证、模板渲染,做一个需要前后端交互的完整系统,比Flask省非常多事。
我建议的版本组合是Python 3.10 + Django 4.2 + DRF(Django REST Framework)。Django 4.2是目前稳定性最好的长期支持版本,配合Python 3.10不会有兼容问题。如果你要写前后端分离的接口,DRF几乎是标配,序列化、分页、权限校验都内置好了,比自己手写JsonResponse舒服太多。
至于推荐算法为什么选协同过滤而不是深度学习,理由也很实际:第一,毕设阶段的数据量通常就几千到几万条,深度学习模型在这个规模下不会比协同过滤好;第二,协同过滤的可解释性很强,"因为你买过AJ,所以给你推荐李宁匡威"这句话,答辩老师一听就懂;第三,实现成本低,不需要GPU,不需要训练十几个小时,一个余弦相似度矩阵就能跑出效果。记住一个原则:选题宁可用"小但完整"的组合,也不要硬上"大而残缺"的模型。
1.3 大模型agent的定位:别把AI做成摆设
这两年大模型特别火,很多题目都喜欢往标题里加"大模型"三个字,但加了不等于会用。我见过不少同学,所谓"大模型应用"就是接一个对话接口,用户问什么答什么,跟系统本身的数据毫无关系——这种就是典型的摆设式AI,答辩老师一问就露馅。
真正有价值的做法,是让大模型作为"数据分析入口"。用户输入一句自然语言,比如"最近30天销量下滑最明显的品牌是哪个",agent先把这句话理解成一套结构化请求(时间范围、统计维度、排序方式),然后调用后端已经写好的查询工具,拿到真实数据后,再把数据组织成一段带结论的文字返回给用户。这个过程里大模型不是在"凭空聊天",而是在"调用工具并解释结果",这就是最实用的agent雏形。
我实现的时候没有用任何重型的agent框架,就是用大模型的function calling能力:给模型声明几个工具函数,比如"get_sales_trend(brand, price_min, price_max, days)",模型看到用户问题后会自己决定调用哪个函数、传什么参数,我再在后端真正执行这个函数,把结果喂回给模型生成最终回答。这套逻辑简单可靠,又能在答辩时讲清楚"意图识别-工具调用-结果生成"的完整链路。
2. 数据层怎么打基础:采集、清洗、建模
2.1 先想清楚要哪些字段,再谈抓数据
很多人一上来就写爬虫,爬到什么算什么,这是最大的坑。我在做之前先把数据字典列出来了,最终确定的核心字段大概有这些:商品ID、商品标题、品牌、一级分类(篮球鞋/跑步鞋/板鞋等)、价格、折扣前价格、销量、收藏数、上架时间、尺码区间、流行色、商品链接。
这些字段不是随便定的,每条都对应后面某个模块的需求。品牌、价格、销量是可视化大屏的统计分析基础;销量和收藏数可以合成"商品热度"指标,用于给协同过滤提供隐式反馈;上架时间用于看趋势;分类和尺码则是筛选和分群的前提。宁可前期把字段设计宽一点,也不要后面反复改数据库表。
采集方式上,我用的方案是requests发请求拿页面HTML和接口JSON,再用BeautifulSoup解析。这里提醒一句:不要高频并发地去请求,控制一下频率,比如每请求一次随机sleep几秒,再带上正常的User-Agent和Cookie,避免被反爬策略针对。代码层面结构很简单,核心就是请求-解析-提取-入库的循环,但写的时候要加异常处理,网络超时、页面结构变化、字段缺失都是常态。
2.2 数据清洗的三个关键动作
采集下来的数据一定脏,别指望直接用。我整理下来,必须做的清洗动作有三个。
第一个是去重。商品ID是唯一键,同一商品可能被重复爬到,用pandas的drop_duplicates按商品ID去重即可。第二个是字段类型与格式统一。价格字段经常是字符串,里面还可能带"¥""起""券后"这些杂字符;销量可能是"5000+"这种模糊表达;品牌名大小写不一致。这些都要用正则和类型转换统一处理,比如价格全部转成float,销量把"+"去掉后转成int。第三个是异常值剔除。价格低于1元或者高于99999元的数据基本是测试商品或错误数据,直接过滤掉;销量为负、上架时间为空的数据也一样处理。
清洗之后的验证动作也很重要,我会打印describe()看一眼每列的最大最小值、均值、缺失值数量,确保没有离谱数据混进来。整个清洗逻辑建议封装成独立的脚本,因为后面你很可能要补数据,重新跑一遍清洗就完事了,不用手动处理。
2.3 商品、用户、行为三张核心表怎么建
Django后端建模的时候,我用的是三张核心表加两张辅助表的方案。
商品表Shoe保存商品静态属性,对应上面说的商品ID、标题、品牌、分类、价格、销量、上架时间这些字段。用户表User直接用Django内置的User扩展,或者自己建一个UserProfile存用户的偏好信息。行为表Behavior是推荐系统的核心输入,记录"哪个用户对哪个商品产生了什么行为",行为类型包括浏览、收藏、加入购物车、购买,每种行为对应一个行为权重。
外键关系上,Behavior表用ForeignKey关联User和Shoe。这里有一个关键设计:行为表必须加"行为时间"字段,因为后面做协同过滤的离线评估时,要把用户的行为按时间切分成训练集和测试集,没有时间字段这个评估就做不了。商品表的品牌和分类字段建议加db_index索引,因为可视化和推荐里会频繁按品牌、分类做筛选聚合,没有索引的话数据量一上来查询会明显变慢。
字段类型也有一些讲究:价格用DecimalField不要用FloatField,避免浮点误差;销量用PositiveIntegerField;上架时间用DateField;商品标题和品牌用CharField并指定max_length。这样建表规范,后面用ORM做聚合统计时也会顺手很多。
2.4 业务指标口径必须先定下来
可视化大屏和agent回答问题时,离不开一套统一的指标口径。这个口径如果不提前定清楚,就会出现"前端图表的销量算法和后端接口的销量算法不一致"的尴尬情况。
我定下来的核心指标口径大概是这样的:销售额等于销量乘以成交价(这里用价格字段,不额外处理活动折扣,保持口径简单);客单价等于总销售额除以订单用户数;复购率等于购买次数大于1的用户占比;动销率等于有销量商品数占全部商品数的比例。价格带划分按业务习惯来:300元以下、300-500元、500-1000元、1000-2000元、2000元以上,五个档位。
推荐召回时要用的"商品热度分"我也定了个简单公式:热度分 = 销量 * 0.6 + 收藏数 * 0.4,直接归一化后用于冷启动场景的热门榜排序。这套口径一旦定下来,后续所有模块都按它执行,不要今天改明天又改,否则你会发现自己被各种口径不一致的问题反复折腾。
3. 协同过滤推荐系统:从原理到Django接口
3.1 UserCF还是ItemCF?选型比写代码更重要
协同过滤算法分两大类:基于用户的协同过滤(UserCF)和基于物品的协同过滤(ItemCF)。很多教程会告诉你两者都要学,但在实际项目里,你必须根据业务场景做选型。
基于物品的协同过滤更适用于鞋类电商这种垂直商品场景。核心逻辑是"如果你喜欢A商品,那么和你喜欢A的其他用户喜欢的B商品,也可能被你喜欢";换成ItemCF就是"你买了AJ1,我们找到和AJ1最相似的商品(比如类似风格的Dunk或者同品牌的其他配色)推荐给你"。ItemCF的优点是推荐结果和用户历史行为直接相关、可解释性强,而且商品之间的关系相对稳定,不用频繁更新。UserCF则更适合内容社区、新闻资讯这类有强时效性的场景,因为用户兴趣变化快、内容生命周期短。
选型建议总结成一句话:这个题目里,你主要做的是"商品推荐"而不是"内容推荐",所以ItemCF是更稳妥的主算法。当然,答辩时可以把两种算法都实现出来做对比实验,然后给出"哪个指标更好、为什么更好"的结论,这会是一个很好的加分项。
3.2 评分矩阵与相似度计算的实现细节
协同过滤的第一步是构建"用户-商品"评分矩阵,行是用户,列是商品,值是评分或行为权重。
我没有用显式评分(鞋子平台本来就没有给商品打分的强场景),而是通过行为权重合成隐式评分:浏览记0.5分、收藏记1分、加入购物车记2分、购买记3分。如果一个用户对同一商品有多个行为,分数累加。这样得到的是一个稀疏但是有商业含义的评分矩阵。
相似度计算我用的是余弦相似度。商品A和商品B的向量分别是"所有用户对它们的评分向量",两个向量夹角的余弦值越接近1,说明这两个商品被同一批用户喜欢的程度越一致。代码实现上,我直接用了pandas构建DataFrame、用scikit-learn的cosine_similarity计算,这比自己写循环快得多且不容易出错。
矩阵规模大的时候要注意内存问题,用户数和商品数过万之后稠密矩阵会非常占内存,这时候要用scipy.sparse的稀疏矩阵格式。我当时数据量在5000个用户、3000个商品左右,df.pivot_table构建出来的矩阵里95%以上都是空值,换成CSR稀疏矩阵之后内存占用直接降到原来的几十分之一。
3.3 把协同过滤结果封装成推荐API
算法跑通之后,要把结果变成Django接口给前端调用。我的做法是推荐接口设计为:GET /api/recommend/?user_id=xxx&top_n=10。
后端流程是:先从Behavior表查出当前用户的行为记录,找出他买过或收藏过的商品集合;再在之前算好的商品相似度矩阵里,找出这些种子商品各自最相似的TopN商品;然后按相似度加权汇总,过滤掉用户已经购买过的商品;最后按加权得分排序取前top_n返回。
相似度矩阵怎么存,这是一个容易踩坑的点。最简单的方式是训练完成后把"商品A-商品B-相似度"三元组存到数据库的一张item_sim表里,推荐时直接查表。缺点是数据量大了查询慢。另一种方式是每次启动服务时把矩阵load进Redis,查询走内存,速度快很多。我做毕设时用的方案是:矩阵规模小就存数据库表,规模超过一万条记录就放Redis,两者代码相差不大,可以都实现再对比。
接口返回的JSON结构我会定义成统一格式,包含商品ID、标题、品牌、价格、销量、推荐理由字段。这个推荐理由字段就是可解释性的体现——"因为你收藏了Dunk Low,所以推荐同风格的运动板鞋",前端把这句话直接展示出来,整个系统的完成度一下就上去了。
3.4 冷启动和离线评估,答辩必问的两个问题
如果答辩老师只问三个问题,冷启动和效果评估一定占两个。冷启动有三类:新用户冷启动、新商品冷启动、系统冷启动。
新用户没有行为数据,协同过滤完全无法工作。我的处理方案是分两步:先用热门榜兜底,把热度分Top20的商品推荐给新用户;再让用户在登录时选一下偏好的品牌和价格带,基于这些标签做粗筛。新商品冷启动同理,没有行为数据就把同品牌、同分类、最近上架的商品作为替代推荐,同时给予一定的曝光加权。系统冷启动就是整个库里没有行为数据的情况,这时候只能做基于商品属性的相似推荐,也就是用品牌、分类、价格带之间的距离来计算"内容相似度"。
离线评估我用的是时间切分法:把每个用户的行为按时间排序,取最后20%作为测试集,前80%作为训练集。用训练集训练推荐模型,在测试集上看推荐结果中有多少商品是用户真实交互过的,计算命中率(Hit Rate)和召回率。我当时的实测结果,ItemCF的Top10命中率大概在22%到30%之间,这个数字不算惊艳,但对于答辩来说已经足够说明算法有效了。
4. 可视化分析大屏:图表怎么选、数据怎么给
4.1 一屏看清"谁在卖、卖什么、卖多少钱"
可视化大屏是整套系统里最直观、最能撑场面的部分,但很多人的大屏只是堆图表,没有分析逻辑。我在设计大屏时问了自己三个问题:谁在卖(品牌格局)、卖什么(品类结构)、卖多少钱(价格分布)。
围绕这三个问题,大屏的布局我这样安排:顶部一排指标卡,放总商品数、总销量、总销售额、平均客单价四个核心指标;中间主体区域放销量趋势折线图(近12个月)和品牌销量Top10柱状图;左侧放价格带分布饼图和尺码分布环形图;右侧放商品热度散点图(销量与收藏量的关系)以及热门商品词云。这样整个大屏从上到下、从左到右,覆盖了从宏观指标到微观商品的全部分析视角。
图表选型上也有讲究:时间趋势用折线图,排名对比用柱状图,结构占比用饼图,两个数值维度的关系用散点图,文本关键词用词云。不要为了炫技用一堆3D图表,答辩时老师更看重的是"你选的图表是否准确表达了业务含义"。
4.2 Django后端聚合接口:用ORM减少查询量
大屏上的每一个图表背后都应该有一个数据接口,不要在前端拿全量数据自己算。我在Django里设计了一组统计接口:/api/stats/overview返回顶部指标卡数据;/api/stats/trend返回按月份聚合的销量趋势;/api/stats/brand_rank返回品牌销量Top10;/api/stats/price_dist返回价格带分布;/api/stats/category_dist返回品类和尺码分布。
关键实现技巧是善用Django ORM的annotate聚合。以品牌销量Top10为例,一行查询就能搞定:Shoe.objects.values('brand').annotate(total_sales=Sum('sales')).order_by('-total_sales')[:10]。这条语句在数据库层面完成分组和求和,不会把全表数据拉到Python内存里,性能好很多。
还有一个细节:统计结果不是每次请求都实时算的。我当时给统计接口加了一层Redis缓存,缓存键可以设计成统计类型加时间范围,比如stats:brand_rank:2025,缓存过期时间设为一小时。这样大屏刷新时不会反复压数据库,演示的时候数据加载也明显更快。
4.3 pyecharts与前端渲染的集成细节
图表的渲染方案我推荐pyecharts,它基于ECharts封装,Python语法生成图表配置,对熟悉pandas的同学特别友好。用法简单到像是写配置字典:创建Bar对象、加入x轴和y轴数据、set_global_opts设置标题和坐标轴,一个柱状图就出来了。
把pyecharts嵌入Django,主流有两种方式。方式一是在Django视图里直接调用图表对象的render_embed(),生成本地HTML片段塞进模板;方式二是图表数据以JSON格式传给前端,由前端的ECharts实例负责渲染。毕设演示场景下我推荐方式一,因为省事且稳定,但如果你打算做前后端分离,就选方式二。
集成时有一个容易被忽略的坑:pyecharts生成的HTML会引用ECharts的JS依赖,本地离线演示时可能会加载失败。解决办法是把依赖JS文件下载到static目录,或者用render_embed()配合本地打包好的echarts.min.js,确保在没有外网的环境下图表也能正常显示。
5. 大模型agent:给系统加一个会查数的AI助手
5.1 功能设计:自然语言查数和智能推荐解释
大模型agent在系统里承担两个任务。第一个是自然语言查数,用户用中文提问,系统返回数据答案并生成分析文字。第二个是智能推荐解释,也就是把协同过滤的结果"翻译"成人话,比如推荐页签里显示"因为你近期收藏了多款联名款球鞋,系统为你重点推荐了同风格的商品"。
这两个功能落到实处都是同一套技术:大模型负责理解和生成,后端函数负责真正执行查询。我把这个模块做成一个独立的"分析助手"页面,用户在输入框里提问,后端把问题转成工具调用并执行,最后把答案流式地返回展示。这个页面天然就是一个完整的前后端交互demo,工作量不大但观感非常好。
设计时我坚持了一点:所有返回给用户的数据都必须来自真实数据库,大模型不能自己编数字。为了避免模型胡编,我会在提示词里反复强调"只能基于工具返回的数据进行回答,如果工具没有返回数据,请直接告知用户暂时没有相关数据"。这条约束约束好了,agent的可信度才会高。
5.2 提示词模板与工具调用
agent的实现我分成了三部分:系统提示词、工具定义、执行循环。
系统提示词设定角色,我写的大意是:"你是某鞋类电商平台的数据分析助手,负责根据工具返回的数据回答用户问题。回答时先给出结论,再简要解释数据依据。"工具定义部分告诉模型有哪些函数可以调用。以"get_brand_sales_rank"为例,工具描述里写明参数含义,包括品牌、价格区间、时间范围、排序方式。模型看到用户问题"300到800元之间销量前十的篮球鞋品牌"后,会自动把参数解析成price_min=300、price_max=800、category="篮球鞋"、top_n=10,并选择调用这个工具。
执行循环是标准的function calling流程:把用户问题连同工具定义发给大模型,模型返回一个"要调用工具"的响应;后端解析出工具名和参数,执行对应的Django查询函数,把查询结果以JSON字符串的形式追加到对话里,再次发给模型;模型基于真实数据生成最终文本回复。这个往返流程就是最简洁的agent机制,核心代码量并不多,逻辑也清晰。
5.3 流式输出与接口安全
大模型接口响应时间通常在一秒到几秒之间,如果让用户干等,体验会很差。我建议用流式输出优化:调用大模型接口时开启stream参数,把模型逐步生成的token通过SSE推给前端,效果就是"打字机"式逐字输出。Django里可以用StreamingHttpResponse实现SSE,前端用EventSource或者fetch的ReadableStream接收,代码量不大但体验提升非常明显。
接口安全方面有几个细节。大模型的API密钥绝不能写在settings.py里直接提交,要放到环境变量或者本地配置文件中并加入.gitignore。接口要做访问控制,最简单的是加token校验,登录用户才能调用agent接口,避免密钥被消耗。成本控制也要注意:提示词不要太长,回答的max_tokens限制在300左右,温度调低到0.3以下,这样回答更稳定也更省钱。
6. 实操复盘:我踩过的坑和排查清单
6.1 评分矩阵构建时最容易翻车的三个点
第一个坑是用户ID和商品ID没有转为统一的数字编码。文本ID进矩阵会让pandas的pivot_table建出一个巨大而且奇怪的索引,后续做相似度计算会出错。解决办法是先分别做用户和商品的编码映射字典,保证矩阵的行列都是连续的整数索引。
第二个坑是空值处理。用户-商品评分矩阵极度稀疏,直接对包含NaN的矩阵做余弦相似度计算会得到大量无意义结果。我的做法是先用fillna(0)把评分矩阵填零,再调cosine_similarity。注意不要在填充前先做中心化,否则零值全变成负值,相似度含义就变了。
第三个坑是只算"有交集"的向量。如果两个商品几乎没有共同用户,它们的余弦相似度要么为0要么数值不稳,应该设置一个最低共同用户数阈值,比如"共同用户数少于5的直接丢弃"。这个阈值能过滤掉大量随机噪声,推荐质量会明显提升。
6.2 Django与可视化集成的两条路线
可视化集成路线我两条都试过,给你一个明确的选择建议。如果你的页面是Django模板渲染的传统多页应用,直接选pyecharts的render_embed(),视图里返回一个包含图表HTML的字符串,模板用safe过滤器渲染,半小时就能把所有图表挂上去。
如果你打算做成前后端分离的单页应用,那就让Django只提供JSON接口,前端用原生ECharts或Vue/React来渲染。这条路线工作量更大,但页面会更灵活,后续接大模型agent的流式输出也更顺畅。
我当时是混合方案:大屏页面用Django模板加pyecharts,agent页面用前后端分离加SSE。这样两种渲染方式都涉及到了,PPT里还能多讲一个技术点,算是性价比很高的组合。
6.3 常见报错速查表
我把做这个项目过程中碰到的高频问题和解决方案整理成了清单,做的时候可以直接对着排查。
pip安装Django时报版本冲突,多数是Python版本过低。Django 4.2要求Python 3.8以上,建议直接升到Python 3.10再装。运行数据库迁移时报"Table already exists",一般是之前迁移中断过,解决办法是手动删除数据库中的django_migrations记录后重新migrate。页面表单提交时报CSRF verification failed,Django默认开启了CSRF校验,模板表单里加csrf_token即可。跨域调用接口报CORS错误,安装django-cors-headers并把前端域名加进白名单。pyecharts图表中文乱码,通常是系统缺少中文字体,或者IDE的默认编码不是UTF-8,统一用UTF-8并在绘图配置里指定中文字体即可。
agent接口调用报超时,建议把大模型请求放到独立线程执行,接口先返回任务ID,前端再轮询或通过WebSocket接收结果,避免HTTP请求长时间挂起。这个方案虽然多了一点代码量,但稳定性明显优于直接同步等待。
最后说点掏心窝的话。这套系统我前后改了三版:第一版只做推荐,答辩老师连问三个数据口径的问题我就答不上来;第二版加了可视化大屏,能看到的东西多了;第三版把大模型agent接进去之后,面试官的第一反应是"这个工作量确实可以"。但给你一个建议:毕设源码可以借鉴,但每个模块的中间结果、每个参数为什么这样定,你一定要能自己推导出来。别等到答辩站在讲台上,才发现自己连价格带为什么分五档都解释不清楚。希望你做完这套系统时,不仅拿到了代码,还真正把电商数据链路这回事弄明白了。