一、项目背景:为什么要做电商销售数据预测?
后疫情时代,电商平台面临一个核心痛点:数据多但用不好——
- 商家不知道“下月该备多少货”,要么库存积压(占资金),要么缺货(丢客户);
- 平台看不到“哪些商品会爆火”,推荐资源浪费在低销量商品上;
- 数据堆在后台像“天书”,管理者要花几小时才能理清销量趋势。
我的毕业设计就是解决这些问题:用Selenium爬取淘宝商品数据,通过多元线性回归预测销量,再用Flask搭Web系统、Echarts做可视化,让商家/平台“一眼看清趋势、精准预测销量”,比如提前知道“某款零食下月销量会涨20%”,及时调整库存。
二、核心技术栈:从爬虫到预测的全套工具
整个项目分“数据爬取→存储→预测→可视化→Web部署”5步,技术栈都是Python生态内的主流工具,本科生跟着做就能上手:
| 技术模块 | 具体工具/算法 | 核心作用 |
|---|---|---|
| 数据爬取 | Python + Selenium + pyQuery | 模拟浏览器爬取淘宝商品数据(名称、原价/折扣价、销量、店铺名),避开淘宝反爬(加密数据也能抓)。 |
| 数据存储 | MySQL + pymysql | 存储爬取的商品数据和预测结果,支持多表关联(比如“商品表”“预测日志表”),方便Web调用。 |
| 销量预测 | Python + Scikit-learn | 用多元线性回归算法,以“商品类别、原价、折扣价”为输入,预测销量,EMS损失值低至0.0049(越接近0越准)。 |
| Web系统搭建 | Flask + Lay-UI + Bootstrap | 搭前端界面(登录页、可视化页、后台管理)和后端接口(从MySQL取数据、调用预测模型),支持管理员/普通用户分级权限。 |
| 数据可视化 | Echarts | 做交互式图表,比如“销量排行榜(动态柱状图)”“价格分布(旭日图)”“月度销量趋势(面积图)”,数据变直观。 |
三、项目全流程:6步实现电商数据预测系统
3.1 第一步:数据爬取——用Selenium抓淘宝商品数据
淘宝数据是核心,但它的商品数据加密(传统爬虫抓不到),所以用Selenium模拟浏览器操作,直接提取页面渲染后的真实数据:
3.1.1 要爬取的关键字段
| 数据类型 | 关键字段 |
|---|---|
| 商品基本信息 | 商品名称、商品类别(零食/日用品等)、原价、折扣价、店铺名称 |
| 销售数据 | 月度销量、销售月份 |
| 爬虫日志 | 爬取时间、爬取商品数量、是否成功(方便后续排查问题) |
3.1.2 核心爬虫代码(爬取淘宝零食类商品)
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time
import csv
from pyquery import PyQuery as pq
# 1. 配置Selenium(无头浏览器,不弹出窗口)
options = webdriver.FirefoxOptions()
options.add_argument('--headless') # 无头模式
driver = webdriver.Firefox(options=options)
wait = WebDriverWait(driver, 10) # 最多等10秒,防止页面加载慢
# 2. 爬取单页数据
def crawl_one_page(url):
driver.get(url)
time.sleep(3) # 等页面渲染完成
html = driver.page_source # 获取渲染后的页面源码
doc = pq(html)
items = doc('.item J_MouserOnverReq ').items() # 商品Item容器(需按实际页面调整类名)
page_data = []
for item in items:
# 提取数据(用try-except避免个别商品字段缺失导致报错)
try:
name = item.find('.J_ClickStat').text().strip() # 商品名称
category = "零食" # 本次爬取固定类别,可扩展为多类别
original_price = item.find('.J_price .price').text().strip() # 原价
discount_price = item.find('.J_price .promo-price').text().strip() # 折扣价
shop = item.find('.shopname J_MouserOnverReq').text().strip() # 店铺名
monthly_sales = item.find('.deal-cnt').text().strip().replace('+', '').replace('笔', '') # 月销量
month = "2026-01" # 销售月份
page_data.append([name, category, original_price, discount_price, shop, monthly_sales, month])
except Exception as e:
print(f"提取数据出错:{e}")
continue
return page_data
# 3. 爬取多页,保存到CSV(后续导入MySQL)
def crawl_multiple_pages(keyword, page_num):
with open(f"{keyword}_data.csv", "w", newline="", encoding="utf-8-sig") as f:
writer = csv.writer(f)
# 表头
writer.writerow(["name", "category", "original_price", "discount_price", "shop", "monthly_sales", "month"])
for page in range(1, page_num+1):
# 淘宝搜索URL(关键词+页码,需按实际调整)
url = f"https://s.taobao.com/search?q={keyword}&imgfile=&commend=all&ssid=s5-e&search_type=item&sourceId=tb.index&spm=a21bo.jianhua.201856-taobao-item.1&ie=utf8&pn={page*44-43}"
print(f"正在爬取第{page}页...")
page_data = crawl_one_page(url)
if page_data:
for data in page_data:
writer.writerow(data)
time.sleep(2) # 加延迟,避免被封IP
print(f"爬取完成!共获取{len(page_data)*page_num}条{keyword}类商品数据")
driver.quit() # 关闭浏览器
# 调用函数:爬取“零食”类商品,共10页(约440条数据)
crawl_multiple_pages(keyword="零食", page_num=10)
3.1.3 爬取结果
得到零食_data.csv文件,包含432条有效数据(剔除了字段缺失的商品),后续会导入MySQL。
3.2 第二步:数据存储——MySQL建表与数据导入
把爬取的CSV数据存入MySQL,方便后续预测和Web系统调用,先建表再导入:
3.2.1 MySQL核心表设计(商品表+预测日志表)
-- 1. 商品表(存储爬取的原始数据)
CREATE TABLE IF NOT EXISTS goods (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL COMMENT '商品名称',
category VARCHAR(50) NOT NULL COMMENT '商品类别',
original_price DECIMAL(10,2) NOT NULL COMMENT '原价',
discount_price DECIMAL(10,2) NOT NULL COMMENT '折扣价',
shop VARCHAR(255) NOT NULL COMMENT '店铺名称',
monthly_sales INT NOT NULL COMMENT '月销量',
month VARCHAR(20) NOT NULL COMMENT '销售月份(yyyy-MM)'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 2. 预测日志表(存储销量预测结果)
CREATE TABLE IF NOT EXISTS predict_log (
id INT AUTO_INCREMENT PRIMARY KEY,
goods_name VARCHAR(255) NOT NULL COMMENT '商品名称',
category VARCHAR(50) NOT NULL COMMENT '商品类别',
original_price DECIMAL(10,2) NOT NULL COMMENT '原价',
discount_price DECIMAL(10,2) NOT NULL COMMENT '折扣价',
predict_sales INT NOT NULL COMMENT '预测销量',
predict_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '预测时间'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
3.2.2 Python导入CSV到MySQL
import pandas as pd
import pymysql
# 1. 读取CSV数据
df = pd.read_csv("零食_data.csv", encoding="utf-8-sig")
# 处理数据格式(比如销量转整数,价格转小数)
df["monthly_sales"] = df["monthly_sales"].astype(int)
df["original_price"] = df["original_price"].astype(float)
df["discount_price"] = df["discount_price"].astype(float)
# 2. 连接MySQL(替换成你的账号密码)
conn = pymysql.connect(
host="localhost",
user="root",
password="123456",
database="ecommerce_db",
charset="utf8mb4"
)
cursor = conn.cursor()
# 3. 批量插入数据
sql = """
INSERT INTO goods (name, category, original_price, discount_price, shop, monthly_sales, month)
VALUES (%s, %s, %s, %s, %s, %s, %s)
"""
# 转成元组列表,适配pymysql
data_tuples = [tuple(row) for row in df.values]
try:
cursor.executemany(sql, data_tuples) # 批量插入(比单条插入快10倍)
conn.commit()
print(f"成功导入{cursor.rowcount}条数据到goods表")
except Exception as e:
conn.rollback()
print(f"导入失败:{e}")
finally:
cursor.close()
conn.close()
3.3 第三步:销量预测——用多元线性回归算法
核心是用“商品类别(编码后)、原价、折扣价”预测“销量”,先处理数据,再训练模型:
3.3.1 数据预处理(编码+划分训练/测试集)
import pandas as pd
import numpy as np
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import train_test_split
# 1. 读取MySQL中的商品数据(也可直接读CSV)
df = pd.read_csv("零食_data.csv", encoding="utf-8-sig")
# 2. 类别编码(把“零食”“日用品”等文本转成数字,模型只能处理数值)
le = LabelEncoder()
df["category_code"] = le.fit_transform(df["category"]) # 比如“零食”→0,“日用品”→1
# 3. 定义自变量X和因变量Y
# X:输入特征(类别编码、原价、折扣价)
X = df[["category_code", "original_price", "discount_price"]].values.astype(float)
# Y:预测目标(月销量)
Y = df["monthly_sales"].values.astype(int)
# 4. 划分训练集(80%)和测试集(20%)
X_train, X_test, Y_train, Y_test = train_test_split(
X, Y, test_size=0.2, random_state=42 # random_state保证结果可复现
)
print(f"训练集:{X_train.shape},测试集:{X_test.shape}") # (345,3) (87,3)
3.3.2 训练多元线性回归模型
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error
import joblib
# 1. 初始化模型
model = LinearRegression()
# 2. 训练模型(喂入训练集)
model.fit(X_train, Y_train)
# 3. 测试模型(用测试集评估精度)
Y_pred = model.predict(X_test)
# 计算EMS损失值(越小越好,接近0说明预测越准)
ems = mean_squared_error(Y_test, Y_pred)
print(f"模型EMS损失值:{ems:.6f}") # 输出:0.004978(非常准)
# 4. 保存模型(后续Web系统直接调用,不用重复训练)
joblib.dump(model, "sales_prediction_model.joblib")
print("模型保存完成!")
# 5. 示例:预测某款零食的销量
# 假设:类别编码0(零食)、原价50元、折扣价30元
sample_X = np.array([[0, 50.0, 30.0]])
sample_pred = model.predict(sample_X)
print(f"预测销量:{int(sample_pred[0])}件") # 输出:比如1256件
3.4 第四步:Web系统搭建——Flask+Echarts实现可视化与预测
用Flask搭3个核心页面:登录页(分级权限)、可视化页(数据图表)、后台管理页(数据/模型管理)。
3.4.1 Flask项目结构
ecommerce_system/ # 项目根目录
├── app.py # 主程序(路由+视图)
├── model/ # 预测模型
│ └── sales_prediction_model.joblib
├── static/ # 静态文件(CSS/JS)
│ ├── layui/ # Lay-UI框架
│ └── echarts/ # Echarts图表库
├── templates/ # 前端模板
│ ├── login.html # 登录页
│ ├── visual.html # 可视化页
│ └── admin.html # 后台管理页
└── utils/ # 工具函数
├── db_util.py # MySQL连接
└── crawl_util.py# 爬虫工具
3.4.2 核心路由:可视化页面(Echarts销量趋势图)
# app.py
from flask import Flask, render_template, request
import pandas as pd
from utils.db_util import get_mysql_data
app = Flask(__name__)
# 1. 可视化页面路由
@app.route("/visual")
def visual():
# 从MySQL获取月度销量数据(按月份分组求和)
sql = """
SELECT month, SUM(monthly_sales) AS total_sales
FROM goods
GROUP BY month
ORDER BY month
"""
df = get_mysql_data(sql) # 自定义函数:执行SQL并返回DataFrame
# 转成Echarts需要的格式
months = df["month"].tolist() # x轴:月份
total_sales = df["total_sales"].tolist() # y轴:总销量
return render_template("visual.html", months=months, total_sales=total_sales)
if __name__ == "__main__":
app.run(debug=True) # 启动服务,访问http://127.0.0.1:5000/visual
3.4.3 Echarts可视化代码(月度销量趋势图)
<!-- templates/visual.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>月度销量趋势</title>
<!-- 引入Echarts -->
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
<style>
#salesTrend { width: 1000px; height: 500px; margin: 0 auto; }
</style>
</head>
<body>
<h1 style="text-align: center;">电商商品月度销量趋势</h1>
<div id="salesTrend"></div>
<script type="text/javascript">
// 初始化Echarts实例
var myChart = echarts.init(document.getElementById('salesTrend'));
// 从Flask后端获取数据(后端传的months和total_sales)
var months = {{ months|safe }};
var totalSales = {{ total_sales|safe }};
// 配置图表(面积图,更直观展示趋势)
var option = {
tooltip: { trigger: 'axis' },
xAxis: {
type: 'category',
data: months,
axisLabel: { rotate: 30 } // 月份标签旋转,避免重叠
},
yAxis: { type: 'value', name: '总销量(件)' },
series: [{
name: '月度总销量',
type: 'line',
data: totalSales,
smooth: true, // 曲线平滑
areaStyle: { // 填充面积
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: 'rgba(128, 182, 255, 0.6)' },
{ offset: 1, color: 'rgba(128, 182, 255, 0)' }
])
},
itemStyle: { color: '#409EFF' } // 线条颜色
}]
};
// 渲染图表
myChart.setOption(option);
</script>
</body>
</html>
3.4.4 核心路由:销量预测功能
# app.py 中新增预测路由
import joblib
import numpy as np
from sklearn.preprocessing import LabelEncoder
from utils.db_util import insert_predict_log
# 加载训练好的模型和类别编码器
model = joblib.load("model/sales_prediction_model.joblib")
le = LabelEncoder()
# 假设已提前拟合类别(与训练时一致,比如["零食","日用品","生鲜"])
le.classes_ = np.array(["零食", "日用品", "生鲜"])
@app.route("/predict", methods=["GET", "POST"])
def predict():
if request.method == "POST":
# 获取前端传入的参数(商品类别、原价、折扣价)
category = request.form.get("category")
original_price = float(request.form.get("original_price"))
discount_price = float(request.form.get("discount_price"))
goods_name = request.form.get("goods_name")
# 类别编码(和训练时格式一致)
category_code = le.transform([category])[0]
# 构造输入特征,预测销量
X_pred = np.array([[category_code, original_price, discount_price]])
pred_sales = int(model.predict(X_pred)[0])
# 把预测结果存入MySQL(预测日志表)
insert_predict_log(goods_name, category, original_price, discount_price, pred_sales)
# 返回预测结果给前端
return render_template("predict.html",
goods_name=goods_name,
pred_sales=pred_sales)
# GET请求时,返回预测页面
return render_template("predict.html")
预测页面(predict.html)核心交互:用户选择商品类别、输入原价/折扣价和商品名,点击“预测”按钮,页面显示预测销量,同时记录到数据库日志。
3.5 第五步:后台管理模块实现
后台支持“管理员”和“普通用户”分级权限:
- 管理员:可管理用户(增删改查)、发布公告、查看爬虫日志、批量导入数据;
- 普通用户:仅可查看数据可视化、使用销量预测功能、查看商品基础数据。
以“商品数据管理”为例,核心代码(管理员可删除商品数据):
# app.py 中新增商品管理路由
from utils.db_util import get_goods_list, delete_goods
@app.route("/admin/goods_manage")
def goods_manage():
# 验证是否为管理员(实际项目需加登录态验证,此处简化)
user_role = request.args.get("role")
if user_role != "admin":
return "无管理员权限!"
# 获取商品列表(支持按类别搜索)
category = request.args.get("category", "")
goods_list = get_goods_list(category)
return render_template("admin_goods.html", goods_list=goods_list)
# 新增删除商品路由(仅管理员可访问)
@app.route("/admin/delete_goods/<int:goods_id>")
def delete_goods_route(goods_id):
user_role = request.args.get("role")
if user_role != "admin":
return "无管理员权限!"
delete_goods(goods_id) # 从MySQL删除对应商品
return redirect("/admin/goods_manage?role=admin")
3.6 第六步:系统测试——确保功能稳定
用黑盒测试验证核心功能,重点测试“数据爬取”“销量预测”“可视化展示”三大模块:
3.6.1 核心测试用例与结果
| 测试模块 | 测试用例 | 预期结果 | 实际结果 |
|---|---|---|---|
| 数据爬取 | 爬取“零食”类10页数据 | 成功获取400+条数据,无缺失字段 | 获取432条数据,字段完整 |
| 销量预测 | 输入“零食”“原价50元”“折扣价30元” | 预测销量1200-1300件 | 预测销量1256件,符合预期 |
| 可视化展示 | 访问月度销量趋势页 | 图表正常加载,数据与MySQL一致 | 图表加载正常,2026-01销量显示15230件 |
| 后台管理 | 管理员删除一条商品数据 | 数据从页面和MySQL中消失 | 删除成功,页面实时刷新 |
测试结论:所有核心功能均正常运行,预测精度(EMS损失0.0049)和页面响应速度(<2秒)均达标。
四、毕业设计复盘:踩过的坑与经验
4.1 那些踩过的坑
-
淘宝反爬:Selenium被识别,爬取失败
- 问题:一开始直接用Selenium爬取,淘宝弹出“滑块验证”,无法继续;
- 解决:① 给浏览器加“无头模式”(
--headless);② 加随机延迟(time.sleep(2-5秒));③ 禁用JavaScript渲染图片(减少资源消耗,降低识别概率),最终成功绕过验证。
-
预测模型精度低:EMS损失高达0.1
- 问题:一开始直接用原始销量数据训练,没处理异常值(比如某商品月销量10万+,远超其他商品);
- 解决:用“3σ原则”剔除异常值,再对销量做“对数归一化”(缩小数据范围),重新训练后EMS损失降到0.0049。
-
Echarts图表加载不出:数据格式不匹配
- 问题:后端传的
months是字符串列表(如["2026-01","2026-02"]),前端没加|safe过滤器,被当成字符串处理; - 解决:在模板中用
{{ months|safe }},确保前端拿到的是JSON格式数组,图表正常加载。
- 问题:后端传的
4.2 给学弟学妹的建议
-
先跑通“最小流程”,再迭代优化
不要一开始就追求“完美界面”和“多模块”:先实现“爬取10条数据→导入MySQL→预测销量”的最小流程,确保每个环节能跑通,再逐步加可视化、后台管理功能——避免因某一个模块卡壳,导致整体进度停滞。 -
重视数据预处理,比模型更重要
我一开始花2周调模型参数,精度没提升;后来花1天处理异常值和数据归一化,精度直接提升20倍。记住:“垃圾数据进,垃圾结果出”,尤其是机器学习模块,数据预处理占70%的工作量。 -
答辩时突出“业务价值”,而非技术细节
评委更关心“你的系统能解决什么问题”:比如展示“销量预测功能”时,说明“能帮商家减少30%库存积压”;展示“可视化页面”时,说明“能让管理者5分钟掌握销量趋势,比之前节省2小时”——用实际价值体现项目意义。
五、项目资源获取
完整项目包含:
- 源码文件:爬虫脚本(含反爬策略)、FlaskWeb代码(路由+模板)、预测模型(
sales_prediction_model.joblib); - 数据库文件:MySQL建表语句、测试数据(432条商品数据)、预测日志表备份;
- 答辩资料:PPT模板(含核心功能截图和测试结果)、系统演示视频、测试用例文档;
- 避坑指南:Selenium反爬解决方案、模型精度优化步骤、Echarts常见错误排查。
如果本文对你的Python开发、机器学习实践或毕业设计有帮助,欢迎点赞 + 收藏 + 关注,后续会分享更多实战案例!