蔚来汽车销售数据可视化分析
蔚来汽车销售数据可视化分析平台
项目概述
本文详细介绍一个基于 Django 开发的蔚来汽车销售数据可视化分析平台。该项目从零开始构建了一套完整的数据采集、数据存储、数据展示、数据分析和个性化推荐系统,帮助用户深入了解蔚来汽车的市场表现。
项目在线演示地址:小红书
项目 GitHub:主页Github
目录结构
Car_Sales/
├── Car_Sales/ # Django 项目配置目录
│ ├── settings.py # 项目配置文件
│ ├── urls.py # 全局路由配置
│ └── db_config.py # 数据库配置
├── sales/ # 核心业务应用
│ ├── management/commands/ # Django 自定义管理命令
│ │ └── import_nio_sales.py # 历史数据导入命令
│ ├── migrations/ # 数据库迁移文件
│ ├── templates/sales/ # 页面模板(HTML)
│ ├── models.py # 数据模型(ORM)
│ ├── services.py # 业务逻辑层
│ ├── views.py # 视图函数
│ ├── urls.py # 路由配置
│ └── forms.py # 表单类
├── static/ # 静态资源文件
│ ├── css/ # 自定义样式
│ ├── js/ # JavaScript 文件
│ │ └── dashboard/ # 可视化图表脚本
│ └── vendor/ # 第三方库(ECharts、Tailwind、FontAwesome)
├── templates/ # 全局模板
├── scraw.py # 数据爬虫脚本
└── manage.py # Django 管理入口
一、技术栈
1.1 后端技术
| 技术 | 说明 |
|---|---|
| Python 3 | 主要编程语言 |
| Django 5.2.4 | Web 框架 |
| MySQL | 关系型数据库 |
| Django ORM | 数据库操作抽象层 |
| Django Auth | 用户认证系统 |
1.2 前端技术
| 技术 | 说明 |
|---|---|
| HTML5 + CSS3 | 页面结构与样式 |
| Tailwind CSS | 原子化 CSS 框架(本地 CDN 加载) |
| ECharts | 数据可视化图表库(本地静态资源) |
| Font Awesome | 图标库(本地静态资源) |
| Vanilla JavaScript | 前端交互逻辑 |
1.3 数据采集
| 技术 | 说明 |
|---|---|
| requests | HTTP 请求库 |
| PyMySQL | MySQL 驱动 |
| JSON | 数据解析 |
二、数据模型设计
项目的核心数据模型定义在 [models.py](file:///d:/PycharmProjects/Car_Sales/sales/models.py) 中。
2.1 车辆销量数据(VehicleData)
class VehicleData(models.Model):
brand_name = models.CharField("品牌", max_length=50, default="蔚来")
series_id = models.IntegerField("车系ID", db_index=True)
series_name = models.CharField("车系名称", max_length=100)
month = models.IntegerField("月份", db_index=True)
rank_no = models.IntegerField("排名", default=0)
sales_count = models.IntegerField("销量", default=0)
min_price = models.FloatField("最低价", null=True, blank=True)
max_price = models.FloatField("最高价", null=True, blank=True)
price_text = models.CharField("价格区间", max_length=50, blank=True)
dealer_price = models.CharField("经销商价格", max_length=50, blank=True)
image_url = models.URLField("图片", blank=True)
description = models.TextField("简介", blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
constraints = [
models.UniqueConstraint(
fields=["series_id", "month"], name="unique_vehicle_month"
)
]
设计亮点:
- 使用
UniqueConstraint确保同一车系同一月份只有一条全国销量记录,避免数据重复 db_index=True对series_id和month字段建立索引,提升查询性能- 通过
likes和favorites的反向关系,快速获取点赞数和收藏数
2.2 用户模型(CustomUser)
class CustomUser(AbstractUser):
phone = models.CharField("手机号", max_length=20, blank=True)
avatar = models.URLField("头像地址", blank=True)
bio = models.TextField("个人简介", blank=True)
继承 Django 内置的 AbstractUser,扩展了手机号、头像和个人简介字段。
2.3 社交互动模型
class Favorite(models.Model):
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
vehicle = models.ForeignKey(VehicleData, on_delete=models.CASCADE)
class LikeRecord(models.Model):
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
vehicle = models.ForeignKey(VehicleData, on_delete=models.CASCADE)
class Comment(models.Model):
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
vehicle = models.ForeignKey(VehicleData, on_delete=models.CASCADE)
content = models.TextField("评论内容")
class BrowseHistory(models.Model):
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
vehicle = models.ForeignKey(VehicleData, on_delete=models.CASCADE)
每个社交模型都使用 UniqueConstraint 确保用户对同一车型只有一条记录。
三、数据采集爬虫
数据爬虫脚本 [scraw.py](file:///d:/PycharmProjects/Car_Sales/scraw.py) 从懂车帝网站抓取汽车销量数据。
3.1 爬虫配置
RANK_DATA_URL = "https://www.dongchedi.com/motor/pc/car/rank_data"
TARGET_BRANDS = [
{"brand_id": 112, "brand_name": "蔚来"},
{"brand_id": 202, "brand_name": "理想"},
{"brand_id": 195, "brand_name": "小鹏"},
{"brand_id": 535, "brand_name": "小米汽车"},
{"brand_id": 426, "brand_name": "极氪"},
]
支持抓取 5 个新能源汽车品牌的销量数据:蔚来、理想、小鹏、小米汽车、极氪。
3.2 核心爬取逻辑
def fetch_brand_month_sales(session, month, brand):
payload = request_json(
session,
RANK_DATA_URL,
params=build_rank_params(month, brand["brand_id"]),
)
data = payload.get("data") or {}
return {
"sales_list": data.get("list") or [],
"latest_month": latest_month,
}
构建请求参数:
def build_rank_params(month, brand_id):
return {
"aid": "1839",
"app_name": "auto_web_pc",
"city_name": "",
"count": "100",
"offset": "0",
"month": str(month) if month else "",
"brand_id": str(brand_id),
"rank_data_type": "11",
# ...
}
3.3 数据入库
def replace_month_sales(conn, month, sales_list):
rows = transform_sales_rows(month, sales_list)
with conn.cursor() as cursor:
cursor.execute("DELETE FROM sales_vehicledata WHERE month = %s", (month,))
cursor.executemany("""
INSERT INTO sales_vehicledata (...) VALUES (...)
ON DUPLICATE KEY UPDATE ...
""", rows)
conn.commit()
return len(rows)
采用 DELETE + INSERT 的方式实现数据的完整替换,确保数据是最新的。使用 ON DUPLICATE KEY UPDATE 处理重复键冲突。
3.4 运行爬虫
python scraw.py
程序会自动:
- 创建数据库(如果不存在)
- 计算目标月份范围(从 2023 年 1 月到上月)
- 按品牌、按月份依次抓取数据
- 将数据批量写入 MySQL 数据库
四、业务服务层
[services.py](file:///d:/PycharmProjects/Car_Sales/sales/services.py) 包含了项目的核心业务逻辑,包括数据筛选、可视化数据处理、推荐系统和预测系统。
4.1 数据筛选
def get_filtered_vehicle_qs(filters=None, include_all_brands=False):
qs = get_vehicle_base_qs(include_all_brands=include_all_brands)
month = filters.get("month")
price_range = filters.get("price_range")
series_keyword = filters.get("series_keyword")
if month:
qs = qs.filter(month=month)
if series_keyword:
qs = qs.filter(series_name__icontains=series_keyword)
if price_range == "lt20":
qs = qs.filter(min_price__lt=20)
elif price_range == "20_30":
qs = qs.filter(min_price__gte=20, min_price__lt=30)
# ... 其他价格区间
return qs
支持多维度筛选:月份、价格区间、车型关键字。
4.2 大盘总览数据
def get_overview_data(qs=None):
rows = get_national_vehicle_rows(source_qs=qs)
month_counter = Counter()
series_counter = Counter()
for row in rows:
month_counter[row["month"]] += row["sales_count"]
series_counter[row["series_name"]] += row["sales_count"]
return {
"summary": summary,
"month_chart": month_chart, # 月度销量趋势
"hot_series": hot_series, # 热门车型 Top10
"series_compare": series_compare,# 累计销量车型对比
}
4.3 竞品分析数据
def get_competitor_data(qs=None):
brand_sales = Counter()
brand_prices = {}
for row in rows:
brand_name = row["brand_name"]
brand_sales[brand_name] += row["sales_count"]
if row["min_price"] is not None:
brand_prices[brand_name].append(row["min_price"])
for brand_name, total_sales in brand_sales.items():
avg_price = sum(prices) / len(prices)
data.append({"brand_name": brand_name, "total_sales": total_sales, "avg_price": avg_price})
return sorted(data, key=lambda item: item["total_sales"], reverse=True)
4.4 价格区间分布
def get_price_data(qs=None):
buckets = {"20万以下": 0, "20-30万": 0, "30-40万": 0, "40万以上": 0}
month_prices = {}
for item in rows:
price = item["min_price"]
if price is None:
continue
if price < 20:
buckets["20万以下"] += 1
elif price < 30:
buckets["20-30万"] += 1
# ...
avg_price_by_month = [
{"month": month, "avg_price": sum(prices) / len(prices)}
for month, prices in sorted(month_prices.items())
]
return {"bucket_data": [...], "avg_price_by_month": [...]}
五、协同过滤推荐系统
推荐系统基于协同过滤算法,利用用户的收藏、点赞、评论、浏览记录构建隐式评分矩阵。
5.1 隐式评分构建
def _build_user_series_matrix():
matrix = defaultdict(lambda: defaultdict(float))
# 收藏 +4.0 分
for user_id, series_id in Favorite.objects.filter(...).values_list("user_id", "vehicle__series_id"):
matrix[user_id][series_id] += 4.0
# 点赞 +3.0 分
for user_id, series_id in LikeRecord.objects.filter(...).values_list("user_id", "vehicle__series_id"):
matrix[user_id][series_id] += 3.0
# 浏览 +1.0 分
for user_id, series_id in BrowseHistory.objects.filter(...).values_list("user_id", "vehicle__series_id"):
matrix[user_id][series_id] += 1.0
# 评论 +2.0 分 * 评论次数
comment_stats = Comment.objects.filter(...).values("user_id", "vehicle__series_id").annotate(total=Count("id"))
for item in comment_stats:
matrix[item["user_id"]][item["vehicle__series_id"]] += item["total"] * 2.0
return matrix
不同行为赋予不同权重:收藏 > 点赞 > 评论 > 浏览。
5.2 相似度计算
def _cosine_similarity(source_vector, target_vector):
overlap = set(source_vector.keys()) & set(target_vector.keys())
if not overlap:
return 0.0
numerator = sum(source_vector[key] * target_vector[key] for key in overlap)
source_norm = sqrt(sum(value * value for value in source_vector.values()))
target_norm = sqrt(sum(value * value for value in target_vector.values()))
return numerator / (source_norm * target_norm)
使用余弦相似度计算用户之间的相似程度。
5.3 推荐生成
def get_recommendations(user, limit=6):
if not user.is_authenticated:
# 未登录用户,返回热门车型
popular_scores = _get_popular_series_scores()
return [row_map[series_id] for series_id, _ in popular_scores.most_common(limit)]
# 获取当前用户的评分向量
target_scores = dict(matrix.get(user.id, {}))
consumed_series_ids = set(target_scores.keys())
if not target_scores:
# 用户无行为记录,返回热门
return popular_recommendations
# 找到相似用户,推荐相似用户喜欢但当前用户未交互的车型
candidate_scores = Counter()
for other_user_id, other_scores in matrix.items():
similarity = _cosine_similarity(target_scores, other_scores)
if similarity <= 0:
continue
for series_id, score in other_scores.items():
if series_id in consumed_series_ids:
continue
candidate_scores[series_id] += similarity * score
# 如果推荐数量不足,补充热门车型
if len(ranked_series_ids) < limit:
popular_scores = _get_popular_series_scores(exclude_series_ids=...)
ranked_series_ids.extend([...])
return [row_map[series_id] for series_id in ranked_series_ids[:limit]]
六、销量预测系统
预测系统集成三种预测算法,预测未来 1-2 个月的销量。
6.1 简单移动平均
def _moving_average_forecast(sales_values, steps=2, window_size=3):
series = list(sales_values)
forecast = []
for _ in range(steps):
window = series[-window_size:] or series
predicted = sum(window) / len(window)
forecast.append(predicted)
series.append(predicted)
return forecast
取最近 3 个月的均值作为预测值。
6.2 加权移动平均
def _weighted_moving_average_forecast(sales_values, steps=2, window_size=3):
series = list(sales_values)
forecast = []
for _ in range(steps):
window = series[-window_size:] or series
weights = list(range(1, len(window) + 1))
denominator = sum(weights) or 1
predicted = sum(value * weight for value, weight in zip(window, weights)) / denominator
forecast.append(predicted)
series.append(predicted)
return forecast
越近期的月份权重越高,例如 window=[a,b,c],权重=[1,2,3],预测值 = (a×1 + b×2 + c×3) / 6。
6.3 线性趋势回归
def _linear_trend_forecast(sales_values, steps=2):
count = len(sales_values)
x_values = list(range(1, count + 1))
y_values = list(sales_values)
x_mean = sum(x_values) / count
y_mean = sum(y_values) / count
numerator = sum((x - x_mean) * (y - y_mean) for x, y in zip(x_values, y_values))
denominator = sum((x - x_mean) ** 2 for x in x_values) or 1
slope = numerator / denominator
intercept = y_mean - slope * x_mean
return [_safe_round(intercept + slope * (count + step)) for step in range(1, steps + 1)]
使用最小二乘法拟合线性趋势,预测未来销量。
七、前端可视化
7.1 ECharts 集成
项目使用本地化的 ECharts([echarts.min.js](file:///d:/PycharmProjects/Car_Sales/static/vendor/echarts/echarts.min.js)),无需 CDN。
7.2 图表初始化
// overview.js
const chart1 = echarts.init(document.getElementById('chart1'), 'dark');
window.NioApp.bindChartResize([chart1, chart2, chart3]);
使用深色主题 'dark',适配网站深色背景风格。
7.3 图表配置
chart1.setOption({
backgroundColor: 'transparent',
tooltip: window.NioApp.nioTooltip(),
grid: window.NioApp.nioGrid(),
xAxis: {
type: 'category',
data: monthData.map(item => item.month),
},
yAxis: {
type: 'value',
},
series: [{
type: 'line',
smooth: true,
data: monthData.map(item => item.total_sales),
lineStyle: { width: 3, color: '#3b82f6' },
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: 'rgba(59,130,246,0.45)' },
{ offset: 1, color: 'rgba(59,130,246,0.02)' }
])
}
}]
});
7.4 预测页面图表
预测页面展示历史销量和多种算法的预测结果:
// 历史销量(实线)
{
name: '历史销量',
type: 'line',
smooth: true,
data: historySales,
lineStyle: { width: 4, color: '#3b82f6' },
areaStyle: { ... }
}
// 预测销量(虚线)
{
name: '简单移动平均',
type: 'line',
smooth: true,
lineStyle: { width: 3, type: 'dashed', color: '#34d399' },
data: [...] // 前半部分为 null,后半部分为预测值
}
八、视图与路由
8.1 URL 路由
# sales/urls.py
urlpatterns = [
path("", views.home_view, name="home"),
path("dashboard/overview/", views.dashboard_overview_view, name="dashboard_overview"),
path("dashboard/overview/api/", views.dashboard_overview_api_view, name="dashboard_overview_api"),
path("dashboard/competitor/", views.dashboard_competitor_view, name="dashboard_competitor"),
path("dashboard/user/", views.dashboard_user_view, name="dashboard_user"),
path("dashboard/region/", views.dashboard_region_view, name="dashboard_region"),
path("dashboard/price/", views.dashboard_price_view, name="dashboard_price"),
path("recommend/", views.recommend_view, name="recommend"),
path("predict/<str:series_name>/", views.predict_view, name="predict"),
# ...
]
8.2 仪表盘视图
def dashboard_overview_view(request):
filters = get_dashboard_filters(request)
qs = get_filtered_vehicle_qs(filters)
data = get_overview_data(qs)
context = {
"title": "大盘总览",
"summary": data["summary"],
"overview_initial_data": data,
}
context.update(get_dashboard_filter_context(filters))
return render(request, "sales/dashboard_overview.html", context)
8.3 API 接口
def dashboard_overview_api_view(request):
filters = get_dashboard_filters(request)
qs = get_filtered_vehicle_qs(filters)
data = get_overview_data(qs)
return JsonResponse(data)
前端页面加载时使用初始数据(SSR),筛选条件变化时通过 API 异步获取新数据(CSR)。
九、项目功能模块
9.1 首页功能
- 展示销量总览数据
- 按车型关键字筛选
- 按月份筛选
- 按价格区间筛选
- 支持销量、价格、车型名称排序
- 分页展示
- 个性化推荐列表
9.2 数据可视化
| 页面 | 功能 |
|---|---|
| 大盘总览 | 月度销量趋势图、热门车型柱状图、累计销量车型排行 |
| 竞品分析 | 品牌销量与均价双轴图、品牌销量占比饼图 |
| 用户画像 | 收藏偏好、点赞偏好、浏览偏好分析 |
| 车型排行 | 全国车型销量排行榜 |
| 价格分析 | 价格区间分布柱状图、月均最低价趋势 |
9.3 用户交互
- 用户注册 / 登录 / 登出
- 个人资料管理
- 收藏车型
- 点赞车型
- 评论车型
- 浏览历史记录
- 密码修改
- 账号注销
9.4 个性化推荐
- 已登录用户:基于协同过滤的个性化推荐
- 未登录用户:热门车型推荐
- 行为不足时:自动回退为热门推荐
9.5 销量预测
- 支持选择具体车型
- 三种预测算法:简单移动平均、加权移动平均、线性趋势回归
- 预测未来 1-2 个月销量
- 预测结果对比图表
十、模板页面
10.1 大盘总览页面
<!-- dashboard_overview.html -->
{% extends "base.html" %}
{% block content %}
<!-- 汇总卡片 -->
<section class="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
<div class="panel p-5">
<div class="text-sm text-slate-400">总记录</div>
<div data-summary-card class="mt-3 text-3xl font-bold text-white">{{ summary.total_records }}</div>
</div>
<!-- ... 其他卡片 -->
</section>
<!-- 月度销量趋势图 -->
<section class="panel p-5">
<div id="chart1" class="chart-box-lg"></div>
</section>
<!-- 热门车型图 -->
<section class="panel p-5">
<div id="chart2" class="chart-box-lg"></div>
</section>
{% endblock %}
10.2 预测页面
<!-- predict.html -->
<section class="panel p-5">
<h3>历史销量与预测走势</h3>
<div id="trend-chart" class="chart-box-lg"></div>
</section>
<section class="panel p-5">
<h3>预测结果对比</h3>
<div id="compare-chart" class="chart-box-lg"></div>
</section>
<!-- 传递给前端的数据 -->
{{ predict_data|json_script:"predict-data" }}
十一、项目部署
11.1 环境要求
- Python 3.10+
- MySQL 5.7+ 或 MySQL 8.0+
11.2 安装依赖
pip install django pymysql requests
11.3 配置数据库
编辑 Car_Sales/db_config.py:
def get_mysql_config():
return {
"HOST": "localhost",
"PORT": 3306,
"USER": "your_username",
"PASSWORD": "your_password",
"NAME": "car_sales",
"CHARSET": "utf8mb4",
}
11.4 数据库迁移
python manage.py migrate
11.5 运行爬虫
python scraw.py
11.6 启动开发服务器
python manage.py runserver
访问 http://127.0.0.1:8000/ 查看网站。
十二、项目截图
















十三、总结
本项目完整实现了一个汽车销售数据可视化分析平台,涵盖了以下技术亮点:
- 完整的数据 pipeline:从爬虫采集到数据库存储到 Web 展示
- Django 全栈开发:使用 Django 框架完成前后端完整开发
- ECharts 数据可视化:多种图表类型展示销售数据
- 协同过滤推荐算法:基于用户行为的个性化推荐
- 多算法销量预测:移动平均、加权移动平均、线性回归
- 用户系统与社交互动:注册登录、收藏点赞评论评论
- 响应式前端设计:使用 Tailwind CSS 适配多种屏幕尺寸
希望本项目对你学习 Django Web 开发、数据可视化和推荐系统有所帮助!
更多推荐



所有评论(0)