☰
电商销量预测系统实战:Python+Django+随机森林打造母婴用品预测看板
2026/10/10 21:52:21 网站建设 项目流程

做一个电商销量预测系统,最难的不是算法,而是把“数据从哪里来、怎么清洗、怎么训练、怎么展示”串成一条能落地的链路。母婴用品这个场景尤其典型:奶粉、纸尿裤这类商品复购周期短,促销敏感度高,备货多了压资金,备货少了丢订单。本文就用 Python + Django + 爬虫 + 随机森林回归算法,从零搭建一套母婴用品电商销量分析与预测系统,覆盖数据采集、数据清洗、模型训练、API 接口和可视化看板,帮你跑通一个完整项目。

读完这篇文章,你可以得到三样东西:第一,理解这套系统的整体架构和数据流向;第二,拿到可直接运行的 Django 项目代码,包含爬虫、特征工程、模型训练和 ECharts 可视化;第三,了解随机森林回归在销量预测中的调参思路,以及项目上线时最容易被忽视的工程问题。

1. 电商销量预测系统:母婴行业的真实痛点

1.1 为什么先拿母婴用品练手

母婴用品品类有明显的业务特征:刚需、高频、季节性强。纸尿裤和奶粉属于标品,用户一旦习惯某个品牌,复购周期非常稳定,但遇到电商大促或者平台补贴,销量又会瞬间放大。如果只靠店长经验备货,很容易出现两种极端情况:

  • 备货过多:仓储成本增加,临近保质期还要折价处理。
  • 备货不足:爆款断货,用户转头就去别家下单,流失率很高。

这个问题本质上不是“多备一点还是少备一点”的经验问题,而是“未来某段时间销量大概是多少”的预测问题。历史销售数据里其实已经包含了很多规律:星期几卖得好、月底有没有冲量、促销价对销量的拉动有多大。机器学习要做的,就是把这些规律从数据里找出来,变成可量化的预测结果。

1.2 传统预测方式为什么不够用

很多运营团队还在用 Excel 移动平均或者简单的增长比例来预测销量。这些方法不是不能用,而是有明确局限:

  • Excel 移动平均:适合平稳序列,但遇到促销、节假日、突发流量就会严重滞后。
  • 线性回归:只能表达线性关系,而销量和价格、促销、时间之间的关系明显非线性。
  • 人工经验系数:主观性强,换一个运营人员,预测口径就变了。

随机森林回归的优势在于,它是一种集成树模型,不需要做复杂的特征缩放,能自动处理特征之间的非线性交互,对异常值也有一定容忍度。在电商销量预测这种“高噪声、多因子”的场景里,它的稳定性比单棵决策树和线性模型更好,默认超参数也往往能拿到一个不错的基线结果。

1.3 本文系统的技术边界

需要先说明一点:本文重点演示的是从数据采集到预测再到可视化的完整闭环,代码可以运行,但定位是教学和工程原型。真实商用系统还需要考虑数据量、模型在线更新、接口鉴权、监控告警等问题,这些我会在最后一章给出工程化建议。

2. 系统总体架构与技术选型

2.1 整体架构分层

整个系统可以分成五层:

层级职责核心工具
数据采集层抓取商品、价格、销量数据requests + BeautifulSoup
数据存储层保存原始数据和清洗数据SQLite / MySQL
数据分析层清洗、特征工程pandas + numpy
模型层训练销量预测模型scikit-learn
Web展示层API接口与可视化页面Django + ECharts

数据流向是:爬虫定时抓取数据写入数据库,pandas 从数据库读取并进行清洗和特征构造,然后训练随机森林回归模型,模型训练完成后通过 joblib 保存到磁盘。Django 启动时加载模型文件,对外提供销量趋势和预测接口,前端页面通过 fetch 请求接口,再用 ECharts 渲染图表。

2.2 技术选型对比

很多初学者会纠结 Django 和 Flask 怎么选。这个项目选择 Django,主要有三个理由:

  • Django 自带 ORM,定义模型后可以自动建表,比手写 SQL 省事。
  • Django 自带 Admin 后台,可以直接在后台查看商品和销售记录,方便调试。
  • Django 的 MTV 架构适合承载多个 App,比如爬虫 App、销售分析 App、预测 App,代码边界更清晰。

随机森林回归对比其他算法的选择逻辑如下:

算法优势劣势适用场景
线性回归简单、可解释性强无法处理非线性关系强线性关系的基线模型
随机森林回归非线性拟合好、容忍噪声模型体积大、预测速度一般电商销量这类多因子场景
XGBoost / LightGBM精度更高、训练更快调参复杂、容易过拟合数据量大且有调优成本的团队

