Python构建电商数据仪表盘:从爬虫到机器学习预测的完整实践
在实际电商运营和数据分析项目中,单纯看后台数字报表已经很难快速洞察趋势、定位问题。一个集成了数据采集、清洗、分析、预测和可视化的综合性仪表盘,能够将分散的数据流转化为直观的商业洞察。本文将以“淘宝购物分析”为背景,构建一个完整的 Python 数据分析项目。这个项目将串联起多个核心技术栈:使用爬虫技术获取模拟数据,应用机器学习算法(多元线性回归)进行关键指标预测,并最终通过 Flask 框架搭建一个交互式可视化大屏。
通过本文,你将能掌握如何将这些技术模块有机整合,形成一个从数据源到业务决策的可复现分析链路。无论你是希望深化 Python 数据分析能力的开发者,还是需要构建内部数据看板的数据分析师,都可以按照本文的步骤,搭建起自己的第一个“数据采集-分析-预测-可视化”一体化项目。
1. 理解项目架构与技术选型
在开始写代码之前,必须先理清整个项目的技术栈如何协同工作。一个常见的误区是直接开始写爬虫或画图,导致各模块数据格式对不上、环境依赖冲突,最终项目无法跑通。
1.1 核心模块与数据流
本项目包含四个核心模块,数据按以下流程传递:
- 数据采集模块 (Crawler) :此模块负责获取原始数据。出于法律和伦理考虑,我们不会直接爬取淘宝等商业网站。本文将采用两种替代方案:使用公开数据集,或编写一个模拟数据生成器来模拟爬虫过程。重点是理解爬虫的数据结构和后续处理流程。
- 数据处理与分析模块 (Data Processing & Analysis) :原始数据通常是杂乱无章的。此模块负责数据清洗(处理缺失值、异常值)、转换(格式化日期、数值类型)和初步分析(计算销售额、用户行为指标等)。我们将使用 Pandas 和 NumPy 库。
- 机器学习预测模块 (Machine Learning) :在分析历史数据的基础上,我们构建一个预测模型。这里选择 多元线性回归 来预测未来一段时间的“销售额”。选择它的原因在于其模型简单、可解释性强,非常适合入门级预测场景,能直观展示特征(如广告投入、促销力度、节假日)与目标变量(销售额)之间的关系。
- 可视化与展示模块 (Visualization & Web Dashboard) :这是最终成果的呈现层。我们将使用 Flask 作为轻量级 Web 框架,负责提供网页服务。在网页端,使用 ECharts 或 Plotly 这类强大的 JavaScript 图表库来绘制可视化大屏。Python 后端(Flask)负责准备分析好的数据和预测结果,通过 API 接口提供给前端页面渲染。
整个架构可以简化为: 模拟爬虫 -> Pandas 清洗 -> Scikit-learn 建模 -> Flask 服务 -> ECharts 图表 。
1.2 环境与工具清单
开始编码前,请确保你的开发环境已就绪。以下是本项目所需的核心 Python 库及其作用:
| 库名 | 用途 | 安装命令 (pip) |
|---|---|---|
| Flask | 创建 Web 服务器和应用路由 | pip install flask |
| Pandas | 数据处理与分析的核心库 | pip install pandas |
| NumPy | 数值计算,支持 Pandas 和 Scikit-learn | pip install numpy |
| Scikit-learn | 机器学习库,用于构建回归模型 | pip install scikit-learn |
| Matplotlib | 基础绘图,可用于模型诊断图 | pip install matplotlib |
| Jinja2 | Flask 默认的模板引擎,用于渲染 HTML | (通常随 Flask 安装) |
| Requests | 模拟 HTTP 请求,用于“爬虫”模块获取公开数据 | pip install requests |
| Faker | 生成模拟数据,替代真实爬虫 | pip install faker |
注意:强烈建议使用虚拟环境(如
venv或conda)来管理项目依赖,避免与系统或其他项目的 Python 包发生冲突。
2. 项目初始化与数据准备
我们首先创建项目目录结构,并解决数据来源问题。由于真实爬虫涉及复杂反爬和法律风险,我们采用模拟数据生成方案,其数据结构完全模拟电商分析的真实场景。
2.1 创建项目目录
在你的工作区创建一个新目录,例如 ecommerce_dashboard ,并建立如下子目录和文件:
ecommerce_dashboard/
├── app.py # Flask 主应用文件
├── data_generator.py # 模拟数据生成脚本
├── model_train.py # 机器学习模型训练与保存脚本
├── requirements.txt # 项目依赖列表
├── static/ # 存放静态文件 (CSS, JS, 图片)
│ └── js/ # 存放前端图表库(如 echarts.min.js)
└── templates/ # 存放 HTML 模板文件
└── index.html # 可视化大屏主页面
在项目根目录下创建 requirements.txt 文件,并写入上一节列出的依赖:
flask
pandas
numpy
scikit-learn
matplotlib
requests
faker
然后,在终端中进入项目目录,运行 pip install -r requirements.txt 安装所有依赖。
2.2 生成模拟电商数据集
我们将编写 data_generator.py ,生成一个包含关键字段的模拟销售数据集。这些字段是后续分析和预测的基础。
# data_generator.py
import pandas as pd
import numpy as np
from faker import Faker
from datetime import datetime, timedelta
import random
# 初始化Faker,用于生成随机中文信息
fake = Faker('zh_CN')
def generate_sales_data(num_records=1000, start_date='2023-01-01', end_date='2023-12-31'):
"""
生成模拟电商销售数据
Args:
num_records: 生成的数据条数
start_date: 数据开始日期
end_date: 数据结束日期
Returns:
pandas.DataFrame
"""
data = []
start = datetime.strptime(start_date, '%Y-%m-%d')
end = datetime.strptime(end_date, '%Y-%m-%d')
date_range = (end - start).days
# 模拟商品类别
categories = ['电子产品', '服装服饰', '家居日用', '美妆护肤', '食品饮料']
# 模拟支付方式
payment_methods = ['支付宝', '微信支付', '信用卡', '花呗']
for _ in range(num_records):
order_date = start + timedelta(days=random.randint(0, date_range))
# 生成模拟特征:广告投入、是否促销、是否周末、是否节假日(简化)
ad_spend = round(random.uniform(500, 5000), 2) # 广告投入
is_promotion = random.choice([0, 1]) # 0否,1是
is_weekend = 1 if order_date.weekday() >= 5 else 0
# 简单模拟几个节假日
holiday_list = ['2023-01-01', '2023-02-10', '2023-05-01', '2023-10-01']
is_holiday = 1 if order_date.strftime('%Y-%m-%d') in holiday_list else 0
# **核心:模拟销售额(目标变量),使其与特征有一定线性关系**
# 基础销售额 + 广告效应 + 促销效应 + 周末效应 + 节假日效应 + 随机噪声
base_sales = 20000
sales = (base_sales +
ad_spend * 2.5 +
is_promotion * 15000 +
is_weekend * 8000 +
is_holiday * 12000 +
random.randint(-5000, 5000))
# 确保销售额为正
sales = max(sales, 1000)
record = {
'order_id': fake.uuid4()[:8],
'order_date': order_date.strftime('%Y-%m-%d'),
'category': random.choice(categories),
'payment_method': random.choice(payment_methods),
'ad_spend': ad_spend,
'is_promotion': is_promotion,
'is_weekend': is_weekend,
'is_holiday': is_holiday,
'sales': round(sales, 2)
}
data.append(record)
df = pd.DataFrame(data)
# 将日期列转换为datetime类型,便于后续按时间分析
df['order_date'] = pd.to_datetime(df['order_date'])
return df
if __name__ == '__main__':
# 生成1000条模拟数据
df = generate_sales_data(1000)
# 保存为CSV文件,供后续步骤使用
df.to_csv('sales_data.csv', index=False, encoding='utf-8-sig')
print(f"模拟数据已生成,共 {len(df)} 条,保存至 'sales_data.csv'")
print(df.head())
运行此脚本 ( python data_generator.py ),你将在项目根目录得到 sales_data.csv 文件。这个文件模拟了爬虫获取并初步清洗后的数据,包含订单ID、日期、品类、支付方式、广告投入、促销标志、周末标志、节假日标志以及最终的销售额。
3. 构建多元线性回归预测模型
有了数据,我们就可以构建预测模型。目标是利用 ad_spend (广告投入)、 is_promotion (是否促销)、 is_weekend (是否周末)、 is_holiday (是否节假日)这四个特征,来预测 sales (销售额)。
3.1 模型训练与评估
创建 model_train.py 文件,用于训练模型并保存。
# model_train.py
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score
import joblib # 用于保存和加载模型
import matplotlib.pyplot as plt
def train_and_save_model(data_path='sales_data.csv', model_path='linear_regression_model.pkl'):
"""
训练多元线性回归模型并保存
"""
# 1. 加载数据
df = pd.read_csv(data_path)
print("数据加载成功,前5行:")
print(df.head())
# 2. 准备特征 (X) 和目标变量 (y)
# 选择用于预测的特征列
feature_cols = ['ad_spend', 'is_promotion', 'is_weekend', 'is_holiday']
X = df[feature_cols]
y = df['sales']
# 3. 划分训练集和测试集 (80%训练,20%测试)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
print(f"训练集样本数: {X_train.shape[0]}, 测试集样本数: {X_test.shape[0]}")
# 4. 创建并训练模型
model = LinearRegression()
model.fit(X_train, y_train)
# 5. 在测试集上进行预测并评估
y_pred = model.predict(X_test)
# 计算评估指标
mae = mean_absolute_error(y_test, y_pred)
mse = mean_squared_error(y_test, y_pred)
rmse = np.sqrt(mse)
r2 = r2_score(y_test, y_pred)
print("\n=== 模型评估结果 ===")
print(f"平均绝对误差 (MAE): {mae:.2f}")
print(f"均方误差 (MSE): {mse:.2f}")
print(f"均方根误差 (RMSE): {rmse:.2f}")
print(f"决定系数 (R² Score): {r2:.4f}")
# 6. 输出模型系数和截距
print("\n=== 模型参数 ===")
for i, col in enumerate(feature_cols):
print(f"特征 '{col}' 的系数: {model.coef_[i]:.4f}")
print(f"模型截距: {model.intercept_:.4f}")
# 7. 可视化预测结果 vs 真实值 (可选,用于诊断)
plt.figure(figsize=(10, 6))
plt.scatter(y_test, y_pred, alpha=0.5)
plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], 'r--', lw=2) # 理想对角线
plt.xlabel('实际销售额')
plt.ylabel('预测销售额')
plt.title('多元线性回归:预测值 vs 实际值')
plt.tight_layout()
plt.savefig('prediction_vs_actual.png') # 保存图片,可供前端使用
plt.show()
# 8. 保存训练好的模型
joblib.dump(model, model_path)
print(f"\n模型已保存至 '{model_path}'")
# 9. 返回模型和特征列名,供后续Flask应用使用
return model, feature_cols
if __name__ == '__main__':
train_and_save_model()
运行此脚本 ( python model_train.py ),控制台会输出模型评估指标。R² 分数越接近 1,说明模型对数据的拟合程度越好。同时,脚本会生成一个 linear_regression_model.pkl 文件,这就是我们训练好的模型,Flask 应用将直接加载它进行预测,而无需每次启动都重新训练。
3.2 模型解读与注意事项
多元线性回归模型的预测公式为: 预测销售额 = 截距 + 系数1 * ad_spend + 系数2 * is_promotion + 系数3 * is_weekend + 系数4 * is_holiday
从输出的系数可以直观判断每个特征对销售额的影响方向和大小。例如,如果 ad_spend 的系数是正数,说明广告投入增加会带来销售额增长。
注意:在实际电商分析中,特征工程远比这里复杂。可能还需要考虑商品价格、用户评价、竞品活动、季节性趋势等。此外,线性回归假设特征与目标变量是线性关系,且特征之间相互独立。如果数据不满足这些假设(如存在多重共线性),预测效果会变差。对于更复杂的非线性关系,可能需要考虑多项式回归、决策树或神经网络等模型。本项目以演示流程为主。
4. 使用 Flask 搭建 Web 应用与 API
现在,我们将创建一个 Flask 应用,它主要做三件事:1. 提供可视化大屏的网页;2. 提供分析数据的 API 接口;3. 加载模型并提供预测接口。
4.1 创建 Flask 主应用
编辑 app.py 文件:
# app.py
from flask import Flask, render_template, jsonify, request
import pandas as pd
import numpy as np
import joblib
import os
from datetime import datetime
app = Flask(__name__)
# 全局变量,用于缓存数据和模型
sales_df = None
model = None
feature_cols = ['ad_spend', 'is_promotion', 'is_weekend', 'is_holiday']
def load_data_and_model():
"""加载数据和模型,在应用启动时或首次请求时调用"""
global sales_df, model
try:
sales_df = pd.read_csv('sales_data.csv')
sales_df['order_date'] = pd.to_datetime(sales_df['order_date'])
print("销售数据加载成功。")
except FileNotFoundError:
print("警告: 未找到 'sales_data.csv',请先运行 data_generator.py。")
sales_df = pd.DataFrame()
try:
model = joblib.load('linear_regression_model.pkl')
print("预测模型加载成功。")
except FileNotFoundError:
print("警告: 未找到 'linear_regression_model.pkl',请先运行 model_train.py。")
model = None
# 在应用启动时加载
load_data_and_model()
@app.route('/')
def index():
"""渲染主页面(可视化大屏)"""
return render_template('index.html')
@app.route('/api/sales_summary')
def get_sales_summary():
"""API: 获取核心销售指标汇总"""
if sales_df is None or sales_df.empty:
return jsonify({'error': '数据未加载'}), 500
total_sales = sales_df['sales'].sum()
avg_sales = sales_df['sales'].mean()
total_orders = len(sales_df)
# 计算月度趋势(示例:取最近6个月)
sales_df['year_month'] = sales_df['order_date'].dt.to_period('M')
monthly_sales = sales_df.groupby('year_month')['sales'].sum().tail(6)
# 将Period对象转换为字符串,便于JSON序列化
monthly_sales.index = monthly_sales.index.astype(str)
summary = {
'total_sales': round(total_sales, 2),
'avg_sales_per_order': round(avg_sales, 2),
'total_orders': total_orders,
'monthly_trend': monthly_sales.to_dict()
}
return jsonify(summary)
@app.route('/api/category_distribution')
def get_category_distribution():
"""API: 获取商品品类销售额分布"""
if sales_df is None or sales_df.empty:
return jsonify({'error': '数据未加载'}), 500
category_sales = sales_df.groupby('category')['sales'].sum().sort_values(ascending=False)
return jsonify(category_sales.to_dict())
@app.route('/api/payment_method_distribution')
def get_payment_method_distribution():
"""API: 获取支付方式分布"""
if sales_df is None or sales_df.empty:
return jsonify({'error': '数据未加载'}), 500
payment_dist = sales_df['payment_method'].value_counts().to_dict()
return jsonify(payment_dist)
@app.route('/api/predict', methods=['POST'])
def predict_sales():
"""API: 基于输入特征预测销售额"""
if model is None:
return jsonify({'error': '预测模型未加载'}), 500
try:
# 从POST请求的JSON中获取特征值
data = request.get_json()
# 确保传入的特征顺序与训练时一致
input_features = [data.get(col, 0) for col in feature_cols]
# 转换为numpy数组并reshape以符合模型输入格式
features_array = np.array(input_features).reshape(1, -1)
prediction = model.predict(features_array)[0]
return jsonify({
'predicted_sales': round(prediction, 2),
'input_features': dict(zip(feature_cols, input_features))
})
except Exception as e:
return jsonify({'error': f'预测失败: {str(e)}'}), 400
if __name__ == '__main__':
# 设置debug=True仅用于开发,生产环境应设为False
app.run(debug=True, host='0.0.0.0', port=5000)
4.2 API 接口说明
Flask 应用提供了以下关键 API 端点,前端页面将通过 Ajax 调用这些接口获取数据:
GET /:返回主页面index.html。GET /api/sales_summary:返回总销售额、平均订单额、总订单数以及最近6个月的销售额趋势数据。GET /api/category_distribution:返回各商品品类的销售额分布。GET /api/payment_method_distribution:返回不同支付方式的订单数量分布。POST /api/predict:接收 JSON 格式的特征数据,返回模型预测的销售额。请求体示例:{"ad_spend": 3000, "is_promotion": 1, "is_weekend": 0, "is_holiday": 0}。
5. 构建前端可视化大屏
前端页面使用 Jinja2 模板渲染,并引入 ECharts 库来绘制图表。我们需要准备 ECharts 的 JS 文件,并编写 index.html 。
5.1 准备 ECharts 库
从 ECharts 官网 下载最新版本的 echarts.min.js ,将其放入 static/js/ 目录下。
5.2 编写 HTML 模板
编辑 templates/index.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="{{ url_for('static', filename='js/echarts.min.js') }}"></script>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Microsoft YaHei', sans-serif; }
body { background-color: #0f1b3d; color: #fff; padding: 20px; }
.dashboard-header { text-align: center; margin-bottom: 30px; }
.dashboard-header h1 { color: #4dc9ff; font-size: 2.5em; margin-bottom: 10px; }
.dashboard-header p { color: #aaa; }
.stats-cards { display: flex; justify-content: space-around; margin-bottom: 30px; flex-wrap: wrap; }
.card { background: linear-gradient(145deg, #1a2b5f, #0d1a40); border-radius: 10px; padding: 25px; text-align: center; min-width: 200px; margin: 10px; box-shadow: 0 5px 15px rgba(0,0,0,0.3); }
.card h3 { color: #7bd0ff; margin-bottom: 15px; font-size: 1.2em; }
.card .value { font-size: 2.5em; font-weight: bold; color: #00ffcc; }
.card .unit { font-size: 0.9em; color: #aaa; }
.charts-container { display: grid; grid-template-columns: repeat(auto-fit, minmax(500px, 1fr)); gap: 25px; margin-bottom: 30px; }
.chart-panel { background-color: #162447; border-radius: 10px; padding: 20px; box-shadow: 0 5px 15px rgba(0,0,0,0.3); height: 400px; }
.chart-title { text-align: center; margin-bottom: 15px; color: #4dc9ff; font-size: 1.3em; }
.chart { width: 100%; height: 350px; }
.prediction-panel { background-color: #162447; border-radius: 10px; padding: 25px; margin-top: 20px; }
.prediction-form { display: flex; flex-wrap: wrap; gap: 20px; align-items: center; justify-content: center; }
.form-group label { display: block; margin-bottom: 8px; color: #7bd0ff; }
.form-group input, .form-group select { padding: 10px; border-radius: 5px; border: 1px solid #2a3f7f; background-color: #0f1b3d; color: white; width: 180px; }
button { padding: 12px 30px; background: linear-gradient(to right, #00c6ff, #0072ff); border: none; border-radius: 5px; color: white; font-weight: bold; cursor: pointer; transition: opacity 0.3s; }
button:hover { opacity: 0.9; }
#predictionResult { margin-top: 20px; text-align: center; font-size: 1.5em; color: #00ffcc; }
</style>
</head>
<body>
<div class="dashboard-header">
<h1>📊 电商销售数据分析与预测平台</h1>
<p>基于模拟数据的多维度可视化与机器学习预测 | Flask + ECharts + Scikit-learn</p>
</div>
<!-- 核心指标卡片 -->
<div class="stats-cards" id="statsCards">
<!-- 数据将由JS动态填充 -->
<div class="card"><h3>总销售额</h3><div class="value">--</div><div class="unit">元</div></div>
<div class="card"><h3>平均订单额</h3><div class="value">--</div><div class="unit">元</div></div>
<div class="card"><h3>总订单数</h3><div class="value">--</div><div class="unit">笔</div></div>
</div>
<!-- 图表区域 -->
<div class="charts-container">
<div class="chart-panel">
<div class="chart-title">月度销售额趋势</div>
<div id="trendChart" class="chart"></div>
</div>
<div class="chart-panel">
<div class="chart-title">商品品类销售额分布</div>
<div id="categoryChart" class="chart"></div>
</div>
<div class="chart-panel">
<div class="chart-title">支付方式分布</div>
<div id="paymentChart" class="chart"></div>
</div>
</div>
<!-- 销售额预测面板 -->
<div class="prediction-panel">
<h2 style="text-align: center; color: #4dc9ff; margin-bottom: 20px;">🔮 销售额预测 (多元线性回归模型)</h2>
<div class="prediction-form">
<div class="form-group">
<label for="adSpend">广告投入 (元):</label>
<input type="number" id="adSpend" value="3000" min="0" step="500">
</div>
<div class="form-group">
<label for="isPromotion">是否促销:</label>
<select id="isPromotion">
<option value="0">否</option>
<option value="1" selected>是</option>
</select>
</div>
<div class="form-group">
<label for="isWeekend">是否周末:</label>
<select id="isWeekend">
<option value="0">否</option>
<option value="1">是</option>
</select>
</div>
<div class="form-group">
<label for="isHoliday">是否节假日:</label>
<select id="isHoliday">
<option value="0">否</option>
<option value="1">是</option>
</select>
</div>
<button onclick="predictSales()">预测销售额</button>
</div>
<div id="predictionResult"></div>
</div>
<script>
// 1. 初始化ECharts实例
let trendChart = echarts.init(document.getElementById('trendChart'));
let categoryChart = echarts.init(document.getElementById('categoryChart'));
let paymentChart = echarts.init(document.getElementById('paymentChart'));
// 2. 定义图表通用样式
const chartTheme = {
backgroundColor: 'transparent',
textStyle: { color: '#ccc' },
grid: { left: '10%', right: '5%', bottom: '15%', top: '15%', containLabel: true }
};
// 3. 从后端API获取数据并渲染图表
function fetchDataAndRender() {
// 获取核心指标
fetch('/api/sales_summary')
.then(response => response.json())
.then(data => {
if (data.error) {
console.error(data.error);
return;
}
document.getElementById('statsCards').children[0].querySelector('.value').innerText = data.total_sales.toLocaleString();
document.getElementById('statsCards').children[1].querySelector('.value').innerText = data.avg_sales_per_order.toLocaleString();
document.getElementById('statsCards').children[2].querySelector('.value').innerText = data.total_orders.toLocaleString();
// 渲染月度趋势图(折线图)
const months = Object.keys(data.monthly_trend);
const sales = Object.values(data.monthly_trend);
const trendOption = {
...chartTheme,
xAxis: { type: 'category', data: months, axisLine: { lineStyle: { color: '#2a3f7f' } } },
yAxis: { type: 'value', axisLine: { lineStyle: { color: '#2a3f7f' } }, axisLabel: { formatter: '{value} 元' } },
series: [{
data: sales,
type: 'line',
smooth: true,
lineStyle: { color: '#00ffcc', width: 3 },
itemStyle: { color: '#4dc9ff' },
areaStyle: { color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: 'rgba(0, 255, 204, 0.6)' },
{ offset: 1, color: 'rgba(0, 255, 204, 0.1)' }
])}
}],
tooltip: { trigger: 'axis', formatter: '{b}<br/>{c} 元' }
};
trendChart.setOption(trendOption);
});
// 获取品类分布数据(饼图)
fetch('/api/category_distribution')
.then(response => response.json())
.then(data => {
const categoryData = Object.entries(data).map(([name, value]) => ({ name, value }));
const categoryOption = {
...chartTheme,
tooltip: { trigger: 'item', formatter: '{a} <br/>{b}: {c}元 ({d}%)' },
legend: { orient: 'vertical', left: 'left', top: 'center', textStyle: { color: '#ccc' } },
series: [{
name: '销售额',
type: 'pie',
radius: ['40%', '70%'],
center: ['60%', '50%'],
data: categoryData,
emphasis: { itemStyle: { shadowBlur: 10, shadowOffsetX: 0, shadowColor: 'rgba(0, 0, 0, 0.5)' } },
itemStyle: { borderRadius: 10, borderColor: '#0f1b3d', borderWidth: 2 }
}]
};
categoryChart.setOption(categoryOption);
});
// 获取支付方式分布数据(柱状图)
fetch('/api/payment_method_distribution')
.then(response => response.json())
.then(data => {
const methods = Object.keys(data);
const counts = Object.values(data);
const paymentOption = {
...chartTheme,
xAxis: { type: 'category', data: methods, axisLine: { lineStyle: { color: '#2a3f7f' } } },
yAxis: { type: 'value', axisLine: { lineStyle: { color: '#2a3f7f' } } },
series: [{
data: counts,
type: 'bar',
itemStyle: { color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#83bff6' },
{ offset: 0.5, color: '#188df0' },
{ offset: 1, color: '#188df0' }
]) },
emphasis: { itemStyle: { color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#2378f7' },
{ offset: 0.7, color: '#2378f7' },
{ offset: 1, color: '#83bff6' }
]) } }
}],
tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } }
};
paymentChart.setOption(paymentOption);
});
}
// 4. 销售额预测函数
function predictSales() {
const adSpend = parseFloat(document.getElementById('adSpend').value);
const isPromotion = parseInt(document.getElementById('isPromotion').value);
const isWeekend = parseInt(document.getElementById('isWeekend').value);
const isHoliday = parseInt(document.getElementById('isHoliday').value);
const requestData = {
ad_spend: adSpend,
is_promotion: isPromotion,
is_weekend: isWeekend,
is_holiday: isHoliday
};
fetch('/api/predict', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(requestData)
})
.then(response => response.json())
.then(data => {
const resultDiv = document.getElementById('predictionResult');
if (data.error) {
resultDiv.innerHTML = `<span style="color:#ff6b6b;">预测失败: ${data.error}</span>`;
} else {
resultDiv.innerHTML = `预测销售额: <strong>${data.predicted_sales.toLocaleString()} 元</strong><br><small>基于输入: ${JSON.stringify(data.input_features)}</small>`;
}
})
.catch(error => {
document.getElementById('predictionResult').innerHTML = `<span style="color:#ff6b6b;">请求出错: ${error}</span>`;
});
}
// 5. 页面加载完成后初始化
window.onload = function() {
fetchDataAndRender();
// 窗口大小改变时,重置图表大小
window.onresize = function() {
trendChart.resize();
categoryChart.resize();
paymentChart.resize();
};
};
</script>
</body>
</html>
6. 运行与验证
至此,所有核心模块已开发完成。现在让我们启动整个应用并进行验证。
6.1 启动 Flask 应用
- 确保你已在项目根目录下。
- 确保
sales_data.csv和linear_regression_model.pkl文件已存在(通过运行之前的脚本生成)。 - 在终端中运行:
python app.py - 如果一切正常,你将看到类似输出:
同时,控制台会打印“销售数据加载成功。”和“预测模型加载成功。”。* Serving Flask app 'app' * Debug mode: on WARNING: This is a development server. Do not use it in a production deployment. * Running on all addresses (0.0.0.0) * Running on http://127.0.0.1:5000 * Running on http://<your-local-ip>:5000
6.2 访问可视化大屏
打开浏览器,访问 http://127.0.0.1:5000 。你应该能看到一个完整的电商数据分析大屏,包含:
- 顶部核心指标卡片(总销售额、平均订单额、总订单数)。
- 中部的三个图表:月度销售额趋势折线图、商品品类销售额分布饼图、支付方式分布柱状图。
- 底部的销售额预测面板。
所有图表和数据都是动态从 Flask 后端 API 获取并渲染的。
6.3 测试预测功能
在预测面板中,调整广告投入、促销、周末、节假日的选项,点击“预测销售额”按钮。页面会向 /api/predict 发送 POST 请求,并将模型预测的结果实时显示在下方。
7. 常见问题排查与优化建议
项目运行过程中可能会遇到一些问题,以下是常见问题的排查路径和解决方案。
7.1 启动与运行问题
| 问题现象 | 可能原因 | 检查与解决方式 |
|---|---|---|
运行 python app.py 报 ModuleNotFoundError |
依赖未安装或虚拟环境未激活 | 1. 确认在项目目录下。 2. 运行 pip install -r requirements.txt 。 3. 若使用虚拟环境,请先激活。 |
访问 http://127.0.0.1:5000 页面空白或报错 |
静态文件路径错误或模板未找到 | 1. 检查 templates/index.html 和 static/js/echarts.min.js 文件是否存在且路径正确。 2. 查看 Flask 控制台是否有模板渲染错误日志。 |
| 页面图表不显示,浏览器控制台报 404 | Flask 未正确提供静态文件或 API 路径错误 | 1. 检查 index.html 中 url_for('static', ...) 的路径。 2. 在浏览器开发者工具的 Network 面板查看具体哪个 API 请求失败,核对 app.py 中的路由定义。 |
| 预测结果始终为 0 或异常值 | 模型未加载或特征数据格式错误 | 1. 检查 Flask 启动日志是否成功加载模型。 2. 检查 app.py 中 feature_cols 的顺序是否与训练时一致。 3. 在浏览器开发者工具中查看 /api/predict 请求的请求体和响应体,确认数据格式。 |
7.2 数据与模型问题
| 问题现象 | 可能原因 | 检查与解决方式 |
|---|---|---|
| 核心指标和图表无数据 | sales_data.csv 文件未生成或路径错误 |
1. 确认项目根目录下存在 sales_data.csv 。 2. 检查 app.py 中 load_data_and_model 函数的文件路径。 3. 运行 data_generator.py 重新生成数据。 |
| 模型预测误差极大 | 模拟数据与模型假设不符,或特征工程过于简单 | 1. 检查 model_train.py 输出的 R² 分数,如果很低(如<0.5),说明模型拟合差。 2. 考虑在 data_generator.py 中调整销售额的生成公式,使其与特征的相关性更强。 3. 在真实项目中,需要进行更严谨的特征工程和模型调优。 |
| 月度趋势图只显示一个点 | 数据日期范围太小或分组逻辑有误 | 1. 检查 data_generator.py 中 start_date 和 end_date 的跨度,确保超过一个月。 2. 检查 app.py 中 sales_df['year_month'] 的计算是否正确。 |
7.3 生产环境部署建议
当前项目使用 app.run(debug=True) 启动,这仅适用于开发。若要部署到生产环境,需要考虑以下方面:
- 更换 WSGI 服务器 :使用 Gunicorn(Linux/Unix)或 Waitress(Windows)等生产级 WSGI 服务器替代 Flask 内置开发服务器。
# 示例:使用 Gunicorn pip install gunicorn gunicorn -w 4 -b 0.0.0.0:8000 app:app - 配置管理 :将配置(如数据库连接字符串、密钥)从代码中分离,使用环境变量或配置文件。
- 静态文件服务 :在生产中,通常使用 Nginx 等 Web 服务器来代理动态请求并直接提供静态文件,性能更好。
- 数据库集成 :将数据从 CSV 文件迁移到 PostgreSQL、MySQL 等数据库,便于管理大量数据和执行复杂查询。
- 模型更新机制 :建立定期重新训练模型的自动化流程,并将新模型安全地更新到生产环境。
- 日志与监控 :配置应用日志、错误监控和性能指标(如使用 Prometheus + Grafana)。
8. 项目扩展与深入学习方向
本项目是一个完整的教学演示,你可以在此基础上进行扩展,使其更贴近真实业务场景。
- 接入真实数据源 :学习使用
requests、BeautifulSoup或Selenium从允许爬取的公开数据源(如政府开放数据、Kaggle 数据集)获取数据。 务必遵守网站的robots.txt协议和相关法律法规。 - 深化数据分析 :使用 Pandas 进行更复杂的分析,如用户复购率分析、RFM 用户分层、销售漏斗分析等。
- 尝试更复杂的模型 :将多元线性回归替换为其他算法,如随机森林、梯度提升树(XGBoost/LightGBM)或时间序列模型(ARIMA、Prophet),以处理非线性关系或时序预测。
- 增加交互性 :在前端使用更高级的 ECharts 特性,如数据刷选、图表联动、下钻分析。
- 用户认证与多租户 :使用 Flask-Login 等扩展为仪表盘增加登录功能,并根据用户角色展示不同数据。
- 自动化与任务调度 :使用 Celery 或 APScheduler 实现定时数据更新、模型训练任务。
通过这个项目,你不仅学会了如何将 Python 爬虫(模拟)、数据分析、机器学习和 Web 开发组合起来,更重要的是掌握了构建一个端到端数据应用的标准流程和架构思想。在实际工作中,每个模块都可能由更专业的工具承担(如 Airflow 调度、Spark 处理、TensorFlow 建模、React 前端),但理解数据从产生到产生价值的完整链路,是每一位数据工程师或数据分析师的核心能力。
更多推荐




所有评论(0)