157-基于Python的懂车帝汽车数据爬虫分析与可视化系统
基于Python的懂车帝汽车数据爬虫分析与可视化系统:从采集到可视化的完整实践
本文分享一个从零到一落地的数据项目:基于 Python + Django 的“懂车帝汽车数据爬虫分析与可视化系统”。项目覆盖数据采集、清洗、入库、后端展示与 ECharts 可视化,适合作为课程设计/毕设/内部数据平台的入门与进阶参考。
本文包含:项目目录结构、技术栈说明、核心代码讲解、运行部署流程、可视化示例与拓展方向,并预留了可视化截图位。
目录
- 项目简介与功能概览
- 项目目录结构
- 技术栈说明
- 环境准备与安装
- 配置说明(Django 与数据库)
- 数据模型设计(Models)
- 数据采集与清洗(爬虫 + Pandas)
- 后端视图与路由
- 前端可视化(ECharts 示例)
- 运行与体验
- 常见问题 FAQ
- 可视化展示(预留位)
- 拓展方向与总结
项目简介与功能概览
本系统围绕“新能源汽车数据”展开:
- 数据源:基于懂车帝公开页面与接口采集车型和销量数据;
- 数据库:落地 MySQL,提供车辆信息、销量、续航与整车参数等多维数据表;
- 后端:Django 组织页面与数据,提供筛选、分页、统计;
- 可视化:前端基于 ECharts 展示销量、分布、趋势、排行等图表;
- 用户体系:注册、登录、退出、资料编辑与密码修改。
已实现的核心功能(真实可用)包括:
- 用户注册/登录/退出、资料编辑、头像上传、密码修改;
- 续航榜单、销量榜单、车型与整车参数数据的分页浏览与多条件筛选;
- ECharts 可视化:月/年销量、月环比、六个月趋势、品牌销量、能源类型、价格区间、厂商分布、车型级别、驱动方式、电池类型/冷却方式、平均 CLTC 续航、平均整备质量等。
项目目录结构
下述为核心目录的精简视图(略去部分静态资源与图片):
car/
├─ car/ # 业务应用
│ ├─ models.py # 数据模型(User/Endurance/Car/CarInfo/CarData)
│ ├─ views.py # 视图函数(列表页、可视化页、用户中心等)
│ ├─ urls.py # 路由配置
│ └─ templates/ # 模板页面
│ ├─ index.html
│ ├─ login.html
│ ├─ register.html
│ ├─ edit_profile.html
│ ├─ change_password.html
│ ├─ endurance_list.html
│ ├─ car_list.html
│ ├─ car_info_list.html
│ ├─ car_data_list.html
│ ├─ endurance_visualization.html
│ ├─ car_sales_visualization.html
│ ├─ car_info_visualization.html
│ └─ car_data_visualization.html
├─ newcar/ # Django 项目配置
│ ├─ settings.py # 数据库/静态资源/语言/时区配置
│ ├─ urls.py
│ └─ wsgi.py / asgi.py
├─ spider/
│ └─ spiders.py # 爬虫脚本(requests + lxml + pandas)
├─ utils/
│ ├─ reade_car_sales_data.py # JSON 转 CSV + 图片下载
│ ├─ save_endurance_data.py # 续航图片下载并更新 DB
│ ├─ getChartData.py # 图表数据工具(示例/遗留)
│ └─ getPublicData.py # 公共数据访问工具
├─ static/ # 前端静态资源(Bootstrap/jQuery/ECharts 等)
├─ media/ # 上传与下载的图片目录(头像、车辆图)
├─ design_157_car.sql # MySQL 数据库初始化脚本(含表结构与数据)
└─ manage.py
技术栈说明
- 后端与框架:
- Django 4.1(模板 / ORM / 会话 / 路由)
- Django SimpleUI(管理后台美化)
- 数据库:
- MySQL(
django.db.backends.mysql
) - PyMySQL(脚本直连与更新)
- MySQL(
- 爬虫与数据处理:
- requests、lxml(采集与解析)
- pandas(CSV 清洗去重)
- 前端与可视化:
- Bootstrap、jQuery、Font Awesome
- ECharts(含 wordcloud 与 liquidfill 扩展)
环境准备与安装
建议 Python 3.9+、MySQL 5.7+/8.0。
# 1) 创建并激活虚拟环境(任选其一)
python -m venv venv
"venv/Scripts/activate" # Windows# 2) 安装依赖(根据实际需要精简/补充)
pip install django==4.1 pymysql requests lxml pandas# 如需使用 SimpleUI(可选)
pip install django-simpleui# 3) 数据库准备(两种方式,二选一)
# 方式A:导入现成 SQL(推荐用于快速跑通)
# 在 MySQL 中创建数据库 design_157_car 并导入根目录的 design_157_car.sql# 方式B:使用 Django 迁移(如果你打算从空库启动)
python manage.py makemigrations
python manage.py migrate# 4) 启动开发服务
python manage.py runserver 0.0.0.0:8000
配置说明(Django 与数据库)
关键设置位于 newcar/settings.py
:
DATABASES = {"default": {"ENGINE": "django.db.backends.mysql","NAME": "design_157_car","USER": "root","PASSWORD": "123456","HOST": "localhost","PORT": "3306",}
}STATIC_URL = "/static/"
STATICFILES_DIRS = (BASE_DIR / 'static',)MEDIA_URL = "/media/"
MEDIA_ROOT = BASE_DIR / 'media'
数据模型设计(Models)
系统围绕五张核心表展开:用户、续航、销量、车辆信息、整车参数。
# car/models.py(节选)
class User(models.Model):username = models.CharField(max_length=255, default='')password = models.CharField(max_length=255, default='')address = models.CharField(max_length=255, default='')avatar = models.FileField(upload_to='avatar', default='avatar/default.png')textarea = models.CharField(max_length=255, default='')creatTime = models.DateField(auto_now_add=True)class Endurance(models.Model):img = models.FileField(upload_to='carPicture', default='')carName = models.CharField(max_length=255, default='')carType = models.CharField(max_length=255, default='')price = models.FloatField(default=0.0)range = models.IntegerField(default=0)achieveRate = models.CharField(max_length=255, default='')avgRate = models.CharField(max_length=255, default='')season = models.CharField(max_length=255, default='')temperature = models.CharField(max_length=255, default='')class Car(models.Model):seriesName = models.CharField(max_length=255, default='')masterPic = models.URLField(max_length=512, default='https://.../default.png')year = models.CharField(max_length=6, default='') # '202410'monthSale = models.IntegerField(default=0)yearSale = models.IntegerField(default=0)monthBasis = models.CharField(max_length=10, default='')sixMonth = models.CharField(max_length=255, default='') # '32691,34464,...'class CarInfo(models.Model):brand = models.CharField(max_length=100)carName = models.CharField(max_length=100)carImg = models.URLField(max_length=200)saleVolume = models.IntegerField()price = models.CharField(max_length=50)manufacturer = models.CharField(max_length=100)rank = models.IntegerField()carModel = models.CharField(max_length=100)energyType = models.CharField(max_length=50)marketTime = models.CharField(max_length=50)insure = models.CharField(max_length=50)class CarData(models.Model):manufacturer_model = models.CharField(max_length=100)car_name = models.CharField(max_length=100)guide_price = models.DecimalField(max_digits=10, decimal_places=2)car_level = models.CharField(max_length=50)body_structure = models.CharField(max_length=50)length = models.IntegerField()width = models.IntegerField()height = models.IntegerField()wheelbase = models.IntegerField()curb_weight = models.IntegerField()battery_type = models.CharField(max_length=50)energy_type = models.CharField(max_length=50)cell_brand = models.CharField(max_length=50)battery_cooling = models.CharField(max_length=50)cltc_range = models.IntegerField(null=True, blank=True)wltc_range = models.IntegerField(null=True, blank=True)battery_capacity = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True)drag_coefficient = models.DecimalField(max_digits=5, decimal_places=2, null=True, blank=True)energy_density = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True)drive_type = models.CharField(max_length=50)
数据采集与清洗(爬虫 + Pandas)
爬虫入口位于 spider/spiders.py
,采用 requests
请求懂车帝接口,配合 lxml
抓取详情页补充字段,使用 pandas
清洗 CSV 再写入数据库。
# spider/spiders.py(节选)
class Spider(object):def __init__(self):self.baseUrl = 'https://www.dongchedi.com/motor/pc/car/rank_data'self.headers = {'User-Agent': 'Mozilla/5.0 (... Chrome/104.0.0.0 Safari/537.36)'}def get_month_list(self):months = []for year in range(2022, 2023):for month in range(1, 13):months.append(f'{year}{month:02d}')return monthsdef scrape_data_for_month(self, month):params = { 'month': month, 'count': 10000, 'rank_data_type': 11 }page_json = requests.get(self.baseUrl, headers=self.headers, params=params).json()car_list = page_json.get("data", {}).get("list", [])for car in car_list:# 组装品牌/车系/图片/销量/价格/厂商/排名等字段...# 请求详情页,抓取 carModel/energyType/marketTime/insure 等...def clear_csv(self):df = pd.read_csv('./temp.csv')df.dropna(inplace=True)df.drop_duplicates(inplace=True)return df.valuesdef save_to_sql(self):data = self.clear_csv()for car in data:CarInfo.objects.create(brand=car[0], carName=car[1], carImg=car[2], saleVolume=car[3],price=car[4], manufacturer=car[5], rank=car[6], carModel=car[7],energyType=car[8], marketTime=car[9], insure=car[10])if __name__ == '__main__':spider_obj = Spider()spider_obj.init()for month in spider_obj.get_month_list():spider_obj.scrape_data_for_month(month)spider_obj.save_to_sql()
图片下载与本地路径替换(以销量数据为例)在 utils/reade_car_sales_data.py
:
output_dir = '../media/cars'
os.makedirs(output_dir, exist_ok=True)with open('car_sales_data.json', 'r', encoding='utf-8') as file:response = json.load(file)for item in response.get('data', []):master_pic_url = "https://cdn-fs.touchev.com" + item['masterPic']img_response = requests.get(master_pic_url)if img_response.status_code == 200:img_name = f"{item['seriesName']}.jpg"with open(os.path.join(output_dir, img_name), 'wb') as img_file:img_file.write(img_response.content)# 将 URL 替换为本地可访问路径master_pic_url = f"/media/cars/{img_name}"
后端视图与路由
数据的分页、筛选与可视化入口在 car/views.py
。示例:
# 列表与筛选:车辆榜单(按市场时间、能源类型、车型、厂商、车名)
def car_info_list(request):car_info_list = CarInfo.objects.all()market_time = request.GET.get('market_time')energy_type = request.GET.get('energy_type')car_model = request.GET.get('car_model')manufacturer = request.GET.get('manufacturer')car_name = request.GET.get('car_name')if market_time: car_info_list = car_info_list.filter(marketTime=market_time)if energy_type: car_info_list = car_info_list.filter(energyType=energy_type)if car_model: car_info_list = car_info_list.filter(carModel=car_model)if manufacturer: car_info_list = car_info_list.filter(manufacturer=manufacturer)if car_name: car_info_list = car_info_list.filter(carName__icontains=car_name)car_info_list = car_info_list.order_by('-marketTime', 'rank')paginator = Paginator(car_info_list, 10)page_obj = paginator.get_page(request.GET.get('page', 1))return render(request, 'car_info_list.html', { 'page_obj': page_obj, ... })# 可视化数据准备:销量
def car_sales_visualization(request):car_data = Car.objects.all()series_names = [d.seriesName for d in car_data]month_sales = [d.monthSale for d in car_data]year_sales = [d.yearSale for d in car_data]month_bases = []for d in car_data:try:month_bases.append(float(d.monthBasis.rstrip('%')) if d.monthBasis else 0)except ValueError:month_bases.append(0)six_month_sales_list = [[int(s) for s in d.sixMonth.split(',') if s] for d in car_data]return render(request, 'car_sales_visualization.html', {'series_names_json': json.dumps(series_names),'month_sales_json': json.dumps(month_sales),'year_sales_json': json.dumps(year_sales),'month_bases_json': json.dumps(month_bases),'six_month_sales_list_json': json.dumps(six_month_sales_list),})
路由集中在 car/urls.py
:
urlpatterns = [path("home/", views.home),path("login/", views.login),path("register/", views.register),path("logOut/", views.logOut),path('endurance-list/', views.endurance_list),path('car-list/', views.car_list),path('car-info-list/', views.car_info_list),path('car-data-list/', views.car_data_list),path('endurance-visualization/', views.endurance_visualization),path('car-sales-visualization/', views.car_sales_visualization),path('car-info-visualization/', views.car_info_visualization),path('car-data-visualization/', views.car_data_visualization),path('edit-profile/', views.edit_profile),path('change-password/', views.change_password),
]
前端可视化(ECharts 示例)
以“电动汽车销量分析”页面为例(car/templates/car_sales_visualization.html
),从后端注入 JSON,再用 ECharts 渲染:
<!-- 模板中通过后端上下文注入数据 -->
<script>const seriesNames = JSON.parse('{{ series_names_json|safe }}');const monthSales = JSON.parse('{{ month_sales_json|safe }}');const yearSales = JSON.parse('{{ year_sales_json|safe }}');const monthBases = JSON.parse('{{ month_bases_json|safe }}');const sixMonthSalesList = JSON.parse('{{ six_month_sales_list_json|safe }}');const monthChart = echarts.init(document.getElementById('month-sales-chart'));monthChart.setOption({title: { text: '月销量' },tooltip: {},xAxis: { type: 'category', data: seriesNames },yAxis: { type: 'value' },series: [{ type: 'bar', data: monthSales }]});// 其余图表同理...
}</script>
运行与体验
-
启动服务后访问:
http://127.0.0.1:8000/car/login/
-
注册登录后进入系统首页,左侧导航进入:
- 续航榜单:/car/endurance-list/
- 车辆数据:/car/car-list/
- 车辆榜单:/car/car-info-list/
- 整体数据:/car/car-data-list/
- 可视化:/car/*-visualization
- 头像上传、资料修改与密码修改在“Edit”菜单下。
常见问题 FAQ
- 静态资源不生效:检查
STATIC_URL
与STATICFILES_DIRS
,开发环境确保通过模板引用/static/...
。 - 图片 404:确认
MEDIA_URL
与MEDIA_ROOT
设置,模板中使用/media/...
路径,Django 开发模式需通过urlpatterns += static(...)
暴露媒体目录。 - MySQL 连接失败:检查账号/密码/端口,确保已创建数据库并导入
design_157_car.sql
或执行迁移。 - pandas/lxml 缺失:
pip install pandas lxml
,Windows 环境建议提前安装编译依赖或使用预编译轮子。 - ECharts 无法渲染:确认
<script src="/static/js/echarts.min.js"></script>
已正确引入且容器有宽高。
可视化展示
项目源码获取,码界筑梦坊各平台同名,博客底部含联系方式卡片,欢迎咨询!
基于Python的汽车数据可视化分析系统
---## 拓展方向与总结拓展建议:- 增量爬取与定时任务:结合 Celery/Crontab 定时刷新数据;
- 数据质量与异常检测:对销量/价格等关键字段进行阈值校验与异常报警;
- 更丰富的可视化:箱线图、雷达图、桑基图、时序预测;
- 权限与审计:基于 Django 权限体系引入角色与操作审计;
- 前后端分离:将可视化切换为 REST API + 前端框架(Vue/React + ECharts)。至此,一个覆盖“采集—清洗—入库—分析—可视化”的端到端样例已完整跑通,可作为后续课程设计与企业内部数据工具的基础模板。