3. 环境准备与 Django 项目初始化

3.1 创建虚拟环境

建议使用 Python 3.8 及以上版本。虚拟环境可以避免不同项目之间的依赖冲突,这是 Python 项目开发的第一步。

python -m venv venv source venv/bin/activate # Windows 使用 venv\Scripts\activate

3.2 安装依赖包

本项目需要的核心依赖如下:

pip install django requests beautifulsoup4 pandas scikit-learn joblib
  • django:Web 框架,负责 API 和页面。
  • requests + beautifulsoup4:爬虫请求和页面解析。
  • pandas:数据清洗和特征工程。
  • scikit-learn:随机森林回归模型。
  • joblib:模型保存和加载。

Django 版本建议使用 3.2 及以上版本,具体以官方当前稳定版为准。安装完可以用python -m django --version检查版本。

3.3 创建项目和 App

django-admin startproject sales_system cd sales_system python manage.py startapp crawler python manage.py startapp sales python manage.py startapp prediction python manage.py migrate python manage.py createsuperuser

这里拆了三个 App,分工如下:

  • crawler:负责爬虫数据采集。
  • sales:负责销售记录、商品模型的 CRUD 和 API 接口。
  • prediction:负责模型训练脚本和预测逻辑。

migrate会生成 Django 默认的数据表,createsuperuser创建后台管理员账号。

4. 数据采集与预处理

4.1 爬虫的合规边界

先强调一个重要原则:爬虫必须遵守目标网站的 robots 协议、服务条款和相关法律法规。本文的示例代码只演示请求结构,不针对任何真实电商网站做绕过反爬的操作。如果业务中需要对接淘宝等平台的数据,请优先使用官方开放接口,或者通过合法授权的数据服务商获取数据,而不是自行抓取。

4.2 爬虫基础示例

下面是一个通用爬虫结构,使用 requests 请求页面,BeautifulSoup 解析商品信息。实际开发时,选择器需要根据目标网页结构调整。

# 文件路径:crawler/spider.py import time import random import requests from bs4 import BeautifulSoup 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" } def fetch_html(url): """请求页面并返回 HTML 字符串""" try: resp = requests.get(url, headers=HEADERS, timeout=10) resp.raise_for_status() resp.encoding = resp.apparent_encoding return resp.text except Exception as e: print(f"[ERROR] 请求失败: {e}") return None def parse_products(html): """解析商品列表页,提取名称、价格、销量""" soup = BeautifulSoup(html, "html.parser") products = [] for item in soup.select(".product-item"): name_node = item.select_one(".name") price_node = item.select_one(".price") sales_node = item.select_one(".sales") if not all([name_node, price_node, sales_node]): continue name = name_node.get_text(strip=True) price = float(price_node.get_text(strip=True).replace("¥", "")) sales_text = sales_node.get_text(strip=True).replace("人付款", "") sales = int(sales_text) if sales_text.isdigit() else 0 products.append({ "name": name, "price": price, "sales_volume": sales }) return products def crawl_product_pages(urls): """批量抓取多个商品列表页,控制抓取频率""" all_products = [] for url in urls: html = fetch_html(url) if html: all_products.extend(parse_products(html)) # 控制请求频率,避免对目标站点造成压力 time.sleep(random.uniform(1, 3)) return all_products

这段代码有两个关键设计:

  • 请求头模拟浏览器 User-Agent,降低被拒概率。
  • 每次请求之间 sleep 1 到 3 秒,控制抓取频率。

真实项目中,建议增加失败重试机制,并且把抓取结果写入日志,方便追溯。

4.3 数据清洗

爬虫抓到的数据不能直接拿来训练。常见的质量问题包括:重复记录、价格带单位、销量字段类型错误、缺失值。使用 pandas 可以快速处理。

# 文件路径:prediction/data_clean.py import pandas as pd def load_raw_data(filepath): df = pd.read_csv(filepath) print(f"原始数据量: {df.shape}") return df def clean_data(df): # 去除重复记录 df = df.drop_duplicates(subset="name") # 价格和销量转为数值类型,无法转换的置为 NaN df["price"] = pd.to_numeric(df["price"], errors="coerce") df["sales_volume"] = pd.to_numeric(df["sales_volume"], errors="coerce") # 删除关键字段为空的行 df = df.dropna(subset=["price", "sales_volume"]) # 销量不可能为负数 df = df[df["sales_volume"] >= 0] # 价格缺失时用中位数填充 df["price"].fillna(df["price"].median(), inplace=True) df.reset_index(drop=True, inplace=True) print(f"清洗后数据量: {df.shape}") return df

