ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Python电商销量预测系统:爬虫+Django+随机森林实战

Python电商销量预测系统:爬虫+Django+随机森林实战 在电商运营中销量分析不只是看销售报表更重要的是通过历史数据判断未来走势提前调整库存和营销策略。母婴用品这类商品受季节、促销、用户评价等因素影响明显单纯靠人工经验很难把握规律。本文会围绕一个完整的 Python 电商销量分析与预测系统说明如何用爬虫获取商品数据用 Django 搭建 Web 管理系统用随机森林回归算法构建销量预测模型并将预测结果、商品趋势、品牌占比等信息通过可视化图表展示出来。读者可以按照文章顺序从环境准备、数据采集、特征工程、模型训练到 Django 集成逐步落地最终得到一个可运行、可扩展的销量分析预测系统。这套系统的技术主线很明确先解决数据从哪里来再解决数据怎么处理然后解决销量怎么预测最后解决业务人员怎么看结果。四部分串联起来就是一个典型的数据分析项目闭环。学习过程中你会同时接触到爬虫、数据清洗、机器学习建模和 Web 开发适合想用 Python 独立完成一个综合项目的开发者。1. 先理解销量预测系统由哪几个模块组成1.1 电商销量预测要解决什么问题电商平台的销量数据总量大、波动频繁受价格、好评率、上新时间、搜索热度、竞品行为等多因素影响。母婴用品市场又有自己的特点比如孕妇装、奶粉、纸尿裤、婴儿车等品类季节性强促销节点集中品牌集中度高。如果运营人员能提前预测未来几周的销量就能更合理备货减少积压和缺货。销量预测本质上是回归问题用历史销量数据、商品属性、时间特征等作为输入预测未来某个时间段的销量数值。回归算法很多比如线性回归、决策树、支持向量回归但随机森林回归在中小规模表格数据上往往表现稳定不需要大量调参还能输出特征重要性。所以这个项目选择随机森林回归作为核心算法既有解释性又有不错的效果。1.2 系统由数据采集、数据仓库、特征工程、模型服务、可视化五层组成从工程角度看这套系统可以拆成五个层次层次负责内容关键技术点数据采集层从电商平台抓取商品销量、价格、评价、标题等数据requests、BeautifulSoup、反爬处理数据存储层保存原始数据和处理后的特征数据SQLite、MySQL、Django ORM特征工程层清洗缺失值、构造时间特征和商品特征pandas、numpy模型层训练随机森林回归模型评估误差并保存模型scikit-learn、joblib应用层Django Web 系统提供数据管理、预测入口和图表展示Django、ECharts、Chart.js实际项目中爬虫采集到的数据往往很脏比如价格字段存在文字、销量数据缺失、商品标题不规则。这些数据不能直接进入模型必须经过数据清洗和特征构造。模型训练完成后要通过接口暴露预测能力Django 的视图函数可以加载已保存的模型文件接收前端传入的商品特征返回预测销量。1.3 为什么选择 Django 和随机森林回归Django 是 Python 生态里最完整的 Web 框架之一自带 ORM、Admin 后台、模板引擎和表单处理非常适合做数据管理类系统。爬虫采集的数据可以存入 Django 模型对应的数据库表管理后台能直接查看和调整数据重新训练模型也可以做成后台操作按钮。随机森林回归属于集成学习它训练多棵决策树最终结果是所有树预测值的平均。相比单棵决策树它不容易过拟合相比线性回归它能捕捉特征之间的非线性关系相比 XGBoost、LightGBM它对数据量和特征维度要求更低训练速度对中小数据量也更快。对于母婴用品这种商品数量在几千到几万级别的场景随机森林回归的性价比很高。还有一个重要原因随机森林自带feature_importances_属性可以直接输出哪些特征对销量影响最大。运营人员看到“好评率”“价格”“商品收藏数”这些特征的重要性排序比看到黑盒模型更容易接受。2. 环境准备和依赖配置要先对齐否则后面每一步都会出问题2.1 Python 版本和依赖清单项目基于 Python 3.9 或 3.10 开发比较稳妥。Django 3.2 或 4.x 都可以scikit-learn 建议使用 1.0 以上版本pandas 和 numpy 使用最新稳定版即可。依赖清单如下Django4.0 requests2.28 beautifulsoup44.11 pandas1.5 numpy1.23 scikit-learn1.1 joblib1.2 matplotlib3.6安装命令pip install django requests beautifulsoup4 pandas numpy scikit-learn joblib matplotlib生产环境建议使用虚拟环境python -m venv venv source venv/bin/activate # Linux/macOS venv\Scripts\activate # Windows注意如果使用 Django 4.x要注意不同版本的时区配置、URL 路由写法差异。项目落地前先确认 Python 和 Django 版本是否能匹配避免反复折腾环境。2.2 项目目录结构为了把爬虫、数据清洗、模型训练和 Django 项目分离推荐这样一个目录结构baby_sales_project/ ├── manage.py ├── requirements.txt ├── config/ │ ├── __init__.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py ├── apps/ │ ├── sales/ │ │ ├── models.py │ │ ├── views.py │ │ ├── urls.py │ │ ├── admin.py │ │ └── templates/ │ └── predictor/ │ ├── models.py │ ├── views.py │ ├── urls.py │ └── ml/ │ ├── train_model.py │ ├── predict.py │ └── model.pkl ├── crawler/ │ ├── taobao_spider.py │ └── data_clean.py └── static/ └── echarts.min.js在真实项目中我建议把机器学习相关代码放在独立应用predictor中爬虫代码放在项目根目录的crawler包中。这样 Web 系统和数据处理逻辑不会耦合在一起重新训练模型、更换爬虫规则都更方便。2.3 检查环境是否可用的快速命令在开始写代码前先确认关键库能正常导入python -c import django, pandas, sklearn, bs4, requests; print(django.get_version(), pandas.__version__, sklearn.__version__)如果输出正常说明基础依赖已经就绪。这一步能提前暴露 Python 路径、依赖缺失、版本冲突等问题。3. 爬虫部分从电商平台获取母婴商品数据3.1 爬虫方案的合规思路电商平台通常有严格的反爬机制直接大量请求很容易遇到验证码、IP 封锁、数据不完整等问题。对于学习项目可以优先考虑两种方式使用公开测试数据或模拟数据构造 CSV先把系统跑通。对平台前端页面做有限量、低频的请求并遵守目标网站的 robots.txt 规则不抓取用户隐私不用于商业用途。实际展示的爬虫代码用于说明思路建议在本地小范围测试。这里以淘宝搜索结果页为例说明如何获取商品标题、价格、销量和店铺信息。3.2 使用 requests 和 BeautifulSoup 抓取搜索结果页淘宝 PC 端页面结构变化较快这里给出一个简化版本的爬虫示例重点讲解思路。import requests from bs4 import BeautifulSoup def fetch_product_list(keyword, page1): url https://s.taobao.com/search params { q: keyword, s: (page - 1) * 44, } 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 } resp requests.get(url, paramsparams, headersheaders, timeout10) resp.raise_for_status() # 注意页面数据通常通过 JavaScript 异步加载这里只是模拟解析流程 soup BeautifulSoup(resp.text, html.parser) items [] for card in soup.select(.Card--doubleCard): title card.select_one(.Title--title).text.strip() price_text card.select_one(.Price--priceInt).text.strip() sales_text card.select_one(.SalesPoint--point).text.strip() items.append({ title: title, price: price_text, sales: sales_text, }) return items这个代码不能直接照搬到生产环境因为淘宝页面结构随时会变而且搜索数据通过接口返回的情况越来越多。更稳妥的学习方案是先通过浏览器开发者工具查看页面请求找到返回 JSON 的接口再用请求接口的方式获取结构化数据。3.3 把爬虫数据保存到 Django ORM 模型爬虫采集到的数据要先定义模型才能入库。在apps/sales/models.py中定义商品表from django.db import models class Product(models.Model): title models.CharField(max_length255, verbose_name商品标题) price models.FloatField(verbose_name价格) sales models.PositiveIntegerField(verbose_name销量) comment_count models.IntegerField(default0, verbose_name评论数) fav_count models.IntegerField(default0, verbose_name收藏数) shop_name models.CharField(max_length100, blankTrue, verbose_name店铺) category models.CharField(max_length50, default母婴用品, verbose_name类目) created_at models.DateTimeField(auto_now_addTrue, verbose_name采集时间) class Meta: db_table product_info verbose_name 商品信息保存数据的逻辑from apps.sales.models import Product def save_products(items): objs [ Product( titleitem[title], pricefloat(item[price]), salesint(item[sales]), ) for item in items ] Product.objects.bulk_create(objs, ignore_conflictsTrue)使用bulk_create可以一次写入多条数据性能比循环create好很多。ignore_conflictsTrue可以跳过主键冲突的数据适合重复采集场景。3.4 爬虫数据清洗的常见问题从页面抓到的销量文本通常不是纯数字。比如“已售 5.2 万”要解析成 52000“4000 人付款”要提取 4000。这类脏数据不能直接入库需要在爬虫保存前处理。import re def parse_sales_text(text): text text.replace(已售, ).replace(人付款, ).strip() if 万 in text: return int(float(text.replace(万, )) * 10000) match re.search(r\d, text) if match: return int(match.group()) return 0价格字段同理可能包含“¥”符号和起购说明只提取整数或小数部分即可。4. 数据预处理与特征工程模型预测效果的关键4.1 从数据库读取数据并构造特征从 Django 模型的sales字段中只能看到最终销量但销量预测需要更多特征。特征工程的目标是把商品标题、价格、评论数、收藏数、采集时间等信息转换成模型能理解的特征矩阵。核心特征可以分为三类商品自身特征价格、评论数、收藏数、店铺类型、是否包邮。时间特征采集日是一年中的第几周、是否双十一、是否周末。文本特征标题中是否包含“婴儿”“纸尿裤”“奶瓶”等关键词。从数据库读取并构造 DataFrameimport pandas as pd from apps.sales.models import Product def build_dataset(): qs Product.objects.all().values() df pd.DataFrame(qs) df[price] pd.to_numeric(df[price], errorscoerce) df[sales] pd.to_numeric(df[sales], errorscoerce) df df.dropna(subset[price, sales]) df[comment_rate] df[comment_count] / (df[sales] 1) df[fav_rate] df[fav_count] / (df[sales] 1) df[week_of_year] pd.to_datetime(df[created_at]).dt.isocalendar().week.astype(int) df[is_weekend] pd.to_datetime(df[created_at]).dt.weekday 5 return df这里要注意isocalendar().week返回的是UInt32等特殊类型后续进入模型前要转成int。4.2 特征选择与相关性分析特征不是越多越好。如果加入太多无关特征随机森林虽然不容易严重过拟合但模型训练时间和内存占用会上升。建议先用相关性矩阵观察特征与销量之间的关系corr df[[price, comment_count, fav_count, sales]].corr() print(corr[sales])当评论数和收藏数与销量高度相关时可以保留如果某个特征缺失率超过 50%建议直接删除。最终进入模型的特征建议保持在 10 个以内。例如feature_cols [ price, comment_count, fav_count, comment_rate, fav_rate, week_of_year, is_weekend, ]4.3 划分训练集和测试集训练模型前要划分训练集和测试集避免把全部数据用于训练导致无法评估真实效果。from sklearn.model_selection import train_test_split X df[feature_cols] y df[sales] X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42 )这里使用固定的random_state42目的是保证每次运行代码时划分结果一致方便复现实验。5. 随机森林回归模型构建与评估5.1 随机森林回归算法原理随机森林回归的底层是多棵决策树。训练时每棵树使用从原始数据中有放回抽样得到的样本子集同时每棵树分裂时只随机选择一部分特征进行最优划分。预测时把每棵树的预测结果取平均值。这种随机性设计带来两个好处第一单棵树的过拟合被多棵树平均抵消第二特征随机选择使模型对不同特征组合都有适应能力。在销量数据这种噪声较大的场景中随机森林往往比单一决策树稳定很多。5.2 训练随机森林回归模型使用 scikit-learn 训练模型from sklearn.ensemble import RandomForestRegressor from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score model RandomForestRegressor( n_estimators200, max_depth10, min_samples_leaf2, random_state42, n_jobs-1, ) model.fit(X_train, y_train) y_pred model.predict(X_test) print(R2:, r2_score(y_test, y_pred)) print(MAE:, mean_absolute_error(y_test, y_pred)) print(RMSE:, mean_squared_error(y_test, y_pred, squaredFalse))这里的参数含义需要理解清楚参数含义调大效果调小效果n_estimators决策树数量模型更稳定但训练更慢训练快但可能欠拟合max_depth每棵树最大深度模型更复杂可能过拟合模型更简单可能欠拟合min_samples_leaf叶子节点最少样本数防止过拟合容易过拟合n_jobs并行使用的 CPU 核数训练更快但占用资源多训练慢但占用小如果数据量不大n_estimators可以先从 100 开始。max_depth不建议设得过大比如 20 以上的深度在销量数据里容易把训练数据中的噪声学进去。5.3 特征重要性分析随机森林模型训练完成后可以输出特征重要性importance_df pd.DataFrame({ feature: feature_cols, importance: model.feature_importances_, }).sort_values(importance, ascendingFalse) print(importance_df)特征重要性可以帮助运营理解销量预测的关键因素。比如发现price重要性最高说明价格对母婴用品销量影响最大如果week_of_year重要性高说明销量有很强的季节规律。5.4 保存模型供 Django 加载模型训练完成后使用 joblib 保存到predictor/ml/model.pklimport joblib joblib.dump(model, apps/predictor/ml/model.pkl)保存模型时同时保存特征列名避免后续调用时特征顺序不一致joblib.dump({model: model, feature_cols: feature_cols}, apps/predictor/ml/model.pkl)加载时只需要从这个打包字典中取出模型和特征列。6. Django 系统集成把模型和可视化接入 Web6.1 配置 Django 项目在 Django 中新增应用python manage.py startapp predictor python manage.py startapp sales在config/settings.py中注册应用INSTALLED_APPS [ django.contrib.admin, django.contrib.auth, django.contrib.contenttypes, django.contrib.sessions, django.contrib.messages, django.contrib.staticfiles, sales, predictor, ]执行数据库迁移python manage.py makemigrations python manage.py migrate6.2 创建销量预测接口在不使用 Django REST Framework 的情况下可以直接使用 Django 视图返回 JSON 数据前端通过 ECharts 解析。在predictor/views.py中写预测接口import json import joblib import pandas as pd from django.http import JsonResponse from django.views.decorators.csrf import csrf_exempt package joblib.load(apps/predictor/ml/model.pkl) model package[model] feature_cols package[feature_cols] csrf_exempt def predict_sales(request): if request.method POST: body json.loads(request.body) df pd.DataFrame([{ price: float(body[price]), comment_count: int(body[comment_count]), fav_count: int(body[fav_count]), comment_rate: float(body[comment_count]) / (float(body[fav_count]) 1), fav_rate: float(body[fav_count]) / (float(body[price]) 1), week_of_year: int(body[week_of_year]), is_weekend: int(body[is_weekend]), }]) df df[feature_cols] pred model.predict(df)[0] return JsonResponse({predicted_sales: round(float(pred), 2)}) return JsonResponse({error: method not allowed})这里要特别提醒生产环境不能使用csrf_exempt直接放开接口需要补充身份验证、参数校验和日志记录。上面的写法只用于本地学习验证。更合理的做法是把特征构造逻辑抽成公共函数避免前端传什么字段后端就构造什么字段造成特征不一致的问题。6.3 实现销量趋势分析视图销量分析页面需要展示销量趋势折线图、品类占比饼图、价格分布散点图。以销量趋势为例需要从数据库中按日期聚合销量总和from django.db.models import Sum from django.utils import timezone from datetime import timedelta def sales_trend_data(request): end_date timezone.now().date() start_date end_date - timedelta(days30) records ( Product.objects .filter(created_at__date__gtestart_date) .values(created_at__date) .annotate(totalSum(sales)) .order_by(created_at__date) ) data { dates: [str(r[created_at__date]) for r in records], sales: [r[total] for r in records], } return JsonResponse(data)前端通过 AJAX 获取数据然后用 ECharts 绘制折线图fetch(/sales/trend/) .then(response response.json()) .then(data { const chart echarts.init(document.getElementById(trendChart)); chart.setOption({ tooltip: {}, xAxis: { data: data.dates }, yAxis: {}, series: [ { name: 销量, type: line, data: data.sales } ] }); });6.4 可视化图表设计建议ECharts 是常用的可视化库适合展示销量趋势、品类分布、预测结果对比。图表设计上要注意折线图适合展示销量随时间的变化趋势。饼图适合展示不同母婴品类的销量占比。散点图适合观察价格与销量之间的关系。柱状图适合对比不同品牌或店铺的销量。不要在一张图里塞太多信息。一个图表只表达一个核心问题比花哨的全景展示更容易被业务人员理解。7. 运行验证和结果分析7.1 从零到一的启动步骤运行整个系统需要按以下顺序操作创建虚拟环境并安装依赖。执行 Django 迁移。运行爬虫采集数据或导入本地 CSV 数据。执行数据清洗和特征工程生成训练数据。运行模型训练脚本保存模型。启动 Django 服务python manage.py runserver 0.0.0.0:8000浏览器访问http://127.0.0.1:8000可以看到销量分析首页访问预测页面可以输入商品参数并得到预测值。7.2 模型效果验证模型训练完成后重点关注以下指标R2 越接近 1说明模型解释能力越强。MAE 反映预测销量与实际销量的平均偏差例如 MAE 为 200表示平均预测差 200 件。RMSE 对较大误差更敏感如果 RMSE 明显大于 MAE说明存在少数预测误差很大的样本。对于母婴用品销量数据如果数据量在 500 条以上R2 达到 0.7 以上已经是不错效果。如果 R2 过低优先检查特征工程质量而不是盲目更换算法。7.3 预测接口测试使用 curl 测试预测接口curl -X POST http://127.0.0.1:8000/predict/sales/ \ -H Content-Type: application/json \ -d {price: 129, comment_count: 1000, fav_count: 3000, week_of_year: 32, is_weekend: 1}正常返回示例{predicted_sales: 832.45}如果返回 500 错误先检查 Django 日志确认是模型加载失败还是特征列不匹配。8. 常见问题排查8.1 模型预测结果很差现象常见原因检查方式处理建议R2 接近 0 或负值特征与销量无关输出特征相关性矩阵增加价格、评价、时间等有效特征预测值总是类似固定值数据方差太小查看销量字段分布补充不同销量区间的样本训练集表现好但测试集差过拟合对比训练集和测试集指标减小max_depth增大min_samples_leaf模型效果差时不要一开始就怀疑算法。先检查数据是否覆盖足够多商品再检查特征构造是否正确最后再调参。8.2 Django 加载模型报错错误示例ValueError: The feature names should match those that were passed during fit.原因是构造预测用 DataFrame 时列的顺序或列名与训练时不一致。解决方案是加载模型时同时加载特征列名预测前强制按特征列表选取列。8.3 爬虫采集不到数据如果页面结构发生变化或触发反爬常见处理方式现象常见原因检查方式处理建议返回内容为空页面数据为异步加载打开开发者工具查看 XHR 请求找到真实 JSON 接口请求频繁被限制未控制请求频率查看响应状态码增加请求间隔、使用代理池出现验证码触发反爬检查页面是否有验证码元素使用更缓和的采集策略遵守平台规则爬虫不是本系统核心只要能获取足够的示例数据让模型跑通即可。实际项目中更推荐对接平台的开放接口或使用授权数据。8.4 中文乱码Django 页面出现中文乱码通常是因为settings.py中没有配置语言和编码LANGUAGE_CODE zh-hans TIME_ZONE Asia/Shanghai USE_I18N True USE_TZ True数据库存储乱码则要确认数据库字符集为utf8mb4。9. 最佳实践与项目扩展方向9.1 学习环境与生产环境的差异学习环境下SQLite 足够模型文件本地加载爬虫脚本手动触发。生产环境要做出多方面的改变项目学习环境做法生产环境要求数据库SQLiteMySQL/PostgreSQL 主从读写分离模型更新手动训练定时或事件触发重训练保留模型版本预测接口直接加载模型文件模型服务独立部署接口加鉴权和限流爬虫运行本地脚本分布式调度日志监控失败重试前端展示单机访问Nginx 静态资源缓存图表结果数据备份无定期备份数据库和模型文件9.2 模型重训练的触发时机销量数据每天都在新增模型不可能一劳永逸。建议在满足以下条件时重新训练模型每天新增数据达到训练集规模的 10% 以上。商品结构发生明显变化比如新增了大量之前没有的品类。预测误差连续一周超过阈值。重训练时不要覆盖历史模型建议按日期保存模型文件logs/ ├── model_20250101.pkl ├── model_20250107.pkl └── model_20250114.pkl这样一旦新模型效果不理想可以快速回退到旧版本。9.3 多模型对比与融合随机森林回归是基线模型后续可以尝试两个方向用梯度提升树模型进行对比例如 XGBoost 和 LightGBM看是否能在 RMSE 上带来提升。使用 Stacking 融合策略把随机森林的预测结果作为梯度提升模型的输入特征之一。每次对比都固定训练集和测试集划分用相同指标评估才能判断模型提升是真实效果还是随机波动。9.4 从销量预测走向库存决策销量预测只是起点。预测结果可以继续推动库存预警和采购建议当预测值超过当前库存的一定比例时触发补货提醒。当预测值连续下降时提示运营减少采购或者准备促销清库存。结合商品利润按预测销量排序生成重点运营商品清单。这部分需要根据业务规则做规则引擎本质上是在模型输出上面再加一层决策逻辑。结语电商销量分析预测系统的核心难点不在某一个技术点上而在于把爬虫、数据清洗、特征工程、模型训练、Web 展示串成一条完整链路。使用 Django 承载业务系统使用随机森林回归完成销量预测再通过 ECharts 输出可视化结果可以在一个项目中同时覆盖 Python 数据分析与 Web 开发的主要知识点。对于新手来说建议先用模拟数据跑通全流程再逐步加入真实爬虫数据和更复杂的特征工程。对于已经能跑通系统的开发者下一步可以从模型优化、多算法对比、生产环境部署三个方向继续打磨。销量预测不是一步到位的事情持续迭代数据和模型比一次性追求高精度更有价值。
返回列表