
简介这份资源是面向计算机专业学生与Web开发初学者的毕业设计完整项目包基于Python与Django构建疫情数据可视化分析系统帮助读者理解数据采集、处理与前端展示的完整链路适合作为毕设选题或课程实战参考。压缩包共745个文件约15.81MB涵盖40个py后端逻辑、43个vue与164个js前端交互、53个css样式、39个html页面以及sql建表脚本、bat启动脚本和docx开发说明前后端与数据库结构一目了然。已有61人学习下载。资源提供完整源码、数据库文件、配置教程与开发说明读者可据此快速搭建运行环境掌握Django的MVC架构、Pandas与NumPy数据处理方式及MySQL存储方案并在此基础上修改扩展功能积累从需求分析到部署上线的项目经验。1. 从一份疫情数据看板说起PythonDjango 这套组合到底能交付什么去年帮一个学弟看他的计算机毕业设计题目是「基于 PythonDjango 的疫情数据可视化分析系统」。他给我看的第一版是一个把 CSV 直接读进 pandas、用 matplotlib 生成几张静态 PNG、再塞进 HTML 里的页面。能跑但答辩老师问了三句话他就卡住了数据怎么更新多个人同时访问会不会崩省份下钻怎么实现这其实是绝大多数疫情数据可视化分析系统毕业设计的通病——把「可视化」理解成「画图」把「系统」理解成「本地脚本」。这篇笔记就围绕这个标题把一套能真正跑起来、能答辩、能当作品集的项目拆开讲。核心链路是Python 负责数据采集与清洗Django 负责 Web 服务与接口前端用 ECharts 做交互式可视化数据库存结构化疫情数据。它解决的不是「画一张图」而是「让数据从源头流到用户屏幕上并且能按省份、时间、指标多维筛选」。适合正在做计算机毕业设计、需要一套完整源码加配置教程的同学也适合想用 Django 练手一个真实数据项目的 Python 入门者。下面从环境搭建一路讲到部署和避坑每一步都给出可复现的命令和参数。2. 环境搭建与项目骨架怎样安装 Django 并创建第一个 app2.1 Python 与 Django 版本选择先说版本。疫情数据可视化分析系统这类项目依赖链不长主要是 Django、pandas、requests、pymysql 这几个。我一般锁定 Python 3.8 到 3.10 之间Django 用 3.2 LTS 或 4.2 LTS。为什么不用最新版因为毕业设计周期里你大概率会去搜「django 项目实战新手」的教程网上大量示例还停留在 3.x 的写法比如url()和path()混用、django.conf.urls.url已经废弃。用 LTS 版本能少踩一半的兼容坑。安装本身不复杂但 Windows 和 Linux 下有几个细节。Windows 上建议用官方安装包或 Microsoft Store 装 Python勾选「Add Python to PATH」否则后面python命令找不到。Linux 下如果系统自带 Python 3别急着覆盖用python3 -m venv建虚拟环境隔离。虚拟环境这一步很多人跳过结果系统里装了一堆包换项目就冲突。# 创建虚拟环境名字叫 venv python -m venv venv # Windows 激活 venv\Scripts\activate # Linux / macOS 激活 source venv/bin/activate # 安装核心依赖指定版本避免自动升级到不兼容版本 pip install django4.2.7 pandas2.0.3 requests2.31.0 pymysql1.1.0这段命令的逻辑是先隔离环境再装依赖。django4.2.7里的是精确锁定不用因为毕业设计最怕「昨天还能跑今天 pip 一升级就报错」。pymysql 是用来让 Django 连 MySQL 的如果你用 SQLite 可以暂时不装但疫情数据量到几万行以上SQLite 的并发写入会明显吃力建议直接上 MySQL。2.2 创建项目与 app 的标准动作装完 Django用django-admin startproject建项目再用manage.py startapp建应用。这里有个命名习惯项目名用covid_analysisapp 名用visualization或data_app别用中文也别用test这种和 Python 内置模块冲突的名字。# 创建项目注意末尾的点表示在当前目录创建 django-admin startproject covid_analysis . # 创建数据可视化 app python manage.py startapp visualization命令末尾那个点很关键。不加的话Django 会多套一层covid_analysis/covid_analysis/目录后面配路径时容易绕晕。创建完 app必须去settings.py的INSTALLED_APPS里注册否则模型迁移不会生效。# covid_analysis/settings.py INSTALLED_APPS [ django.contrib.admin, django.contrib.auth, django.contrib.contenttypes, django.contrib.sessions, django.contrib.messages, django.contrib.staticfiles, visualization, # 注册新建的 app ]注册完 app顺手把数据库配置改了。默认是 SQLite改成 MySQL 需要填 NAME、USER、PASSWORD、HOST、PORT 五个参数。注意 Django 4.x 里要在项目根目录的__init__.py加两行让 pymysql 冒充 MySQLdb否则启动就报No module named MySQLdb。# covid_analysis/__init__.py import pymysql pymysql.install_as_MySQLdb()这两行是血泪经验。很多人卡在django.core.exceptions.ImproperlyConfigured上查半天以为是密码错了其实是驱动没挂上。参数说明install_as_MySQLdb()的作用是让 Django 的 MySQL 后端以为自己在用官方驱动实际走的是 pymysql纯 Python 实现安装省事。2.3 数据模型设计三张表撑起整个系统疫情数据可视化分析系统的数据模型不用太复杂三张表足够省份表、每日疫情表、用户表Django 自带。核心是每日疫情表字段包括省份、日期、新增确诊、累计确诊、治愈、死亡。# visualization/models.py from django.db import models class Province(models.Model): name models.CharField(max_length32, uniqueTrue, verbose_name省份) code models.CharField(max_length16, blankTrue, verbose_name行政区划代码) class Meta: db_table province class DailyStat(models.Model): province models.ForeignKey(Province, on_deletemodels.CASCADE, verbose_name省份) date models.DateField(verbose_name日期) new_confirm models.IntegerField(default0, verbose_name新增确诊) total_confirm models.IntegerField(default0, verbose_name累计确诊) cured models.IntegerField(default0, verbose_name治愈) dead models.IntegerField(default0, verbose_name死亡) class Meta: db_table daily_stat unique_together (province, date) # 防止同省同天重复插入 indexes [models.Index(fields[date])] # 按日期查询加索引unique_together是后悔药。数据采集脚本如果重复跑没有这个约束就会插一堆重复行前端图表直接变成锯齿。indexes加在 date 上是因为可视化查询几乎都带时间范围筛选没索引的话几万行数据查询会从毫秒级掉到秒级。on_deletemodels.CASCADE表示省份删了对应的每日数据也删避免脏数据。建完模型执行迁移python manage.py makemigrations visualization python manage.py migratemakemigrations生成迁移文件migrate真正建表。如果报错说表已存在检查是不是之前手动建过同名表或者迁移记录和实际表不一致可以用python manage.py migrate visualization zero回滚再重来。3. 数据采集与清洗从公开接口到入库的完整链路3.1 数据源选择与 requests 采集疫情数据可视化分析系统的数据从哪来是答辩必问的问题。常见做法是调用公开的疫情数据接口或者用历史数据 CSV。这里不指定具体接口地址因为接口会变讲方法更重要用 requests 发 GET 请求拿到 JSON解析出省份、日期、各指标再批量入库。# visualization/services/fetch_data.py import requests import json from datetime import datetime from visualization.models import Province, DailyStat def fetch_and_save(api_url): 从接口拉取数据并写入数据库 headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64), Accept: application/json } resp requests.get(api_url, headersheaders, timeout10) resp.raise_for_status() # 非 200 直接抛异常别让脏数据往下走 data resp.json() for item in data.get(results, []): # get_or_create 避免重复建省份 province, _ Province.objects.get_or_create( nameitem[provinceName], defaults{code: item.get(provinceCode, )} ) date datetime.strptime(item[date], %Y-%m-%d).date() # update_or_create 保证同省同天只留一条重复跑脚本会更新而不是插入 DailyStat.objects.update_or_create( provinceprovince, datedate, defaults{ new_confirm: item.get(newConfirm, 0), total_confirm: item.get(totalConfirm, 0), cured: item.get(cured, 0), dead: item.get(dead, 0), } )逻辑说明raise_for_status()是防线接口返回 404 或 500 时直接中断不会把错误页面当 JSON 解析。get_or_create和update_or_create是幂等操作的核心脚本跑十遍和跑一遍结果一样。参数上timeout10必须加否则接口卡住时脚本会一直挂着。headers里的 User-Agent 有些接口会校验不加可能被拒。3.2 用 pandas 做清洗与聚合原始数据往往有缺失值、字段类型不对、省份名带「省」「市」后缀不统一等问题。pandas 在这里比手写循环高效得多。# visualization/services/clean_data.py import pandas as pd from visualization.models import DailyStat def clean_and_aggregate(): 从数据库读出数据清洗后按日期聚合返回 qs DailyStat.objects.all().values( province__name, date, new_confirm, total_confirm, cured, dead ) df pd.DataFrame(list(qs)) # 统一省份名去掉「省」「市」「自治区」后缀 df[province] df[province__name].str.replace( r(省|市|自治区|维吾尔|回族|壮族|特别行政区)$, , regexTrue ) # 缺失值填 0避免图表断线 num_cols [new_confirm, total_confirm, cured, dead] df[num_cols] df[num_cols].fillna(0).astype(int) # 按日期聚合全国数据 national df.groupby(date)[num_cols].sum().reset_index() return df, nationalstr.replace里的正则一次性处理多种后缀比写多个 if 干净。fillna(0).astype(int)先填再转类型顺序不能反否则 NaN 转 int 会报错。groupby(date).sum()得到全国每日汇总前端画趋势线直接用这个结果。3.3 定时更新与命令封装手动跑脚本不现实用 Django 的自定义管理命令封装配合系统的定时任务Windows 用任务计划程序Linux 用 crontab每天跑一次。# visualization/management/commands/update_covid.py from django.core.management.base import BaseCommand from visualization.services.fetch_data import fetch_and_save class Command(BaseCommand): help 拉取最新疫情数据并入库 def handle(self, *args, **options): api_url https://example.com/api/covid/latest # 替换为实际接口 try: fetch_and_save(api_url) self.stdout.write(self.style.SUCCESS(数据更新完成)) except Exception as e: self.stderr.write(self.style.ERROR(f更新失败: {e}))目录结构必须是app/management/commands/每层都要有__init__.py少一个 Django 就找不到命令。跑的时候用python manage.py update_covid。异常捕获后写到 stderr定时任务日志里能看到失败原因比静默失败强。4. 可视化接口与前端联动ECharts 怎么接 Django 数据4.1 设计 REST 风格接口前端要数据Django 这边返回 JSON。不用 DRF 也能做用JsonResponse就够。接口按维度分全国趋势、省份排行、单省时间序列。# visualization/views.py from django.http import JsonResponse from django.db.models import Sum from visualization.models import DailyStat def national_trend(request): 返回全国每日趋势支持 start/end 查询参数 start request.GET.get(start) end request.GET.get(end) qs DailyStat.objects.all() if start: qs qs.filter(date__gtestart) if end: qs qs.filter(date__lteend) data qs.values(date).annotate( new_confirmSum(new_confirm), total_confirmSum(total_confirm) ).order_by(date) return JsonResponse({ code: 0, dates: [str(d[date]) for d in data], new_confirm: [d[new_confirm] for d in data], total_confirm: [d[total_confirm] for d in data], })values(date).annotate(Sum(...))等价于 SQL 的GROUP BY date在数据库层完成聚合比把全表拉到 Python 里再算快一个数量级。查询参数start/end用date__gte和date__lte注意是双下划线。返回结构里code: 0是约定前端据此判断成功与否。4.2 路由配置与接口测试# covid_analysis/urls.py from django.contrib import admin from django.urls import path from visualization import views urlpatterns [ path(admin/, admin.site.urls), path(api/national_trend/, views.national_trend, namenational_trend), ]配完路由先用浏览器或 curl 测接口别急着写前端。curl http://127.0.0.1:8000/api/national_trend/?start2023-01-01看返回的 JSON 结构对不对。接口不通就查三处urls 有没有拼错、视图有没有返回 JsonResponse、数据库有没有数据。4.3 ECharts 接入与交互下钻前端页面用 ECharts从 CDN 引入然后 ajax 拿数据渲染。关键是点击省份时触发下钻请求单省数据。// static/js/trend.js const chart echarts.init(document.getElementById(trend)); fetch(/api/national_trend/) .then(res res.json()) .then(data { chart.setOption({ tooltip: { trigger: axis }, xAxis: { type: category, data: data.dates }, yAxis: { type: value }, series: [ { name: 新增确诊, type: line, data: data.new_confirm, smooth: true }, { name: 累计确诊, type: line, data: data.total_confirm, smooth: true } ] }); }); // 点击事件实现下钻 chart.on(click, params { const province params.name; fetch(/api/province_trend/?name${encodeURIComponent(province)}) .then(res res.json()) .then(data { /* 重新 setOption 渲染单省数据 */ }); });smooth: true让折线平滑视觉上更舒服但数据波动大时可能失真答辩时如果老师较真可以关掉。encodeURIComponent处理省份名里的特殊字符不加的话「内蒙古自治区」这种可能请求失败。下钻接口province_trend和全国接口写法类似只是多一个province__name过滤条件。5. 避坑与排查疫情数据可视化系统最常见的 5 个翻车点5.1 中文乱码数据库、页面、JSON 三处都要管现象图表上省份名显示成问号或方块。原因通常是 MySQL 建库时字符集不是 utf8mb4或者 Django 连接配置没指定 charset。解决建库用CREATE DATABASE covid DEFAULT CHARACTER SET utf8mb4;settings 里 OPTIONS 加charset: utf8mb4。页面乱码还要检查 HTML 的meta charsetutf-8和文件保存编码。5.2 接口返回慢没加索引和分页现象数据到十万行后趋势接口要等五六秒。原因date 字段没索引或者一次返回全部数据。解决模型里给 date 加db_indexTrue接口层限制返回条数比如只返回最近 180 天或者用qs[:1000]切片。前端也可以做分页加载。5.3 定时任务不执行路径和虚拟环境问题现象crontab 配了但数据不更新。原因crontab 的环境变量和登录 shell 不同找不到 python 和项目路径。解决crontab 里写绝对路径用虚拟环境的 python命令前加cd /path/to/project 并且把输出重定向到日志文件方便排查。5.4 静态文件 404DEBUG 关了之后现象本地跑得好好的部署后 CSS、JS 全 404。原因DEBUGFalse时 Django 不再自动服务静态文件。解决用python manage.py collectstatic收集静态文件交给 Nginx 托管或者开发阶段先保持 DEBUGTrue。生产环境别用 Django 直接服务静态文件。5.5 数据重复脚本重复执行没做幂等现象同一天的数据出现多条图表数值翻倍。原因采集脚本用create()而不是update_or_create()或者没加unique_together。解决模型层加唯一约束脚本层用update_or_create双保险。已经产生的重复数据写个清理脚本按 (province, date) 去重。6. 让系统更像作品三个提升答辩通过率的进阶技巧第一个技巧是加数据校验层。在入库前用 pandas 做一次范围检查比如新增确诊不能为负、累计确诊不能小于新增异常数据打日志并跳过。答辩老师问「数据质量怎么保证」时这一层就是答案。def validate(df): 入库前校验返回清洗后的 df 和异常记录 errors [] mask df[new_confirm] 0 if mask.any(): errors.append(f{mask.sum()} 条新增确诊为负已剔除) df df[~mask] mask2 df[total_confirm] df[new_confirm] if mask2.any(): errors.append(f{mask2.sum()} 条累计小于新增已修正) df.loc[mask2, total_confirm] df.loc[mask2, new_confirm] return df, errors第二个技巧是接口加缓存。全国趋势这种数据一天才变一次没必要每次查库。用 Django 的cache_page装饰器缓存 10 分钟。from django.views.decorators.cache import cache_page cache_page(60 * 10) def national_trend(request): ...60 * 10是秒数表示缓存 10 分钟。开发时记得在 settings 里配 CACHES默认是本地内存缓存生产环境换 Redis。缓存能明显降低数据库压力答辩演示时响应更快。第三个技巧是准备一份数据字典和接口文档。不用多正式一个 Markdown 表格列出每个接口的 URL、参数、返回示例再列一张表说明每个字段的含义和单位。答辩时老师翻到这一页印象分直接上去。接口方法参数返回/api/national_trend/GETstart, enddates, new_confirm, total_confirm/api/province_trend/GETname, start, end同上单省/api/province_rank/GETdate, top省份排行列表最后说个习惯。我做这类项目永远先跑通「采集→入库→接口→图表」这条最小链路再往上加功能。很多人一上来就设计十几个页面、七八张表结果卡在数据库连不上三天没进展就放弃了。先把一条线打通哪怕只有一个接口、一张图能跑起来就有底气往下扩。希望帮到你。本文还有配套的精品资源点击获取