4.4 特征工程

模型不能直接使用日期字符串,需要把日期转换成有意义的数值特征。这里构造四类特征:

  • 时间特征:星期几、月份、是否周末。
  • 价格特征:商品原始价格。
  • 促销特征:是否处于促销状态。
  • 商品特征:品类编号。
# 文件路径:prediction/feature_engineering.py import pandas as pd def build_features(df): df_feat = df.copy() df_feat["sale_date"] = pd.to_datetime(df_feat["sale_date"]) df_feat["day_of_week"] = df_feat["sale_date"].dt.dayofweek df_feat["month"] = df_feat["sale_date"].dt.month df_feat["is_weekend"] = df_feat["day_of_week"].apply(lambda x: 1 if x >= 5 else 0) df_feat["is_promotion"] = df_feat["is_promotion"].astype(int) df_feat["price_bin"] = pd.cut( df_feat["price"], bins=[0, 50, 100, 200, 500, float("inf")], labels=[0, 1, 2, 3, 4] ).astype(int) feature_cols = [ "day_of_week", "month", "is_weekend", "price", "is_promotion", "price_bin" ] return df_feat, feature_cols

这里特别说明一点:特征列顺序在训练和预测时必须保持一致。很多模型上线后效果异常,不是因为模型本身,而是因为线上预测时特征顺序变了。

5. 随机森林回归销量预测模型

5.1 随机森林回归的核心原理

随机森林属于集成学习中的 Bagging 方法。训练时随机抽取样本子集和特征子集构建多棵决策树,回归任务取所有树预测结果的平均值。这种机制带来的好处是:

  • 每棵树只看到部分数据,降低了单棵树的过拟合风险。
  • 特征子集随机性让树之间的相关性降低,集成效果更稳定。
  • 不需要做特征归一化,树模型对特征尺度不敏感。

与线性回归相比,随机森林天然能捕捉销量和价格、促销之间的非线性关系;与 XGBoost 相比,它默认参数下的表现更稳定,适合作为项目的第一个模型。

5.2 模型训练代码

# 文件路径:prediction/train_model.py import joblib import pandas as pd from sklearn.ensemble import RandomForestRegressor from sklearn.model_selection import train_test_split from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score from data_clean import load_raw_data, clean_data from feature_engineering import build_features # 1. 读取并清洗数据 df_raw = load_raw_data("sales_data.csv") df_clean = clean_data(df_raw) # 2. 特征工程 df_feat, feature_cols = build_features(df_clean) # 3. 划分训练集和测试集 X = df_feat[feature_cols] y = df_feat["sales_volume"] X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42 ) # 4. 训练随机森林回归模型 model = RandomForestRegressor( n_estimators=200, max_depth=10, min_samples_split=5, min_samples_leaf=2, random_state=42, n_jobs=-1 ) model.fit(X_train, y_train) # 5. 模型评估 y_pred = model.predict(X_test) print("MAE:", mean_absolute_error(y_test, y_pred)) print("RMSE:", mean_squared_error(y_test, y_pred, squared=False)) print("R2:", r2_score(y_test, y_pred)) # 6. 保存模型和特征列名 joblib.dump(model, "sales_forecast_model.pkl") joblib.dump(feature_cols, "feature_cols.pkl")

这里有几个细节需要注意:

  • random_state=42固定随机种子,保证训练结果可以复现。
  • n_jobs=-1表示使用所有 CPU 核心训练,加快速度。
  • 模型和特征列名都要保存,预测时加载两个文件。

5.3 超参数调优

如果模型效果不理想,优先调整三个参数:

参数作用调大风险调小风险
n_estimators决策树数量训练时间变长,收益递减模型不稳定
max_depth树的最大深度过拟合欠拟合
min_samples_split内部节点分裂所需最少样本数欠拟合过拟合

使用 GridSearchCV 可以快速搜索参数组合:

from sklearn.model_selection import GridSearchCV param_grid = { "n_estimators": [100, 200, 300], "max_depth": [5, 10, 15], "min_samples_split": [2, 5, 10] } grid = GridSearchCV( RandomForestRegressor(random_state=42, n_jobs=-1), param_grid, cv=5, scoring="neg_mean_absolute_error" ) grid.fit(X_train, y_train) print("Best params:", grid.best_params_) print("Best score:", -grid.best_score_)

