资讯动态

Python电商销量预测:爬虫、Transformer与Django整合实战

发布时间:2026/9/5 17:09:42 来源:尧图企业网站定制
做 Python 电商数据预测我把爬虫、Transformer 和 Django 串成了完整方案之前在做电商销量数据相关的分析项目时我明显感觉到一个痛点很多教程只讲其中某一个环节比如“用爬虫抓商品数据”“用 pandas 做可视化”“调一个 Transformer 模型”。但实际业务里数据获取、清洗、建模、部署是环环相扣的少了哪一块都无法在真实场景里跑通。这篇文章围绕“Python 电商数据分析——销量预测”这个主线把数据采集、分析可视化、Transformer 销量预测、Django 后端展示串成一个可运行的完整闭环。新手可以跟着搭建环境并理解整体流程有基础的开发者可以直接参考代码做扩展。文章会给出核心代码、配置文件、运行命令和常见报错排查力求在本地环境也能复现。项目思路以常见电商场景为例不绑定特定平台重点在方法论和技术栈整合。1. 技术链路与核心概念1.1 为什么要把爬虫、Transformer、Django 放在一起讨论电商数据分析并不是一个独立任务而是一套数据流水线。先从公开数据源抓取商品信息、价格、评论数和销量表现然后对抓取到的数据进行清洗因为原始数据大概率存在缺失、重复、单位不一致等问题。清洗之后可以做可视化和特征分析辅助理解销量波动规律。如果要做销量预测则需要构建时序数据集并训练模型Transformer 是当前效果较好的序列建模方式之一。最后要让他人访问分析结果就需要一个 Web 后端Django 是最常见的 Python 框架。所以这条链路通俗理解是爬虫解决“数据从哪来”的问题。pandas pyecharts解决“数据长什么样”的问题。Transformer解决“未来销量怎么变”的问题。Django解决“结果怎么给别人用”的问题。如果只看单独一环在真实业务中很难落地。比如 Transformer 模型训练得再好没有数据就是空谈爬虫抓到的数据不经过清洗直接训练模型的误差也会很大。因此理解全链路比只掌握某个库更重要。1.2 销量预测场景中的数据特点电商销量数据有两个显著特点第一个特点是周期性。销量往往表现出天级别的规律例如工作日和周末会有差异也有周级别的趋势。很多商品会受季节、大促和搜索热度影响。第二个特点是序列性。销量预测本质上是时间序列预测问题我们使用过去 N 天的销量来预测未来 M 天的销量。这里就涉及序列数据的窗口切分也就是用滑窗方式构造训练样本。Transformer 最初是为自然语言处理设计的它的核心是注意力机制。注意力机制可以让模型关注一段时间内最重要的历史信息因此 Transformer 被扩展到了时间序列预测领域并在部分场景中表现优于 LSTM。1.3 Django 在项目中的定位Django 是一个重量级 Python Web 框架自带 ORM、Admin 后台、模板系统和请求处理机制。本项目中 Django 并不是用来做算法训练的而是负责三件事对外提供销量预测的 HTTP 接口。展示数据可视化图表页面。管理采集和预测的结果数据。使用 Django 的最大好处是后续如果想给前端 App、小程序或者报表系统提供数据服务只需复用同一个 ORM 模型和 API 逻辑不需要重写业务代码。2. 环境准备与项目结构2.1 版本说明本文代码以 Python 3.10 以上版本为基础如果你的电脑安装的是 3.8 或 3.9大部分代码也可以运行。建议使用虚拟环境隔离项目依赖避免不同项目之间包版本冲突。版本需要根据你的实际环境调整本文示例以常见组合为例。核心依赖如下依赖库主要用途requests发起 HTTP 请求抓取网页数据pandas数据清洗与结构化处理pyecharts可视化图表生成numpy数值计算torchTransformer 模型训练与推理djangoWeb 后端与 API 开发django-cors-headers解决本地跨域请求问题可选安装时可以使用下面的命令pip install requests pandas pyecharts numpy torch django django-cors-headers国内网络环境下如果下载速度较慢可以临时指定清华镜像源pip install requests pandas pyecharts numpy torch django django-cors-headers -i https://pypi.tuna.tsinghua.edu.cn/simple2.2 项目目录设计良好的目录结构能降低协作成本。建议按功能模块拆分ecommerce_analysis/ ├── crawler/ │ ├── __init__.py │ └── spider.py ├── analysis/ │ ├── __init__.py │ ├── data_clean.py │ └── visualize.py ├── model/ │ ├── __init__.py │ ├── dataset.py │ ├── transformer.py │ └── train.py ├── web_ui/ │ ├── manage.py │ ├── web_ui/ │ │ ├── __init__.py │ │ ├── settings.py │ │ ├── urls.py │ │ └── wsgi.py │ └── analysis_app/ │ ├── __init__.py │ ├── models.py │ ├── views.py │ ├── urls.py │ └── templates/ └── data/ ├── raw_data.csv └── clean_data.csv其中crawler模块负责采集analysis模块负责清洗与图形化分析model模块负责销量预测模型训练web_ui是 Django 工程目录。在项目的根目录下可以创建一个requirements.txt文件方便其他环境安装依赖。依赖内容根据实际安装版本生成即可不需要手工指定太细的版本号。3. 爬虫模块获取商品销量与评论数据3.1 爬虫是整个项目的数据源头训练模型需要历史销量数据。这里以模拟公开商品数据为例展示如何用 requests 抓取一个商品列表页面的基础信息。电商网站的结构经常调整而且反爬策略各不相同所以本文不针对任何特定平台做逆向而是给出通用思路。在实际项目中需要先确认目标数据源是否允许采集并遵守平台规则和 robots 协议。建议优先使用公开 API 或官方数据实在没有的情况下再考虑页面解析。这段代码的作用是构建请求头模拟普通浏览器访问。使用 requests.get 获取页面文本。利用简单字符串拆分提取商品标题、价格和销量字符串。# 文件路径crawler/spider.py import requests import re import pandas as pd class SimpleShopSpider: 一个简单通用的商品页面示例爬虫。 生产环境中应替换为真实合规的数据源并控制采集频率。 def __init__(self): self.session requests.Session() self.session.headers.update({ 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_page(self, url: str) - str: response self.session.get(url, timeout10) response.encoding response.apparent_encoding response.raise_for_status() return response.text def parse_item_blocks(self, html: str, pattern: str): 这里只是演示一种通用正则提取方式。 真实项目中建议使用 lxml 或 BeautifulSoup 做结构化解析。 return re.findall(pattern, html, re.S) def demo_run(self): demo_html div classitem span classtitle无线蓝牙耳机/span span classprice199.00/span span classsales月销 2000/span /div div classitem span classtitle智能手环/span span classprice149.00/span span classsales月销 3500/span /div title_list self.parse_item_blocks(demo_html, rclasstitle(.*?)/span) price_list self.parse_item_blocks(demo_html, rclassprice(.*?)/span) sales_list self.parse_item_blocks(demo_html, rclasssales月销\s*(\d)\) rows [] for title, price, sales in zip(title_list, price_list, sales_list): rows.append({ title: title.strip(), price: float(price), sales: int(sales) }) return pd.DataFrame(rows) if __name__ __main__: spider SimpleShopSpider() df spider.demo_run() print(df)这个示例中有几个需要注意的点。第一requests.Session可以复用连接减少多次请求的资源开销。第二response.encoding response.apparent_encoding是常用的中文编码修正方式如果页面是 GBK 编码这里可以避免乱码。第三正则提取只适合结构简单的静态页面真实网页推荐改用 XPath。跑完上面代码后可以看到类似下面的输出title price sales 0 无线蓝牙耳机 199.0 2000 1 智能手环 149.0 3500到此爬虫部分已经完成了从网页到 pandas DataFrame 的转换。3.2 爬虫运行无输出但 exit code 0 的排查思路开发阶段很常见的一个现象是爬虫程序运行后没有打印任何内容控制台只显示proceed finished with exit code 0。这不一定是代码出错了很可能是程序逻辑中没有触发输出语句或者if __name__ __main__模块没有执行预期逻辑。排查时可以按照这样的顺序检查主入口是否有 print 输出。检查函数返回值是否被接收。如果请求真实网页检查是否被反爬拦截返回了空页面或验证码页面。检查正则表达式是否匹配到数据可以先用少量文本单独测试。确认目标页面需要登录或者 Cookie如果缺少 Cookie页面内容与预期完全不同。在实际项目中爬虫采集一定要控制频率比如在两次请求之间加time.sleep(1)到time.sleep(3)避免给目标服务器造成压力。4. 数据清洗与可视化分析4.1 用 pandas 处理缺失值和重复值采集到的 CSV 数据通常存在三类问题空值、重复记录、文本中有多余符号。下面是一个典型的数据清洗代码。# 文件路径analysis/data_clean.py import pandas as pd def load_raw_data(file_path: str) - pd.DataFrame: df pd.read_csv(file_path) print(f原始数据形状{df.shape}) return df def clean_sales_data(df: pd.DataFrame) - pd.DataFrame: # 1. 删除完全重复的记录 df df.drop_duplicates() # 2. 移除销量为空的行 df df.dropna(subset[sales]) # 3. 价格、销量转为数值类型出错时使用 NaN df[price] pd.to_numeric(df[price], errorscoerce) df[sales] pd.to_numeric(df[sales], errorscoerce) # 4. 删除仍然为 NaN 的行 df df.dropna(subset[price, sales]) # 5. 日期字段统一格式如果有 if date in df.columns: df[date] pd.to_datetime(df[date], errorscoerce) df df.dropna(subset[date]) df df.sort_values(date) return df数据清洗过程中最容易被忽略的是errorscoerce。如果原始 Excel 或 CSV 中某些单元格是“暂无报价”这样的文本直接用pd.to_numeric会报错而加上errorscoerce后会把无法转换的内容置为 NaN后续统一过滤掉即可。删除空值时要注意业务含义。如果销量为空是因为商品刚上架没有销量直接删除可能导致样本减少此时可以结合业务决定是否填充为 0。批量数据操作前建议先对数据做备份。4.2 销售趋势可视化可视化环节推荐 pyecharts因为它生成的是网页图表能够方便后续嵌入 Django 页面与 ECharts 风格统一。下面生成一个折线图展示每天销量变化趋势。# 文件路径analysis/visualize.py from pyecharts.charts import Line from pyecharts import options as opts import pandas as pd def draw_sales_trend(df: pd.DataFrame, output_path: str sales_trend.html): # 假设 df 包含 date 和 sales 两列 df df.sort_values(date) date_list df[date].astype(str).tolist() sales_list df[sales].tolist() line ( Line() .add_xaxis(date_list) .add_yaxis( 销量, sales_list, is_smoothTrue, is_symbol_showFalse, linestyle_optsopts.LineStyleOpts(width2, color#5470C6), ) .set_global_opts( title_optsopts.TitleOpts(title商品销量趋势), tooltip_optsopts.TooltipOpts(triggeraxis), yaxis_optsopts.AxisOpts(name销量), xaxis_optsopts.AxisOpts(name日期, axislabel_optsopts.LabelOpts(rotate30)), ) ) line.render(output_path) print(f图表已生成{output_path})如果需要对销量和价格做联合分析可以使用散点图from pyecharts.charts import Scatter def draw_price_sales_scatter(df: pd.DataFrame): data_pair df[[price, sales]].dropna().values.tolist() scatter ( Scatter() .add_xaxis([item[0] for item in data_pair]) .add_yaxis(销量, [item[1] for item in data_pair]) .set_global_opts( title_optsopts.TitleOpts(title价格与销量关系), xaxis_optsopts.AxisOpts(name价格), yaxis_optsopts.AxisOpts(name销量), ) ) return scatter可视化不只是为了好看更重要的是帮助分析人员判断序列是否有明显趋势、周期性以及价格波动和销量之间的关系。训练 Transformer 模型前做一次趋势图观察可以避免盲目训练。5. Transformer 销量预测模型5.1 为什么销量预测可以用 Transformer在销量预测场景中我们通常会使用滑窗序列例如用前 7 天销量预测下一天销量。输入是一个长度为 7 的序列输出是一个值。相比算法细节更关键的是数据如何编码成序列。Transformer 模型中最重要的两个结构是位置编码和多头注意力。位置编码用于给模型提供顺序信息多头注意力可以帮助模型学习“在第 3 天的销量和第 5 天销量之间可能存在强关联”这样的模式。从工程实现角度这里使用 PyTorch 自带的TransformerEncoder来搭建一个简化版预测模型能够处理任意长度的历史窗口。完整的多头注意力实现比较复杂但通过 PyTorch 封装我们可以把注意力集中在业务逻辑上。5.2 时间序列窗口数据集先把销量数据切分成样本。假设窗口大小为window_size7那么前 7 天的销量向量对应第 8 天的销量标签。# 文件路径model/dataset.py import numpy as np import torch from torch.utils.data import Dataset class SalesDataset(Dataset): def __init__(self, sales_values: np.ndarray, window_size: int 7, predict_step: int 1): # 将数据转为 float32便于后续计算 self.data sales_values.astype(np.float32) self.window_size window_size self.predict_step predict_step def __len__(self): return len(self.data) - self.window_size - self.predict_step 1 def __getitem__(self, idx): x_start idx x_end idx self.window_size y_index x_end self.predict_step - 1 x self.data[x_start:x_end] y self.data[y_index] return torch.from_numpy(x).unsqueeze(-1), torch.tensor(y)这里有两个细节值得说明。第一x的 shape 被改成[window_size, 1]因为 TransformerEncoder 期望输入维度是[sequence_length, batch_size, feature_size]。第二输出只取未来一天的值如果要做多步预测可以在训练后递归把预测结果加入窗口。构造训练集和测试集时要避免数据泄露也就是说不能用测试集的数据去参与训练集的归一化。正确做法是先用训练集计算均值和标准差再用同样的参数处理测试集。# 归一化示例 from sklearn.preprocessing import StandardScaler scaler StandardScaler() train_values train_df[[sales]].values scaler.fit(train_values) train_scaled scaler.transform(train_values) test_scaled scaler.transform(test_df[[sales]].values)5.3 构建 Transformer 预测模型定义模型时我们使用nn.TransformerEncoderLayer和nn.TransformerEncoder。由于销量数据通常是连续值所以这里使用回归头输出一个标量值。# 文件路径model/transformer.py import math import torch import torch.nn as nn class PositionalEncoding(nn.Module): def __init__(self, d_model: int, max_len: int 1000, dropout: float 0.1): super().__init__() self.dropout nn.Dropout(pdropout) pe torch.zeros(max_len, d_model) position torch.arange(0, max_len, dtypetorch.float).unsqueeze(1) div_term torch.exp(torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model)) pe[:, 0::2] torch.sin(position * div_term) pe[:, 1::2] torch.cos(position * div_term) pe pe.unsqueeze(0).transpose(0, 1) self.register_buffer(pe, pe) def forward(self, x): # x: [sequence_length, batch_size, d_model] x x self.pe[: x.size(0), :] return self.dropout(x) class SalesTransformer(nn.Module): def __init__(self, feature_size: int 1, d_model: int 32, nhead: int 4, num_layers: int 2, window_size: int 7): super().__init__() self.d_model d_model self.input_proj nn.Linear(feature_size, d_model) self.positional_encoding PositionalEncoding(d_model) encoder_layer nn.TransformerEncoderLayer(d_modeld_model, nheadnhead, batch_firstFalse) self.transformer_encoder nn.TransformerEncoder(encoder_layer, num_layersnum_layers) self.output_proj nn.Linear(d_model, 1) def forward(self, x): # x 输入 shape: [batch_size, window_size, feature_size] # TransformerEncoder 要求 [sequence_length, batch_size, feature_size] x x.permute(1, 0, 2) x self.input_proj(x) * math.sqrt(self.d_model) x self.positional_encoding(x) x self.transformer_encoder(x) # 取最后一个时间步的输出做预测 x x[-1, :, :] out self.output_proj(x) return out.squeeze(-1)模型的关键点是permute这一步把[batch_size, window_size, feature_size]转成[window_size, batch_size, feature_size]。新手在这里容易忘记维度变化如果出现报错优先检查张量形状。d_model是内部隐藏维度如果数据量较小设置 32 或 64 就够没必要设置 512 那样的大维度。5.4 训练脚本训练思路比较简单确定模型、确定损失函数、确定优化器然后按批次迭代。每次反向传播前需要把上一轮梯度清零否则梯度会累积。# 文件路径model/train.py import numpy as np import torch import torch.nn as nn from torch.utils.data import DataLoader from dataset import SalesDataset from transformer import SalesTransformer def build_loader(values: np.ndarray, window_size: int 7, batch_size: int 32, shuffle: bool True): dataset SalesDataset(values, window_sizewindow_size) loader DataLoader(dataset, batch_sizebatch_size, shuffleshuffle) return loader def train_model(train_loader, model, epochs30, lr1e-3): device torch.device(cuda if torch.cuda.is_available() else cpu) model.to(device) optimizer torch.optim.Adam(model.parameters(), lrlr) criterion nn.MSELoss() for epoch in range(epochs): model.train() total_loss 0.0 for x_batch, y_batch in train_loader: x_batch x_batch.to(device) y_batch y_batch.to(device) optimizer.zero_grad() pred model(x_batch) loss criterion(pred, y_batch) loss.backward() optimizer.step() total_loss loss.item() * x_batch.size(0) avg_loss total_loss / len(train_loader.dataset) if (epoch 1) % 5 0: print(fEpoch {epoch 1}/{epochs}, Loss: {avg_loss:.6f}) return model训练轮数epochs并不总是越多越好。实际项目中发现数据量较小的时候训练 20 到 50 轮就足够了轮数太多可能让模型记住训练集噪声导致验证集误差上升。可以在每个 epoch 后打印验证集损失选择验证集损失最低的模型参数保存。保存模型时推荐只保存状态字典而不是整个模型对象这样在版本升级后更容易加载。torch.save(model.state_dict(), sales_transformer.pth)5.5 简单推理与模型评估训练完成后模型需要还原预测值。由于我们之前做了标准化预测结果要先反标准化才能得到真实的销量数字。def predict_next_sales(model, recent_values: np.ndarray, scaler, window_size: int 7): model.eval() device torch.device(cuda if torch.cuda.is_available() else cpu) model.to(device) if len(recent_values) window_size: raise ValueError(f最近数据长度必须大于等于 {window_size}) last_window recent_values[-window_size:].reshape(1, window_size, 1) last_window_tensor torch.from_numpy(last_window.astype(np.float32)).to(device) with torch.no_grad(): scaled_pred model(last_window_tensor).cpu().numpy() # scaler 在训练时对 2D 数据做标准化因此这里需要 reshape pred_real scaler.inverse_transform(scaled_pred.reshape(-1, 1)) return float(pred_real[0][0])评估指标常用 MAE 和 RMSE它们的含义都是误差越小越好。RMSE 对大误差更敏感如果数据中存在个别异常大促日RMSE 会明显放大此时可以结合 MAE 一起观察。6. Django 后端整合与页面展示6.1 创建 Django 项目与子应用前面几节完成了爬虫、分析和建模现在用 Django 把能力封装成一个 Web 应用。第一步先创建项目。django-admin startproject web_ui cd web_ui python manage.py startapp analysis_app创建完成后把analysis_app添加到INSTALLED_APPS中。# 文件路径web_ui/web_ui/settings.py INSTALLED_APPS [ django.contrib.admin, django.contrib.auth, django.contrib.contenttypes, django.contrib.sessions, django.contrib.messages, django.contrib.staticfiles, corsheaders, analysis_app, ]如果前后端分离开发需要允许跨域请求则在settings.py中追加MIDDLEWARE [ corsheaders.middleware.CorsMiddleware, ... ] CORS_ALLOW_ALL_ORIGINS True在正式项目中CORS_ALLOW_ALL_ORIGINS不建议设置为 True因为等于允许任何网站访问后端接口。更安全的做法是指定允许的域名列表。6.2 设计销量数据模型Django ORM 模型只需要定义几个字段后续可以通过python manage.py makemigrations和python manage.py migrate同步数据库。# 文件路径web_ui/analysis_app/models.py from django.db import models class Product(models.Model): title models.CharField(max_length255, verbose_name商品标题) price models.FloatField(verbose_name价格) created_at models.DateTimeField(auto_now_addTrue, verbose_name创建时间) class Meta: verbose_name 商品 verbose_name_plural 商品 class DailySales(models.Model): product models.ForeignKey(Product, on_deletemodels.CASCADE, verbose_name商品) date models.DateField(verbose_name日期) sales models.FloatField(verbose_name销量) class Meta: verbose_name 每日销量 verbose_name_plural 每日销量 unique_together (product, date)这里用到ForeignKey关联商品和每日销量。注意unique_together (product, date)可以防止同一天同一条商品写入多条记录这比在代码里做重复判断更可靠。6.3 构建预测结果 API接下来提供两个接口一个是获取可视化数据另一个是执行预测。为了简化这里把训练好的模型放在analysis_app/services.py中统一加载。# 文件路径web_ui/analysis_app/services.py import pickle import numpy as np class SalesPredictor: def __init__(self, model_path: str, scaler_path: str, window_size: int 7): self.window_size window_size # 这里建议使用模型加载函数避免在 view 中重复读取 self.model load_sales_model(model_path) with open(scaler_path, rb) as f: self.scaler pickle.load(f) def predict(self, recent_sales: list): values np.array(recent_sales, dtypenp.float32) return predict_next_sales(self.model, values, self.scaler, self.window_size)实际代码中我建议把训练好的.pth文件放在web_ui/models/目录下并在settings.py中配置路径变量这样不会散落得到处都是。6.4 编写视图与路由视图函数中先把数据库中的最近 N 天销量取出来再调用模型得到预测结果最后返回 JSON。# 文件路径web_ui/analysis_app/views.py import json from django.http import JsonResponse from django.views.decorators.csrf import csrf_exempt from .models import Product, DailySales csrf_exempt def predict_sales_api(request): if request.method ! POST: return JsonResponse({code: 400, message: 仅支持 POST 请求}, status400) body json.loads(request.body or {}) product_id body.get(product_id) if not product_id: return JsonResponse({code: 400, message: 缺少 product_id}, status400) sales_rows DailySales.objects.filter(product_idproduct_id).order_by(-date)[:30] sales_values [row.sales for row in reversed(sales_rows)] if len(sales_values) 7: return JsonResponse({code: 400, message: 历史销量不足 7 天无法预测}, status400) predictor load_predictor() next_sales predictor.predict(sales_values) return JsonResponse({code: 0, data: {predict_sales: round(next_sales, 2)}})这段代码中我加了两个判断请求方法必须为 POST历史数据必须足够。原因在于销量预测至少需要一个长度为window_size的窗口。把这类判断放在业务入口处可以减少调用模型时出现维度错误。路由配置分为两步。首先在web_ui/web_ui/urls.py中包含子应用的路由# 文件路径web_ui/web_ui/urls.py from django.contrib import admin from django.urls import path, include urlpatterns [ path(admin/, admin.site.urls), path(api/, include(analysis_app.urls)), ]然后在子应用中增加路由# 文件路径web_ui/analysis_app/urls.py from django.urls import path from . import views urlpatterns [ path(predict/, views.predict_sales_api), ]这里并不需要在真实项目中暴露 Django 默认的 Admin 后台如果只是为了给内部使用可以保留如果是生产环境建议对 Admin 路径做二次保护例如结合django-admin-honeypot之类的插件或至少修改默认 URL。6.5 页面展示数据如果不想单独开发前端页面可以直接在 Django 的模板文件中引用 ECharts 的 CDN通过一个简单的页面读取本地静态 JSON 数据。下面是一个折线图页面模板思路。!-- 文件路径web_ui/analysis_app/templates/sales_chart.html -- !DOCTYPE html html langzh-CN head meta charsetUTF-8 title销量趋势分析/title script srchttps://cdn.jsdelivr.net/npm/echarts5/dist/echarts.min.js/script /head body div idchart stylewidth: 90%; height: 500px; margin: 20px auto;/div script fetch(/api/sales_trend/?product_id1) .then(function (response) { return response.json(); }) .then(function (data) { var chart echarts.init(document.getElementById(chart)); chart.setOption({ title: { text: 商品销量趋势 }, tooltip: { trigger: axis }, xAxis: { type: category, data: data.dates }, yAxis: { type: value }, series: [{ type: line, data: data.sales, smooth: true }] }); }); /script /body /html实际开发中如果使用本地可视化工具生成的 HTML 文件也可以用 iframe 嵌入但在 Django 项目里更推荐直接调用 ECharts 的 API 数据。这样数据更新后图表会同步变化不需要重新生成整个 HTML。7. 全流程串联与运行结果7.1 执行顺序建议整个项目最终的执行顺序如下启动爬虫脚本抓取原始数据并保存为 CSV。运行数据清洗脚本输出干净数据文件。用 pandas 处理出按天的销量序列。运行模型训练脚本得到sales_transformer.pth和归一化器文件。启动 Django 服务。调用预测 API 验证结果。可以先写一个根目录的run_all.py调度脚本方便一键执行前四步但这只是为了本地测试方便正式项目中不建议把训练步骤和 Web 服务启动混在一起。7.2 Django 启动与 API 调用一切就绪后使用下面的命令启动 Web 服务python manage.py runserver启动成功后访问http://127.0.0.1:8000如果看到 Django 默认页面说明项目正常。然后用 curl 模拟 POST 请求curl -X POST http://127.0.0.1:8000/api/predict/ \ -H Content-Type: application/json \ -d {product_id: 1}返回内容大致如下{ code: 0, data: { predict_sales: 326.55 } }请记住预测结果只是基于历史趋势给出的参考值。实际销量会受到促销、商品评价、搜索热度、库存和平台流量分配影响所以线上使用预测结果时需要配合人工校验。7.3 如何判断预测结果是否合理最简单的判断方式是把预测值和最近几天的实际销量画在同一张图上。如果最近一周销量一直稳定在 300 左右模型预测 326属于合理波动。如果销量中有大促日比如 11 月 11 日销量是平日的 20 倍而模型没有把促销特征纳入那么预测值很可能严重偏小。此时应该补充额外的特征列例如“是否促销日”“是否是周末”。8. 高频问题排查与解决思路下面是整理出来的 JSON 格式异常排查表适用于项目联调阶段。问题现象常见原因解决思路启动时提示ModuleNotFoundError: No module named torch当前 Python 环境中未安装 PyTorch执行pip install torch并检查是否激活了正确的虚拟环境执行迁移时报错django.db.utils.OperationalError: no such table还没执行migrate先运行python manage.py makemigrations analysis_app再运行python manage.py migrate爬虫运行后无输出主入口逻辑没有 print 或正则没匹配到结果增加 print 调试先用本地 HTML 字符串测试正则训练时 loss 为 NaN数据中存在无穷值或学习率过大检查标准化结果确认没有 inf调低 lr 到 1e-4Transformer 维度报错输入张量 shape 不符合[seq_len, batch, feature]打印每个张量的 shape确认 permute 是否生效Django 接口返回 403CSRF 验证失败如果是内部调试接口可使用csrf_exempt生产环境建议使用认证和 CSRF Token预测结果一直是同一个固定值模型没有收敛或输入窗口数据标准化方式错误检查训练是否完成打印训练集和测试集损失在实际排查中最常用的是打印中间结果的 shape。尤其在 PyTorch 项目中维度不匹配是最高频问题。可以写一行临时代码print(x shape:, x.shape)然后在出错前观察输出即可。这类调试代码在确认无误后记得删除或者用logging.debug保留下来。9. 工程化建议与生产环境注意事项9.1 数据处理与合规电商数据采集必须遵守法律法规和平台规则。不要把爬虫模块做成绕过登录、暴力遍历等高风险工具。实际方案里应优先使用平台开放的 API、公开数据集或者自行记录业务数据库中的历史数据。对于需要登录才能访问的内容必须获得授权再采集。采集代码中要加入频率控制、超时设置、重试机制。不要对目标网站发起高并发请求否则既容易触发风控也可能给目标服务器带来压力。9.2 模型训练的工程化模型训练中最重要的习惯是固定随机种子和拆分训练测试集。固定随机种子可以保证实验的可复现性import random import numpy as np import torch def set_seed(seed: int 42): random.seed(seed) np.random.seed(seed) torch.manual_seed(seed) if torch.cuda.is_available(): torch.cuda.manual_seed_all(seed)此外不要把测试集数据用于调参也不要在验证集上反复迭代太多次。更严谨的做法是切分出训练集、验证集和测试集三部分。基于时间序列数据时不要使用普通随机切分而应该按时间顺序切分避免未来数据泄露到训练集中。9.3 Django 生产部署Django 内置的开发服务器只用于本地调试生产环境推荐使用 Gunicorn 或 uWSGI 配合 Nginx 部署。如果只是对内提供 API也可以用 Gunicorn 直接启动gunicorn web_ui.wsgi:application -b 0.0.0.0:8000 -w 4-w 4表示启动 4 个 worker 进程。具体 worker 数量应该根据服务器 CPU 核心数和接口耗时设置一般建议2 * CPU 核心数 1但这并不是绝对公式。数据库连接配置也要修改测试阶段可以使用 SQLite正式环境建议切换为 MySQL 或 PostgreSQL。切换时需要修改settings.py中的数据库配置并提前在目标数据库创建好库和用户名。9.4 日志与监控线上服务加入完整的日志非常关键。Django 的settings.py中默认有 LOGGING 配置可以自定义日志文件输出路径。模型预测接口还需要记录请求参数、模型版本、预测时间这样当天后预测结果出现偏差时可以快速定位是哪一版模型或哪一批数据导致的。建议在返回预测结果的响应中带上model_version字段同时保存请求摘要。这是比较容易被忽略的细节但如果后续需要模型迭代回溯会非常有用。9.5 模块解耦与代码维护爬虫、清洗、模型训练、Django Web 服务解耦后任何一处升级都不会影响其他模块。例如把爬虫模块替换成读取数据库数据Django 和模型模块不需要改动。训练模型时如果从 PyTorch 换到其他框架也应该通过模型加载接口隔离变化。如果要长期维护这个项目建议为关键函数补上类型注解和简单的 docstring。比如predict_next_sales的参数和返回类型写清楚后其他人接手时不用从函数体反推数据结构。10. 总结与下一步学习建议从这篇实战教程中可以掌握一条完整的电商数据分析链路用 Python 爬虫获取数据用 pandas 清洗数据用 pyecharts 呈现趋势用 Transformer 完成销量预测再用 Django 把预测能力封装成 API。如果想把模型效果做得更好下一步可以从三个方向深入。第一是特征扩展。除了历史销量之外还可以加入价格、折扣力度、评论数、收藏数、搜索指数等特征模型可以从多维特征中学习到更复杂的销量影响因素。第二是调参与结构优化。可以先通过网格搜索确定窗口大小、层数、学习率等超参数。如果数据量较大可以尝试更大维度的d_model但要注意防止过拟合。第三是部署升级。尝试把训练好的模型封装成独立的推理服务再通过 Django 或其他网关调用这样可以让模型更新不会影响 Web 服务稳定性。实际业务落地中不要盲信任何单一模型销量预测本质上是概率估计。建议保存所有历史预测结果定期做回测对比像维护业务指标一样维护模型误差。希望这篇文章能够帮你快速打通从数据采集到 Web 展示的完整链路你可以在本地环境把代码跑通后再逐步替换成自己业务中的真实数据。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价