这次我们来看一个典型的 Python 综合性项目:电商数据分析平台。它不是一个单独的算法脚本,而是把爬虫采集、数据清洗、销量预测、可视化大屏、词云图展示和 Django Web 框架串成一条完整链路的工程。如果你正在做毕业设计、竞赛项目,或者想在公司内部搭一套轻量级数据分析后台,这个方向覆盖的技术点非常全,值得完整跑一遍。
先看核心结论:项目基于 Django 框架搭建 Web 服务,用爬虫获取电商公开数据,用 pandas 做清洗和统计,用 scikit-learn 训练销量预测模型,用 ECharts 做可视化,用 wordcloud 生成词云图。整个系统可以通过浏览器访问,也能把预测能力封装成 API 接口,供其他系统调用。本文不绑定某个具体仓库,而是给出一套可直接落地的工程实现思路,并给出关键代码示例和验证方法。
1. 核心能力速览
| 能力项 | 说明 |
|---|---|
| 项目类型 | Python Web 数据分析平台 |
| 核心框架 | Django + Django REST Framework |
| 数据采集 | requests + BeautifulSoup4 / Scrapy |
| 数据处理 | pandas + numpy |
| 机器学习 | scikit-learn(线性回归、随机森林等) |
| 可视化 | ECharts 前端图表 + Django JSON 接口 |
| 词云图 | wordcloud + jieba 中文分词 |
| 数据库 | SQLite 或 MySQL,按项目需要切换 |
| 启动方式 | 命令行启动 Django 开发服务器 |
| 接口能力 | REST API 返回 JSON,可支持销量预测、统计数据查询 |
| 批量任务 | Celery + Redis 可实现定时采集和批量预测 |
| 适合场景 | 电商数据分析、销量预测、毕业设计、内部数据看板 |
需要说明的是,这个项目的硬件门槛很低,普通电脑就能跑。预测模型用的不是大模型,而是传统机器学习算法,训练数据量在几千到几万条时,CPU 即可完成训练。主要内存开销集中在 pandas 读取 DataFrame 和词云图生成阶段。
2. 适用场景与使用边界
这类项目适合以下几类读者:
- Python 学习者:想在一个项目里同时接触爬虫、Web 开发、数据处理和机器学习。
- 数据分析岗位候选人:需要一个能展示完整数据流水线的作品,而不是只做 Kaggle 练习赛。
- 毕设选题者:电商销量预测是经典选题,功能边界清晰,容易出成果。
- 中小团队开发者:需要快速搭建一个内部数据看板,把运营数据变成可查询的 Web 页面。
项目能解决的问题包括:
- 商品销量数据的自动采集与入库。
- 销量趋势的统计和可视化。
- 基于历史数据训练销量预测模型。
- 评论关键词的提取与词云展示。
- 通过 Web 页面和 API 对外输出分析结果。
但要明确使用边界。电商数据爬虫涉及版权、隐私和平台规则,只允许采集公开数据,并且要遵守目标网站的 robots.txt 协议。不要爬取用户个人信息、订单隐私数据,也不要对目标站点造成压力。采集频率要合理控制。个人学习和内部研究场景下,建议优先使用公开数据集或自己模拟生成数据,避开法律风险。涉及商业数据时,必须先确认数据来源的授权情况。
3. 技术架构与模块设计
整套系统的模块职责如下:
爬虫采集模块 -> 数据清洗模块 -> 数据库存储 -> Django Web 模块 -> 前端可视化 |-> 机器学习预测模块 -> 输出预测结果 |-> 词云图生成模块 -> 输出图片和词频Django 在这里的角色是 Web 框架和业务调度中心。爬虫采集的数据通过 ORM 写入数据库,Django 的 View 对前端提供 JSON 接口,机器学习模型在训练完成后被加载到内存中,通过独立接口对外提供预测服务。
在 Django 项目中,建议划分以下 App:
| App 名称 | 职责 |
|---|---|
crawler | 爬虫采集逻辑、数据清洗、入库 |
analysis | 数据统计、销量预测、词云生成 |
dashboard | 页面渲染和图表接口 |
api | REST API 接口,对外输出数据 |
需要注意的是,Django 的 MTV 模式中,View 负责业务逻辑,Template 负责页面展示。在数据分析和预测场景下,建议把耗时操作放到异步任务中执行,避免请求阻塞。下一节先解决环境问题。
4. 环境准备与前置条件
建议使用 Python 3.9 到 3.11 之间的版本。Python 3.12 部分依赖包可能还没有完全适配,遇到问题会更麻烦。使用虚拟环境隔离依赖。
# 创建虚拟环境 python -m venv venv # 激活虚拟环境 # Windows venv\Scripts\activate # Linux / macOS source venv/bin/activate安装核心依赖:
pip install django djangorestframework requests beautifulsoup4 pandas numpy scikit-learn wordcloud jieba celery redis如果使用 MySQL 作为数据库,还需要安装连接驱动:
pip install pymysql然后创建 Django 项目和应用:
django-admin startproject ecommerce_analysis cd ecommerce_analysis python manage.py startapp crawler python manage.py startapp analysis python manage.py startapp dashboard python manage.py startapp api创建完成后,在settings.py中注册 App:
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'rest_framework', 'crawler', 'analysis', 'dashboard', 'api', ]数据库默认使用 SQLite,适合本地开发和测试。如果要切换到 MySQL,修改DATABASES配置:
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'ecommerce_db', 'USER': 'root', 'PASSWORD': 'your_password', 'HOST': '127.0.0.1', 'PORT': '3306', } }5. 数据模型设计
数据模型是整个项目的地基。设计不好,后面的分析和预测都会很别扭。核心模型包括商品、销量记录、评论和预测结果。
在crawler/models.py中定义商品和销量记录:
from django.db import models class Product(models.Model): product_id = models.CharField(max_length=64, unique=True, verbose_name='商品ID') title = models.CharField(max_length=255, verbose_name='商品标题') category = models.CharField(max_length=64, verbose_name='类目') price = models.FloatField(verbose_name='价格') shop_name = models.CharField(max_length=128, verbose_name='店铺名称') created_at = models.DateTimeField(auto_now_add=True) class Meta: db_table = 'product' verbose_name = '商品' class SalesRecord(models.Model): product = models.ForeignKey(Product, on_delete=models.CASCADE, verbose_name='商品') date = models.DateField(verbose_name='日期') sales_volume = models.IntegerField(verbose_name='销量') sales_amount = models.FloatField(verbose_name='销售额') updated_at = models.DateTimeField(auto_now=True) class Meta: db_table = 'sales_record' verbose_name = '销量记录' unique_together = ('product', 'date')评论表用于词云分析:
class Comment(models.Model): product = models.ForeignKey(Product, on_delete=models.CASCADE, verbose_name='商品') content = models.TextField(verbose_name='评论内容') rating = models.IntegerField(verbose_name='评分') created_at = models.DateTimeField(auto_now_add=True) class Meta: db_table = 'comment' verbose_name = '商品评论'生成迁移并建表:
python manage.py makemigrations python manage.py migrate需要注意,如果使用 SQLite,整型字段的自动增长不是问题,但并发写入能力较弱。如果爬虫采集频率高,建议使用 MySQL。数据量不大时 SQLite 也够用,关键在于字段设计是否满足后续分析需求。
6. 爬虫数据采集模块
爬虫模块负责把电商数据变成结构化数据。这里只讨论“公开商品列表和公开评价”这一类数据,并且要控制采集频率。
最简单的方式是 requests 加 BeautifulSoup。以下是请求公开商品列表页的示例:
import requests from bs4 import BeautifulSoup def fetch_product_list(url): headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' } response = requests.get(url, headers=headers, timeout=10) response.encoding = 'utf-8' soup = BeautifulSoup(response.text, 'html.parser') product_nodes = soup.select('.product-item') # 选择器需要按实际页面调整 products = [] for node in product_nodes: title = node.select_one('.title').text.strip() price = node.select_one('.price').text.strip().replace('¥', '') products.append({'title': title, 'price': float(price)}) return products注意,不同的电商页面 DOM 结构完全不同,选择器必须根据实际页面调整。更稳妥的做法是优先使用平台开放的官方 API,如果没有,再考虑爬虫。爬虫采集必须遵守robots.txt,并且请求间隔要合理,不要让目标服务器压力过大。
采集完成后需要清洗。清洗步骤包括:
- 去除空值和重复记录。
- 统一日期格式。
- 将价格、销量等字符串转为数值类型。
- 删除明显异常的数据,例如价格小于 0 的记录。
import pandas as pd def clean_sales_data(raw_data): df = pd.DataFrame(raw_data) df.dropna(inplace=True) df.drop_duplicates(inplace=True) df['date'] = pd.to_datetime(df['date']) df['sales_volume'] = pd.to_numeric(df['sales_volume'], errors='coerce') df['sales_amount'] = pd.to_numeric(df['sales_amount'], errors='coerce') df = df[df['sales_volume'] >= 0] return df数据清洗的结果保存到数据库:
def save_products(products): for item in products: Product.objects.update_or_create( product_id=item['product_id'], defaults={ 'title': item['title'], 'category': item['category'], 'price': item['price'], 'shop_name': item['shop_name'], } )使用update_or_create可以避免重复数据,同时保留更新能力。
7. 数据分析与销量预测模块
数据进入数据库后,先做统计分析,再做销量预测。
7.1 销量趋势统计
通过 Django ORM 聚合查询,可以快速统计每日销量:
from django.db.models import Sum from crawler.models import SalesRecord def get_daily_sales(product_id): records = ( SalesRecord.objects .filter(product_id=product_id) .values('date') .annotate(total_volume=Sum('sales_volume')) .order_by('date') ) return list(records)这一步返回的数据可以直接用于前端 ECharts 折线图。统计结果转成 JSON:
import json from django.http import JsonResponse def daily_sales_api(request, product_id): records = get_daily_sales(product_id) dates = [str(item['date']) for item in records] volumes = [item['total_volume'] for item in records] return JsonResponse({'dates': dates, 'volumes': volumes})7.2 特征工程
做销量预测前,要先构造特征。常用特征包括:
- 历史销量序列的滞后值(lag-1, lag-7, lag-30)。
- 星期几、是否周末、是否节假日。
- 商品价格。
- 类目。
- 滚动平均和滚动标准差。
def build_features(df): df_feature = df.copy() df_feature.sort_values('date', inplace=True) df_feature['lag_1'] = df_feature['sales_volume'].shift(1) df_feature['lag_7'] = df_feature['sales_volume'].shift(7) df_feature['rolling_mean_7'] = df_feature['sales_volume'].rolling(window=7).mean() df_feature['day_of_week'] = df_feature['date'].dt.dayofweek df_feature['is_weekend'] = df_feature['day_of_week'].apply(lambda x: 1 if x >= 5 else 0) df_feature.dropna(inplace=True) return df_feature特征是机器学习模型的上限,模型只是逼近这个上限。数据量小的时候,滞后特征和星期特征是效果最明显的两个因素。
7.3 训练销量预测模型
这里用随机森林回归作为示例,因为它在小数据集上不容易过拟合,且能输出特征重要性,方便后面解释。
from sklearn.ensemble import RandomForestRegressor from sklearn.model_selection import train_test_split from sklearn.metrics import mean_absolute_error, r2_score def train_sales_model(df): feature_cols = ['lag_1', 'lag_7', 'rolling_mean_7', 'day_of_week', 'is_weekend'] X = df[feature_cols] y = df['sales_volume'] X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, shuffle=False ) model = RandomForestRegressor( n_estimators=200, max_depth=8, random_state=42 ) model.fit(X_train, y_train) y_pred = model.predict(X_test) print("MAE:", mean_absolute_error(y_test, y_pred)) print("R2:", r2_score(y_test, y_pred)) return model训练完成后,模型需要保存到磁盘,避免每次请求都重新训练。
import joblib joblib.dump(model, 'sales_model.pkl')加载模型:
model = joblib.load('sales_model.pkl')预测下一天销量:
import numpy as np def predict_next_day(model, last_record): features = np.array([[ last_record['lag_1'], last_record['lag_7'], last_record['rolling_mean_7'], last_record['day_of_week'], last_record['is_weekend'], ]]) return model.predict(features)[0]需要特别强调:模型预测结果只能作为参考,不能作为经营决策的唯一依据。销量受促销、天气、平台流量波动、突发事件等多重因素影响,传统机器学习模型只能学习历史规律,无法感知未来变化。
8. 可视化与词云图展示
8.1 Django 集成 ECharts
ECharts 是一个纯前端图表库,通过 CDN 引入即可,不需要额外安装 Python 包。
在dashboard/templates/dashboard/index.html中引入 ECharts 并请求 Django 接口:
<!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8"> <title>电商数据分析平台</title> <script src="https://cdn.jsdelivr.net/npm/echarts@5/dist/echarts.min.js"></script> </head> <body> <div id="chart" style="width: 100%; height: 400px;"></div> <script> fetch('/api/daily_sales/1/') .then(response => response.json()) .then(data => { const chart = echarts.init(document.getElementById('chart')); chart.setOption({ tooltip: {}, xAxis: {data: data.dates}, yAxis: {}, series: [{ type: 'line', data: data.volumes, smooth: true }] }); }); </script> </body> </html>这种前后端分离方式的好处是,接口可以做复用。页面只是消费者之一,移动端或者其他系统也可以调用同一套 API。
8.2 词云图生成
词云图用于展示商品评论中的高频关键词。需要先对评论文本进行中文分词,再生成词云图片。
import jieba import wordcloud def generate_wordcloud(product_id): comments = Comment.objects.filter(product_id=product_id).values_list('content', flat=True) text = ' '.join(list(comments)) words = ' '.join(jieba.cut(text)) wc = wordcloud.WordCloud( font_path='msyh.ttc', # Windows 建议指定中文字体路径 width=800, height=600, background_color='white' ) wc.generate(words) wc.to_file('wordcloud.png') return 'wordcloud.png'中文词云最容易踩的坑是字体问题。默认字体不支持中文,会显示成方框。Windows 下可以指定C:\Windows\Fonts\msyh.ttc,Linux 下需要安装中文字体,比如wqy-microhei。
生成词云图后,接口可以直接把图片路径返回给前端:
from django.http import JsonResponse def wordcloud_api(request, product_id): image_path = generate_wordcloud(product_id) return JsonResponse({'image_url': '/static/' + image_path})更好的方式是把图片保存到媒体目录,然后通过 Django 静态文件服务对外提供。
9. 接口 API 与批量任务
9.1 REST API 设计
使用 Django REST Framework 可以快速把分析结果封装成标准接口。
安装并注册后,在api/views.py中定义接口:
from rest_framework.decorators import api_view from rest_framework.response import Response @api_view(['GET']) def sales_summary(request, product_id): records = SalesRecord.objects.filter(product_id=product_id) total_volume = records.aggregate(total=Sum('sales_volume'))['total'] total_amount = records.aggregate(total=Sum('sales_amount'))['total'] return Response({ 'product_id': product_id, 'total_volume': total_volume, 'total_amount': total_amount, })在api/urls.py中注册路由:
from django.urls import path from . import views urlpatterns = [ path('summary/<str:product_id>/', views.sales_summary), ]启动服务后,可以直接用浏览器访问:
http://127.0.0.1:8000/api/summary/1001/返回结果:
{ "product_id": "1001", "total_volume": 12345, "total_amount": 456789.0 }9.2 批量任务设计
如果采集数据量大,或者需要定时训练模型,建议引入 Celery。Celery 是一个分布式任务队列,配合 Redis 作为消息代理,可以把爬虫采集、模型训练这类耗时任务放到后台执行。
settings.py中配置 Celery:
CELERY_BROKER_URL = 'redis://127.0.0.1:6379/0' CELERY_RESULT_BACKEND = 'redis://127.0.0.1:6379/1'定义异步任务:
from celery import shared_task @shared_task def run_crawler_task(keyword): # 在这里调用爬虫逻辑 products = fetch_product_list(keyword) save_products(products) return len(products)调用任务:
run_crawler_task.delay('手机')Celery Worker 启动命令:
celery -A ecommerce_analysis worker -l info批量预测的场景同样可以做成任务队列:输入商品 ID 列表,后台逐个加载模型并预测,最后把结果写入预测结果表,前端接口直接读取最终结果。
10. 资源占用与性能观察
整个项目的资源消耗主要集中在三块:
- 爬虫采集阶段:内存占用取决于同时请求的并发数,同步 requests 方式下占用不高。
- pandas 数据清洗阶段:DataFrame 会一次性把数据加载到内存,几万条记录大概占用几十到几百 MB,数据量超过百万条时建议改用分块读取。
- 模型训练阶段:随机森林训练速度较快,几万条训练数据在普通 CPU 上几十秒内完成。如果使用 XGBoost,训练速度会更快,但需要单独安装。
观察项目运行状态的方法:
# 查看 Django 进程内存占用 ps aux | grep python # 查看系统资源占用 top -p <pid>如果发现接口响应慢,优先检查数据库查询次数。Django ORM 最常出现的问题是 N+1 查询,比如循环里查数据库。使用select_related和prefetch_related可以大幅减少查询次数。
# 错误示例:循环中查询数据库 for record in sales_records: print(record.product.title) # 正确示例:提前关联查询 from django.db.models import Prefetch sales_records = SalesRecord.objects.select_related('product').all()词云生成是 CPU 密集操作,jieba 分词在文本量大时可能耗时较长。如果部署在服务器上,建议把词云生成放到 Celery 异步任务中,前端先显示“生成中”,任务完成后刷新图片。
11. 常见问题与排查方法
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 启动 Django 报 ModuleNotFoundError | 依赖包未安装或虚拟环境未激活 | 检查是否有 venv,查看 pip list | 激活虚拟环境后重新安装依赖 |
| 爬虫获取不到数据 | 页面结构变化、请求头被识别 | 打印响应文本,检查 HTTP 状态码 | 更新页面选择器,调整 User-Agent |
| 中文词云显示方框 | 缺少中文字体 | 查看字体路径是否存在 | 指定系统可用的中文字体文件 |
| 销量预测结果偏差很大 | 训练数据量不足、特征构造不合理 | 查看训练集和测试集的 MAE | 增加历史数据,尝试滞后特征和节假日特征 |
| 数据库表锁死 | 爬虫写入频繁且数据库为 SQLite | 观察错误日志 | 切换 MySQL,或降低写入频率 |
| 接口返回 500 | 代码异常、数据库连接失败 | 查看 Django 日志 | 修复异常,检查数据库连接配置 |
| Celery 任务不执行 | Redis 未启动或队列名称不匹配 | 检查 Redis 是否运行,查看 worker 日志 | 启动 Redis,重启 worker |
遇到问题时的第一原则:先看日志。Django 开发服务器的终端输出、Celery worker 日志、浏览器开发者工具的 Network 面板,这三处信息足以定位大部分问题。
12. 最佳实践与使用建议
做一个完整的电商数据分析平台,不只是把代码跑通,还要考虑工程化。以下是几个建议。
第一,目录结构要清晰。爬虫代码、数据处理代码、模型训练代码、Web 接口代码分开存放,不要全部堆在views.py里。可以按照crawler/、analysis/、dashboard/、api/划分。
第二,配置使用独立文件。数据库连接、Redis 地址、爬虫请求头、模型路径等常量统一放到settings.py或独立的配置文件中,不要写死在业务代码里。
第三,爬虫要加间隔。每请求一次页面,至少间隔 1 到 2 秒。这既是对目标站点的保护,也是降低被封风险的有效手段。
import time import random def fetch_with_interval(url): time.sleep(random.uniform(1, 2)) response = requests.get(url, headers=headers, timeout=10) return response第四,模型训练要可复现。固定随机种子、固定训练集和测试集划分比例、保存模型版本。这样即使数据更新后重新训练,也能知道模型的相对变化。
random_state = 42 X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=random_state, shuffle=False )第五,接口要做访问控制。如果系统部署到服务器,Django 的ALLOWED_HOSTS要配置成实际域名或 IP。API 如果只限内部使用,可以通过rest_framework.authentication加 Token 认证。
第六,数据合规是底线。采集数据前确认数据来源的公开性,不采集用户隐私数据,不抓取非公开接口,不把采集到的数据用于商业用途。对爬虫代码加适度频率限制,避免给目标服务器带来压力。
13. 总结与下一步
整套系统最值得尝试的点,是把 Django 和机器学习组合起来,让数据分析和预测结果通过 Web 页面跑成一个可交互的工具。对于学习者来说,这个项目比单独跑一个 notebook 更有工程感,也更接近实际工作场景。
第一次跑通时,建议按这个顺序验证:
- 启动 Django 服务,访问首页。
- 用公开数据集或模拟数据写入商品和销量记录表。
- 在页面中查看销量趋势图和词云图。
- 训练一个最小销量预测模型,查看评估指标。
- 调用 API 接口,确认 JSON 输出符合预期。
最容易踩的坑集中在三处:爬虫选择器和反爬机制、中文词云的字体问题、预测模型训练集和测试集设置不当导致的过拟合。
后续可以扩展的方向很明确:把模型换成 XGBoost 或 LightGBM,对比效果;引入时间序列模型如 Prophet 做更专业的销量预测;加入用户行为数据,做更精细的 RFM 客户分层;或者把可视化页面升级成可筛选、可下钻的交互式仪表盘。
如果只是单纯想把销量预测从“能跑”变成“实用”,建议先把功夫花在特征上:引入节假日、促销、天气等外部特征,往往比更换模型提升更明显。整个项目在普通电脑上就能开发测试,值得动手搭一遍。建议收藏备用,后续再往深做时,框架和代码不需要大的改动。