6. Django 后端与可视化看板实现

6.1 Django 模型设计

在 sales App 中定义商品和销售记录两个模型。

# 文件路径:sales/models.py from django.db import models class Product(models.Model): name = models.CharField(max_length=200, unique=True) category = models.CharField(max_length=50) price = models.FloatField() def __str__(self): return self.name class SalesRecord(models.Model): product = models.ForeignKey( Product, on_delete=models.CASCADE, related_name="sales_records" ) sale_date = models.DateField() sales_volume = models.IntegerField() is_promotion = models.BooleanField(default=False) class Meta: db_table = "sales_record" def __str__(self): return f"{self.product.name} - {self.sale_date}"

设计思路是:Product 表保存商品维度信息,SalesRecord 表保存每日销售明细。通过外键关联,可以方便地做按商品、按日期的聚合查询。

创建完模型后执行迁移命令:

python manage.py makemigrations python manage.py migrate

6.2 API 接口实现

在 views.py 中提供两个接口:销量趋势接口和销量预测接口。

# 文件路径:sales/views.py import joblib import pandas as pd from datetime import datetime, timedelta from django.http import JsonResponse from django.views.decorators.http import require_GET from django.db.models import Sum from .models import SalesRecord model = joblib.load("sales_forecast_model.pkl") feature_cols = joblib.load("feature_cols.pkl") @require_GET def sales_trend(request): """返回近 N 天的每日销量汇总""" days = int(request.GET.get("days", 30)) start_date = datetime.now().date() - timedelta(days=days) records = ( SalesRecord.objects .filter(sale_date__gte=start_date) .values("sale_date") .annotate(total=Sum("sales_volume")) .order_by("sale_date") ) data = [ {"date": str(r["sale_date"]), "total": r["total"]} for r in records ] return JsonResponse({"code": 0, "data": data}) @require_GET def sales_forecast(request): """预测未来 7 天的销量""" future_dates = pd.date_range(start=datetime.now().date(), periods=7) future_df = pd.DataFrame({ "day_of_week": future_dates.dayofweek, "month": future_dates.month, "is_weekend": [1 if d >= 5 else 0 for d in future_dates.dayofweek], "price": [100] * 7, "is_promotion": [0] * 7, "price_bin": [2] * 7 }) pred = model.predict(future_df[feature_cols]) result = [ {"date": str(d.date()), "prediction": round(float(p), 2)} for d, p in zip(future_dates, pred) ] return JsonResponse({"code": 0, "data": result})

sales_forecast接口中使用了默认价格和促销状态,实际项目应该从商品维度传入,或者读取最近一段时间的平均价格。

6.3 URL 路由配置

在 sales App 下新建 urls.py:

# 文件路径:sales/urls.py from django.urls import path from . import views urlpatterns = [ path("api/sales/trend", views.sales_trend, name="sales_trend"), path("api/sales/forecast", views.sales_forecast, name="sales_forecast"), ]

在主路由中注册:

# 文件路径:sales_system/urls.py from django.contrib import admin from django.urls import path, include urlpatterns = [ path("admin/", admin.site.urls), path("", include("sales.urls")), ]

6.4 ECharts 可视化页面

在 templates 目录下创建 dashboard.html,使用 ECharts 渲染销量趋势和预测结果。

<!-- 文件路径:sales/templates/dashboard.html --> <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>母婴用品销量预测看板</title> <script src="https://cdn.jsdelivr.net/npm/echarts@5/dist/echarts.min.js"></script> <style> .chart { width: 100%; height: 400px; margin-bottom: 30px; } body { font-family: "Microsoft YaHei", sans-serif; padding: 20px; } h2 { text-align: center; } </style> </head> <body> <h2>母婴用品电商销量分析与预测看板</h2> <div id="trendChart" class="chart"></div> <div id="forecastChart" class="chart"></div> <script> // 近30天销量趋势 fetch("/api/sales/trend?days=30") .then(res => res.json()) .then(json => { const dates = json.data.map(item => item.date); const totals = json.data.map(item => item.total); const chart = echarts.init(document.getElementById("trendChart")); chart.setOption({ title: { text: "近30天销量趋势" }, tooltip: { trigger: "axis" }, xAxis: { data: dates }, yAxis: {}, series: [{ name: "销量", type: "line", data: totals, smooth: true, areaStyle: {} }] }); }); // 未来7天销量预测 fetch("/api/sales/forecast") .then(res => res.json()) .then(json => { const dates = json.data.map(item => item.date); const preds = json.data.map(item => item.prediction); const chart = echarts.init(document.getElementById("forecastChart")); chart.setOption({ title: { text: "未来7天销量预测" }, tooltip: { trigger: "axis" }, xAxis: { data: dates }, yAxis: {}, series: [{ name: "预测销量", type: "bar", data: preds, itemStyle: { color: "#5470c6" } }] }); }); </script> </body> </html>

