ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

基于Flask的智慧气象数据采集分析系统:从数据采集到AI预测的完整工程实践

基于Flask的智慧气象数据采集分析系统:从数据采集到AI预测的完整工程实践 每年到这个节点总会遇到一批学生拿着类似题目来找我Python智慧气象数据采集分析系统基于Flask的天气可视化平台机器学习驱动的大气质量分析……标题一个比一个亮眼但细聊下来大多数人的真实状态是不知道从哪下手不知道做到什么程度算完更不知道答辩时老师会盯着哪里问。这个题目在毕业设计里属于典型的技术栈全但难度不深的类型核心是Python加上Flask框架再配上可视化、数据分析、机器学习甚至深度学习这些关键词。表面看功能很多但恰恰因为多反而容易做成一锅粥。这篇文章我按实际做过这类项目的经验把从题目拆解、数据采集、后端搭建、可视化呈现到AI模型落地这一整条线的关键思路和坑都捋一遍你把它当成一份可直接照着干的工程笔记来看就行。1. 这个题目真正在考什么功能边界与主线设计很多学生拿到这个题目后的第一反应是先爬数据再画图表最后跑个预测模型完事。但这种功能堆砌式的做法往往到答辩时站不住脚。老师问一句你的数据从哪来、质量如何保证模型预测的依据是什么系统各个模块之间是什么关系就很容易卡住。1.1 从题目关键字反推需求权重把标题拆开看核心词有这么几个智慧、气象数据采集、分析系统、Flask框架、可视化、机器学习、深度学习、AI、空气质量分析。这些词在毕设题目里是有权重差异的。我给你的判断是数据采集和数据分析是主干可视化是门面机器学习和深度学习是加分项。原因很简单这个题目叫分析系统不是预测系统所以数据全流程的完整性和分析逻辑的自洽性比模型精度更重要。智慧这个词在本科毕设里通常等于自动化采集智能预测不需要往数字孪生、AI中台那个方向想。深度学习和AI虽然听着高级但在气象场景里LSTM预测PM2.5浓度或温度变化就是最合理且不过分夸张的落点。1.2 一条数据流水线定生死做这类题有一个核心原则不要按功能模块组织项目要按数据流组织项目。你的系统本质上是一条流水线采集数据、清洗入库、查询分析、可视化展示、模型预测。每一步的输出是下一步的输入。我建议你在动手写代码前先在文档里画出这条流水线明确每一步的输入输出。比如采集层定时从天气API拉取城市天气和空气质量数据写入MySQL存储层MySQL存储结构化数据字段设计要满足后续分析和建模需要服务层Flask提供数据查询和预测接口展示层ECharts图表渲染在大屏页面AI层历史数据训练模型保存模型文件预测接口动态调用这条主线走通了项目就已经完成了80%。剩下的20%是界面好不好看、图表够不够丰富、模型有没有对比实验。1.3 一套不踩雷的技术选型我直接给结论以下是多次验证过、适合毕设场景的组合层次推荐选型理由后端框架Flask 2.x轻量、灵活、写接口快适合中小型系统演示数据库MySQL / SQLite结构化气象数据用关系型数据库最合适本地演示用SQLite更省事ORMSQLAlchemy配合Flask-SQLAlchemy避免手写SQL的重复劳动定时采集APScheduler支持cron表达式配置一次每小时跑一次采集脚本可视化前端ECharts 5.x图表类型全、交互好、图标好看在线引用或本地JS包都方便机器学习scikit-learn随机森林、逻辑回归、线性回归足够覆盖大部分需求深度学习PyTorch / Keras做LSTM时间序列预测二选一即可模型部署joblib Flask内存加载训练时保存模型文件Web服务启动时加载预测接口实时调用为什么不用Django不是Django不好是对于这个题目来说Flask的轻量特性更合适——你不需要Django自带的Admin后台、认证体系、ORM全套你只需要一个能快速起接口、结构清晰、答辩时能讲明白的框架。更何况Flask加蓝图加SQLAlchemy这套组合本身就足够规范了。2. 数据采集层API、爬虫与开源数据集的取舍气象数据从哪来是这个项目第一个卡人的地方。我见过太多学生一上来就写爬虫去爬各种天气网站结果反爬机制一升级代码就废了。这里我给你把三条路都讲清楚你再决定走哪条。2.1 三种数据来源的优劣对比数据来源优点缺点适用场景免费天气API和风天气、心知天气、OpenWeatherMap字段规范、可用率高、免费版够用有调用频率限制历史数据要积分实时数据采集演示爬虫爬取公开天气站点数据量不受限可获取历史数据需要处理反爬、页面结构变更、robots合规不建议作为主线开源数据集Kaggle、UCI、国内公开数据数据量大、质量可控、适合做模型训练不是实时数据缺采集过程机器学习和深度学习实验2.2 推荐组合API实时采集加开源历史数据集我的建议是两条腿走路用免费API做实时采集保证系统有活数据用开源历史数据集做模型训练保证AI部分有足够样本。这样既满足采集的功能展示又不至于在模型训练时数据量不够。以心知天气API为例调用逻辑非常简单import requests import json def fetch_weather(city_id, api_key): url https://api.seniverse.com/v3/weather/daily.json params { key: api_key, location: city_id, language: zh-Hans, unit: c, start: 0, days: 3 } resp requests.get(url, paramsparams, timeout10) resp.raise_for_status() return resp.json()返回的JSON里包含白天温度、夜间温度、天气现象、湿度、风速、空气质量等字段。这里有一个关键动作原始JSON一定要经过字段提取和类型转换后再入库不要直接把整个JSON塞进数据库。原因有二一是嵌套JSON后期查询很痛苦二是答辩时表结构设计是必问项你需要展示清晰的关系表。2.3 采集工程的三件正经事去重、日志、重试实时采集最怕的不是没数据而是重复数据和中断数据。我见过有个采集脚本跑了三天库里出现大量同一个城市同一天的两条记录就是因为没做去重约束。去重策略很简单——在建表时给业务唯一键加唯一索引。比如天气记录表中(city_id, date) 设置为联合唯一索引插入时用INSERT ... ON DUPLICATE KEY UPDATE或者先查重再插入。这样无论定时任务跑多少次数据都不会重复。日志和重试同样重要。每次采集请求要记录时间、城市、成功与否、数据条数这样出了问题你能知道是哪个城市挂了还是API密钥过期了。建议用Python内置的logging模块按天切分日志文件import logging from logging.handlers import TimedRotatingFileHandler handler TimedRotatingFileHandler(logs/collector.log, whenmidnight, backupCount7) formatter logging.Formatter(%(asctime)s - %(levelname)s - %(message)s) handler.setFormatter(formatter) logger logging.getLogger(weather_collector) logger.addHandler(handler) logger.setLevel(logging.INFO)重试这块用retrying库或者自己写循环都行原则是对网络超时和5XX错误做最多3次重试对参数错误和4XX错误不重试因为后者重试了也没用。3. Flask后端骨架用蓝图把采集、查询、预测三件事分开Flask后端的设计思路一句话总结就是用蓝图把不同业务域拆开不要让所有路由堆在一个app.py里。这一点答辩时很加分因为老师一眼就能看出你有没有工程意识。3.1 项目目录结构与蓝图划分推荐的结构长这样weather_system/ ├── app.py # 应用入口注册蓝图 ├── config.py # 配置项数据库地址、API密钥等 ├── models/ │ ├── __init__.py │ └── weather.py # SQLAlchemy 模型 ├── routes/ │ ├── __init__.py │ ├── main.py # 页面路由 │ ├── api.py # 数据查询接口 │ └── predict.py # 模型预测接口 ├── services/ │ ├── __init__.py │ ├── collector.py # 数据采集服务 │ └── predictor.py # 模型加载与预测服务 ├── scripts/ │ ├── init_db.py # 初始化数据库 │ └── train_model.py # 训练模型脚本 ├── static/ │ ├── css/ │ ├── js/ │ └── echarts/ ├── templates/ │ └── index.html # 大屏页面 ├── models_data/ # 训练好的模型文件目录 ├── logs/ └── requirements.txt这个结构的好处是边界清楚routes层只做参数接收和响应返回services层只做业务逻辑models层只定义表结构。你后期改任何一层不影响其他层。蓝图注册的方式from flask import Flask from routes.main import main_bp from routes.api import api_bp from routes.predict import predict_bp def create_app(): app Flask(__name__) app.config.from_object(config.Config) app.register_blueprint(main_bp) app.register_blueprint(api_bp, url_prefix/api) app.register_blueprint(predict_bp, url_prefix/api/predict) return app3.2 数据库表设计两张核心表一定要规范这个项目的表不需要太多但核心两张表必须经得起推敲。一张是城市天气日表一张是空气质量日表或者你也可以合并成一张宽表。我的建议是分开因为空气质量字段比天气字段多不少。天气表示例from datetime import datetime from flask_sqlalchemy import SQLAlchemy db SQLAlchemy() class WeatherDaily(db.Model): __tablename__ weather_daily id db.Column(db.Integer, primary_keyTrue, autoincrementTrue) city db.Column(db.String(50), nullableFalse) date db.Column(db.Date, nullableFalse) high_temp db.Column(db.Float) low_temp db.Column(db.Float) weather_day db.Column(db.String(50)) weather_night db.Column(db.String(50)) wind_direction db.Column(db.String(20)) wind_scale db.Column(db.String(20)) humidity db.Column(db.Integer) created_at db.Column(db.DateTime, defaultdatetime.now) __table_args__ ( db.UniqueConstraint(city, date, nameuq_city_date), )空气质量表则建议包含AQI指数、PM2.5、PM10、SO2、NO2、O3、CO以及质量等级字段。注意等级字段我建议存等级名称优、良、轻度污染等不要只存AQI数值因为可视化时你需要按等级分组统计直接查名称高效得多。3.3 接口设计思想按图表需求反推接口后端接口不是随便设计的一个实用原则是前端要什么图表后端就提供什么聚合结果。而不是把原始数据全发给前端让前端自己算。比如可视化大屏上有一个柱状图展示最近7天各城市PM2.5均值对比你应该在后端写好聚合SQL返回结构是[ {city: 北京, avg_pm25: 45.2}, {city: 上海, avg_pm25: 38.7}, {city: 广州, avg_pm25: 28.3} ]对应的查询逻辑from sqlalchemy import func def get_city_avg_pm25(start_date, end_date): result db.session.query( AirQuality.city, func.avg(AirQuality.pm25).label(avg_pm25) ).filter( AirQuality.date.between(start_date, end_date) ).group_by(AirQuality.city).all() return [{city: r.city, avg_pm25: round(r.avg_pm25, 1)} for r in result]3.4 模型接入方式joblib加载加内存常驻机器学习模型接进Flask有一个关键点不要在每次请求时都加载模型文件。joblib.load()是磁盘I/O操作请求一多会很慢。正确做法是在Flask应用启动时把模型加载进全局变量之后接口直接调用。import joblib class Predictor: _model None classmethod def get_model(cls): if cls._model is None: cls._model joblib.load(models_data/pm25_lstm.joblib) return cls._model def predict_next_hour(features): model Predictor.get_model() result model.predict([features]) return result[0]4. 可视化大屏用ECharts把数据讲成故事可视化部分是这个项目最直观的门面。答辩时老师第一眼看到的就是你的页面。一个大气的可视化大屏能直接把项目档次拉高一个级别。4.1 为什么是ECharts而不是Matplotlib如果这只是一个数据分析报告用Matplotlib画图没问题。但你的系统是一个Web平台需要的是交互式图表——鼠标悬停显示数值、时间轴拖动、数据刷新动效。ECharts就是干这个的。另外ECharts纯前端渲染Flask只需要提供JSON数据前后端耦合度低出问题也好排查。页面引用的方式有两种。一种是在线CDN一种是把echarts.min.js下载到本地static目录。毕设答辩现场网络不稳定一定要用本地文件。4.2 大屏布局与核心图表搭配一个典型的可视化大屏由上到下、由左到右分几个区域区域图表类型展示内容顶部数字卡片当前温度、湿度、PM2.5、AQI指数左侧地图散点图各城市空气质量等级分布中间折线图最近24小时温度与PM2.5变化趋势中间下方柱状图各城市平均气温对比右侧雷达图当前城市六项污染物浓度右侧下方堆叠条形图近7天空气质量等级分布这个搭配的合理性在于每一张图表回答一个具体问题。地图回答哪些城市空气差折线回答趋势怎么变雷达回答主要污染物是谁。而不是为了堆图表而堆图表。4.3 空气质量等级颜色规范空气质量等级的颜色是有国标规范的优绿色、良黄色、轻度污染橙色、中度污染红色、重度污染紫色、严重污染褐红色。这个细节很加分说明你不是随便选的颜色。const aqiColorMap { 优: #00E400, 良: #FFFF00, 轻度污染: #FF7E00, 中度污染: #FF0000, 重度污染: #99004C, 严重污染: #7E0023 };在地图散点图和等级分布条形图中应用这套颜色整块大屏的视觉语言就统一了。4.4 交互与实时刷新让大屏活起来静态图表谁都会做加交互才是亮点。我建议加两个交互功能。第一个是时间范围筛选。页面顶部放一个日期选择器用户选中日期区间后前端发起AJAX请求后端按新的时间范围返回聚合数据图表用setOption动态更新。这样你既展示了数据查询能力也让大屏有了分析系统的感觉。第二个是模拟实时刷新。用setInterval每30秒请求一次最新数据折线图向后平移。这里有一个小技巧模拟实时采集时可以从库里拉最近N条数据而不是真的等采集任务跑完避免演示时数据迟迟不更新造成尴尬。核心刷新逻辑setInterval(() { fetch(/api/weather/latest) .then(res res.json()) .then(data { temperatureChart.setOption({ series: [{ data: data.temp_series }] }); pm25Chart.setOption({ series: [{ data: data.pm25_series }] }); }); }, 30000);5. 机器学习与深度学习部分有对比、有结论才是亮点这个题目里的机器学习和AI部分最容易出现的问题是两个极端要么随便跑个线性回归糊弄过去要么非要搞一个复杂的深度学习模型然后在数据处理上翻车。正解是设计一组有对比的实验得出一个可信的结论。5.1 三个合理的选题方向基于气象和空气质量数据最适合毕设的三个任务一是温度预测。用前几天的最高温度、最低温度、湿度、气压、天气现象做特征预测未来24小时最高温度。这是回归任务baseline用线性回归升级用随机森林。二是空气质量等级分类。用六项污染物浓度做特征预测等级优/良/轻度污染/中度污染/重度污染/严重污染。这是多分类任务用随机森林或逻辑回归都能做。三是PM2.5浓度时间序列预测。用过去12小时的PM2.5浓度序列预测未来1小时浓度。这是序列任务适合用LSTM。5.2 特征工程与数据预处理这部分讲清楚比调参重要答辩时老师不会问你调了什么超参数而是会问你数据是怎么处理的。所以你在论文和汇报里要有一条清晰的预处理链条缺失值处理气象数据经常有空值连续型字段用前后日期的线性插值填充不要用均值填充异常值检测用3σ原则或IQR四分位距法识别极端值比如温度传感器故障导致的-999这种特征相关性分析画热力图看温度和湿度、气压之间的关系选择相关性不太强的特征进入模型避免多重共线性归一化对神经网络输入做MinMaxScaler对树模型不必须import pandas as pd from sklearn.preprocessing import MinMaxScaler def preprocess_data(df): # 按日期排序 df df.sort_values(date) # 缺失值线性插值 df[pm25] df[pm25].interpolate(methodlinear) df[temp] df[temp].interpolate(methodlinear) # 异常值替换 for col in [pm25, temp, humidity]: mean, std df[col].mean(), df[col].std() df.loc[(df[col] - mean).abs() 3 * std, col] mean # 相关性分析后选择特征 features df[[temp, humidity, pressure, wind_speed]].values target df[pm25].values return features, target5.3 对比实验设计让结论有说服力不要只跑一个模型就完事。最低要求是三个模型做对比线性回归或逻辑回归作为baseline随机森林作为集成方法代表LSTM作为深度学习方法代表。用统一的测试集和评估指标来衡量。回归任务用MAE平均绝对误差和RMSE均方根误差分类任务用准确率加F1-score因为空气质量等级存在类别不平衡准确率会骗人。把结果汇总成一张表模型任务MAERMSE备注线性回归PM2.5预测18.323.5baseline随机森林PM2.5预测12.116.8特征非线性关系拟合更好LSTMPM2.5预测10.414.2序列特征利用充分这张表比任何花哨的功能展示都更有说服力因为它展示了你完整的研究思路先提出问题再设计实验最后通过数据得出结论。5.4 别让深度学习变成黑洞LSTM在毕设里最常见的问题有两个。一个是数据量不够气象数据如果只有几百条LSTM的表现大概率不如随机森林这个不丢人你如实写进实验分析反而是亮点。另一个是时间步长和序列构造的问题LSTM输入是三维的(batch_size, time_steps, features)很多人在这一步卡很久。构造序列样本的代码import numpy as np def create_sequences(features, target, time_steps12): X, y [], [] for i in range(len(features) - time_steps): X.append(features[i : i time_steps]) y.append(target[i time_steps]) return np.array(X), np.array(y)如果你实在不想碰深度学习把随机森林做好做透加上封装完整的训练和预测流程照样能过。但如果你做了LSTM即使效果只是略微优于随机森林也足以支撑一句话深度学习在时序建模中具有潜力但需要更多数据才能完全释放优势——这句话在答辩中非常好用。6. 答辩前的工程细节这些坑千万别踩很多项目功能没问题栽在工程细节上。这一章全是实测经验。6.1 环境与依赖管理不要把希望寄托在答辩机器的Python环境上。提交材料时一定要附带requirements.txt并且锁定大版本。同时准备一份README写清楚运行顺序先pip install -r requirements.txt再python scripts/init_db.py然后python scripts/train_model.py最后python app.py。这里有个高频坑scikit-learn版本兼容性导致joblib加载模型失败。你在本地用sklearn 1.3训练的模型拿到答辩机器上如果环境是sklearn 1.0joblib.load()可能直接报错。解决办法很简单在README里写明必须安装的版本或者用pickle加协议版本保存。为了保险我的习惯是把依赖版本写死Flask2.3.3 flask-sqlalchemy3.1.1 scikit-learn1.3.2 joblib1.3.2 pandas2.1.4 numpy1.26.2 APScheduler3.10.4 requests2.31.06.2 演示环节的断网保命技巧这是我从实战中总结出来的特别重要答辩现场的网不一定靠谱。你的大屏页面、ECharts本地JS、API数据查询都可能因为断网或防火墙而失败。保命方案是本地化三层ECharts的JS文件下载到static目录不走CDN页面用到的所有图标字体和CSS都放在本地数据库准备一份预采集好的演示数据至少包含30个城市、最近30天的记录这样哪怕现场完全没网你的系统也能完整跑起来。数据库文件直接放到项目目录下答辩时用SQLite模式启动一键演示。6.3 答辩老师最常追问的三个问题根据我的观察这类题目答辩时高频出现的问题有三个提前准备好答案能让你稳很多。第一个问题你的数据量有多少模型训练集和测试集怎么划分的答案要点给出具体数字比如共采集了XX个城市近XX天的数据共计XX条按时间顺序前80%做训练后20%做测试。注意是按时间切分不是随机切分因为时间序列数据随机切分会造成数据泄露这点主动说出来非常加分。第二个问题为什么选择这些特征答案要点从相关性分析入手说明你算过特征之间的相关系数排除高相关性特征。同时结合气象常识比如湿度对PM2.5浓度有影响、风速影响污染物扩散。把数据驱动和领域知识两条腿都展示出来。第三个问题如果污染源突然变化你的模型还准吗这个问题考察的是你对模型局限性的认知。正确回答思路是承认模型基于历史数据建模对突发性污染事件预测能力有限但系统设计了定时重训练机制可以定期用新数据更新模型。你可以提前设计一个手动触发重新训练的按钮哪怕是假的也能体现你有这个意识。6.4 资料交付清单别让辛苦白费最后整理一份完整的交付清单确保辛苦开发的系统能在任何机器上复现源代码含注释、requirements.txt、README部署文档、演示用的SQLite数据库、训练好的模型文件、答辩PPT、系统操作演示视频录屏备用。其中演示视频是我特别推荐的。答辩现场如果出任何意外——投影仪不兼容、系统起不来、数据接口超时——一个提前录好的3分钟演示视频能兜底。没有人会因为你准备充分而扣分反而会因为事故处理得当给你加印象分。写在最后的个人体会这类气象数据分析系统本质上是一个数据全流程项目从采集、存储、分析、可视化到AI预测每一步都不算特别深但串联起来就是完整的能力展示。我见过太多学生纠结于深度学习模型效果不够好或者图表不够炫却忽略了一个核心事实毕业设计考察的是你能否用工程化思维解决一个有实际背景的问题而不是要求你发一篇顶会论文。如果你正在做这个题目我的建议是先把主线走通——数据能采、能存、能查、能展示、能预测然后再考虑优化和扩展。主线通了哪怕界面朴素一点、模型简单一点你都有底气和老师对答如流。反过来功能堆了一堆但数据流断断续续问两句就露怯那才是真正的扣分点。这个系统后续可以扩展的方向也很多比如接入更多城市和更多数据源、增加天气预警推送、做多模型融合预测、把模型换成Prophet或Transformer试试效果。但这些都是后话先把眼前这一条流水线做得扎实你会比大多数人都从容。
返回列表