为了让 dashboard 页面可以直接访问,需要增加一个返回模板的视图函数,并在 urls.py 中注册。注意 Django 默认不会直接渲染 templates 目录下的文件,必须通过视图返回。

7. 项目运行与效果验证

7.1 启动项目

完成以上代码后,按顺序执行:

python manage.py runserver 0.0.0.0:8000

访问http://127.0.0.1:8000/dashboard/查看可视化看板,访问http://127.0.0.1:8000/api/sales/trend?days=30可以确认接口是否返回 JSON 数据。

7.2 验证步骤

建议按照下面的顺序验证功能:

  1. 检查数据库是否有商品和销售记录,可以通过 Django Admin 后台查看。
  2. 调用销量趋势接口,确认返回数据结构是{"code": 0, "data": [...]}。
  3. 调用预测接口,确认预测结果是一个包含 7 个日期的列表。
  4. 打开 dashboard 页面,确认两张图表都有数据渲染。

7.3 判断系统是否正常

一个运行正常的系统,至少满足三个条件:

  • 接口返回状态码 200。
  • 数据库中存在清洗后的历史数据。
  • 模型接口返回的预测值与实际销量量级一致。

如果预测值全部相同或者出现负数,优先检查特征工程代码,很可能训练和预测时的特征列顺序不一致。

8. 常见问题排查与工程建议

8.1 常见问题排查清单

问题现象可能原因排查方式解决方案
爬虫抓不到数据选择器不匹配、页面结构变化打印 HTML 片段检查选择器更新选择器或改用结构化 API
中文乱码响应编码识别错误打印 resp.encoding使用 resp.apparent_encoding
Django 启动失败,端口占用8000 端口被占用命令行执行 netstat 或 lsof 查看端口换端口启动
dashboard 图表空白接口返回空列表或请求报错浏览器直接访问接口地址检查数据库数据和日期范围
模型预测值全部相同特征列顺序不一致或特征全为常值打印预测输入的特征变量统一特征工程函数
模型训练时报类型错误DataFrame 中存在 NaN打印 df.info() 检查用 dropna 或 fillna 处理缺失值

8.2 爬虫合规实践

  • 优先使用平台开放 API,除非 API 不满足需求再考虑网页抓取。
  • 抓取时设置合理的 User-Agent 和请求间隔。
  • 单日抓取量要控制在合理范围,避免对目标服务器造成压力。
  • 抓取的数据只用于学习研究,不用于商业用途,不涉及个人隐私和账号信息。

8.3 模型工程化建议

  • 把“特征列保存”这个步骤纳入模型管理流程,不能只保存模型文件。
  • 模型文件建议带上版本号和训练日期,例如model_20250101_v1.pkl。
  • 线上预测接口要做好输入参数校验,避免脏数据导致预测异常。
  • 建立模型监控机制,定期对比预测值和真实值,模型效果下降时及时重训。

8.4 部署与安全建议

  • 生产环境不要使用 Django 自带的开发服务器,使用 uWSGI 或 Gunicorn + Nginx。
  • 数据库从 SQLite 切换为 MySQL 或 PostgreSQL,并做好备份策略。
  • API 接口需要增加权限控制,至少使用 Token 鉴权,不要裸奔在公网。
  • 涉及生产数据库的操作,先备份,再在测试环境验证。

9. 总结与后续学习方向

本文完成了一个母婴用品电商销量分析与预测系统的完整闭环。从爬虫数据采集到 pandas 数据清洗,从随机森林回归模型训练到 Django 接口封装,再到 ECharts 可视化看板,每个环节都给出了可运行的代码。这套代码的价值不在于模型精度有多高,而在于把零散的技术点串成了一个完整的项目链路。

如果你想在这个项目上继续深入,建议按下面的路径学习:

  • 特征扩展:加入广告投放费用、竞品价格、天气数据、节假日日历等外部特征。
  • 模型对比:用 XGBoost、LightGBM、Prophet 和随机森林做效果对比,理解不同模型的适用场景。
  • 系统升级:增加库存预警模块,把预测结果转化为补货建议,直接指导运营动作。
  • 工程化:学习 Docker 部署、定时任务调度、模型自动化重训练。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询