news 2026/10/5 7:55:26

Python天气数据预测系统实战:从采集清洗到建模预测全流程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Python天气数据预测系统实战:从采集清洗到建模预测全流程

前一段时间有个做户外活动的朋友跟我吐槽,说活动日期老是撞上天气突变,平台上的天气预报又不准,一场活动说取消就取消,损失不小。我当时就在想,与其眼巴巴等着别人给的预报,不如自己抓数据、自己分析、自己做个小范围的气温预测模型。于是就有了这个“基于Python的天气数据分析预测系统”。说白了,就是自己动手写一套从数据采集、清洗、分析到建模预测的完整流程,最后用图表把未来几天的趋势画出来。这套东西特别适合刚学完Python基础、想找实战项目的同学,也适合工作中需要做数据分析和简单预测的人,比如活动策划、物流调度、农业种植、电力负荷估算这些场景。我先把整套项目的设计和核心代码逻辑拆开讲一遍,再把我实际踩过的坑和排查思路整理出来,希望能帮你少绕弯路。

1. 项目整体设计与思路拆解

1.1 先搞清楚预测系统到底要做什么

很多人一上来就想搞深度学习、神经网络,其实没必要。天气预测这事,如果只做短期气温趋势,传统机器学习模型完全够用,而且训练快、可解释性强,出了问题也容易定位。我这个系统核心就干四件事:抓取历史天气数据、清洗数据、构造特征、训练模型并预测未来气温走势。目标很明确,不要贪多,先做一个能用的最小闭环。

确定了目标之后,就要考虑数据从哪来。常见的路子有三种:爬第三方天气网站、调用免费天气API、找现成的历史数据集。我当时为了练手,先用了requests库直接爬公开天气页面,后来又换了免费的Open-Meteo API做数据源。爬虫的好处是灵活,什么字段都能抓;坏处是容易被反爬限制,而且要考虑对方网站的结构变化。API的好处是数据格式规整、稳定,通常有历史数据接口也有未来预报接口,省掉不少解析功夫。如果你只是想做分析建模,我建议直接用API,别跟反爬较劲。

1.2 方案选型:为什么用Python这一套组合

这个项目的技术栈不算复杂:Python做主语言,requests或者urllib做数据采集,pandas做数据处理,scikit-learn做模型训练,matplotlib做可视化。有人会问,为什么要用pandas而不是直接操作列表?因为天气数据是典型的结构化表格数据,有日期、最高温度、最低温度、天气现象、湿度、风速等字段,pandas的DataFrame天生就是干这个的。筛选、分组、滞后字段生成、缺失值填充,十行以内就能搞定,如果用纯Python列表,这些操作会让你写到怀疑人生。

至于为什么选scikit-learn而不是自己手写模型,或者上深度学习框架,原因也很实际:数据量就几百条到几千条,用线性回归、随机森林这类经典模型已经能取得不错的效果。深度学习需要的数据量和调参成本太高,模型解释性是负资产,对一个小系统来说纯属杀鸡用牛刀。我后续会专门说模型的选择,这里先不展开。

1.3 系统架构和模块划分

整个系统我拆成了四个模块:采集模块负责把历史数据和未来预报数据拉下来,清洗模块做格式规范、缺失值处理、异常值剔除,特征工程模块负责把日期转换成星期、月份、滞后温度差等特征,模型与输出模块负责训练、评估和画图。这样做的好处是每一块都可以独立调试,爬虫挂了不影响建模,清洗逻辑改了模型也能继续用。我见过不少新手把一个系统所有代码堆在一个脚本里,一旦出了问题,改一处崩一片,排查起来特别痛苦。所以从项目一开始,就要养成模块化的习惯。

2. 环境准备与数据获取

2.1 Python环境搭建和依赖安装

这一步看似基础,但很多人卡得莫名其妙。如果你电脑上装的是Python 3.8以上版本,其实大部分库都能直接支持。我建议你用虚拟环境,别把包一股脑装到全局环境里,不然不同项目依赖冲突起来会让人崩溃。用venv或者conda都行,我个人习惯用conda,因为它在Windows下处理某些库的底层依赖更方便。

装包的时候,国内用户直接用pip install容易超时或者下载慢,我建议配置一下国内镜像源。比如在命令行里执行:

pip install pandas scikit-learn matplotlib requests -i https://pypi.tuna.tsinghua.edu.cn/simple

这里我踩过一个坑:新电脑上直接pip install scikit-learn,结果因为Python版本是3.12,某几个旧版本的依赖编译不兼容,报了一堆红字。后来换成了指定版本号,比如scikit-learn==1.3.2,问题就解决了。所以装包的时候,如果遇到编译错误,优先检查Python版本和库版本是否匹配,不要盲目升级到最新版。

2.2 数据源选择:API和爬虫怎么权衡

我最初是用爬虫去抓天气网站的,抓到的是HTML页面,需要解析字段,代码里充满了各种find和正则,维护成本很高。后来换成Open-Meteo,这个API无需密钥,支持历史数据和未来预报,返回JSON格式,还允许指定经纬度、地区、时间范围和需要的气象变量。调用方式非常简单,就是一个返回JSON的URL:

https://archive-api.open-meteo.com/v1/archive?latitude=39.90&longitude=116.40&start_date=2023-01-01&end_date=2023-12-31&daily=temperature_2m_max,temperature_2m_min&timezone=Asia%2FShanghai

返回内容用requests.get()拿下来,再调json()方法解析,直接就能转成DataFrame。这样数据获取的时间从一个多小时压缩到几十秒,稳定性也大大提升。如果你非要用爬虫,我提醒一句:一定要设置合理的请求头和访问频率,不然很容易被对方拒绝访问,严重的还可能被拉黑IP。

2.3 采集模块的具体实现

我用requests写了一个简单的封装函数,只干一件事:给定开始日期、结束日期、经纬度,返回一个清洗好的DataFrame。核心代码大概是这样的:

import requests import pandas as pd def fetch_weather_history(latitude, longitude, start_date, end_date): url = "https://archive-api.open-meteo.com/v1/archive" params = { "latitude": latitude, "longitude": longitude, "start_date": start_date, "end_date": end_date, "daily": "temperature_2m_max,temperature_2m_min,precipitation_sum,wind_speed_10m_max", "timezone": "Asia/Shanghai" } resp = requests.get(url, params=params, timeout=30) resp.raise_for_status() data = resp.json()["daily"] df = pd.DataFrame(data) df["date"] = pd.to_datetime(df["time"]) df = df.drop(columns=["time"]) return df

这一步有个小细节容易被忽略:不管API返回的日期字段是字符串还是时间戳,都要显式转换成pandas的Datetime类型,不然后面按日期分组、画图时横轴排序都是乱的。我自己刚开始就是没转换,结果画出来的趋势线跟波浪线一样,上下乱跳,找了好久才定位到是日期类型的问题。

3. 数据清洗与特征处理

3.1 用DataFrame摸清数据家底

数据拿到手之后,第一步不是急着建模,而是先做全面检查。我习惯用df.head()、df.info()、df.describe()三件套看数据长什么样。df.info()能看出每列有没有缺失值、数据类型对不对,df.describe()能看到温度、降水、风速这些数值列的分布,比如最大值是不是明显离谱。这一步能帮你提前发现数据问题,省得建模出来效果莫名其妙。

比如有一次我拉到的数据里出现了-999这样的填充值,明显是API缺失值的占位符。如果不清理,模型就会把-999当作真实温度样本,预测结果直接跑偏。这种情况就需要用np.where把-999替换成NaN,然后再统一做缺失值处理。

3.2 缺失值和异常值处理

缺失值处理的原则是:不能不做,但也不能乱做。对于温度这种连续变量,我一般用前后几天的均值填充,或者直接用ffill方法向上填充,因为温度在短期内变化是连续的,前一天和后一天的温度往往比较接近。如果你用整个列的平均值填充,遇到季节变化大的情况,反而会把数据搞失真。代码很简单:

df["temperature_2m_max"] = df["temperature_2m_max"].replace(-999, pd.NA) df["temperature_2m_max"] = df["temperature_2m_max"].ffill().bfill()

异常值的判断我倾向于先用可视化的方式看分布,比如画个箱线图,如果有温度在夏天出现-30度这种明显不合理的点,就直接剔除。判断标准可以结合实际地区的气候范围,不用搞太复杂的算法,毕竟天气数据质量整体还是比较靠谱的。

3.3 构造特征:让模型有更多线索可挖

很多新手忽略特征工程,直接把原始日期和温度丢给模型,效果自然很一般。我做的第一版模型也是这么干的,预测出来的结果基本是前一天数值的平移,完全体现不出变化趋势。后来我在特征里加入了星期、月份、季节、前一天的温差,甚至几日滑动平均,效果立刻不一样了。

构造特征的核心思路是:让模型能识别出周期性规律。比如月份这个特征,对模型来说是一月份还是六月份,温差差异非常明显。星期特征则能捕捉周中和周末的细微差异。代码上就是几行pandas的操作:

df["month"] = df["date"].dt.month df["day_of_week"] = df["date"].dt.dayofweek df["temp_diff"] = df["temperature_2m_max"] - df["temperature_2m_min"] df["prev_max"] = df["temperature_2m_max"].shift(1) df["rolling_avg_3"] = df["temperature_2m_max"].rolling(3).mean()

这里注意shift(1)生成的prev_max,在数据第一行会变成NaN,所以用完滞后特征之后需要再清洗一次缺失值。滚动均值是很好的平滑特征,能把噪声去掉,让模型学到的趋势更稳健。

4. 模型构建与预测评估

4.1 划分数据集:别让未来数据偷看答案

建模前最重要的一件事就是划分训练集和测试集,但天气数据跟普通表格数据不一样,它是有时间顺序的,不能随便随机打乱。如果随机切分,模型会看到未来的数据去预测过去的数据,准确率虚高,一到真正预测未来时就露馅。这个叫数据泄漏,是时序预测最常见的坑。

正确做法是按照时间顺序划分,比如前80%的时间段做训练,后20%做验证。我常这么写:

train_size = int(len(df) * 0.8) train_df = df.iloc[:train_size] test_df = df.iloc[train_size:]

还有一种更稳的方案是时序交叉验证,比如TimeSeriesSplit,不过对这个小项目来说,简单的时间切分已经够用了。特征列和标签列要分开定义,标签是你要预测的气温值,特征是你构造的那些维度。注意把date列从特征里去掉,模型不需要直接用日期字符串,日期只是用来对齐预测结果的。

4.2 多模型对比:不要只认准一个模型

我一开始只用线性回归,结果发现效果一般,毕竟温度和时间、其他特征之间的关系不完全是线性的。后来我同时跑了决策树、随机森林和支持向量回归,用同样的训练集和测试集对比,发现随机森林在这份数据上表现最好,线性回归次之,决策树单树容易过拟合。

具体实现用scikit-learn非常方便:

from sklearn.ensemble import RandomForestRegressor from sklearn.linear_model import LinearRegression from sklearn.svm import SVR models = { "linear": LinearRegression(), "rf": RandomForestRegressor(n_estimators=100, random_state=42), "svr": SVR(kernel="rbf") } for name, model in models.items(): model.fit(X_train, y_train) score = model.score(X_test, y_test) print(f"{name}: {score:.3f}")

scikit-learn的score()函数返回的是R平方分数,越接近1越好。不要只看这一个指标,我一般还看均方误差和平均绝对误差,因为R方在样本量小的时候容易欺骗人。平均绝对误差更直观,比如误差两度就是两度。

4.3 评估指标与简单调参思路

评价模型好坏不要只盯着测试集,还得看训练集和测试集差距。如果训练集上表现极好,测试集上却一塌糊涂,那就是过拟合。我调参的第一步不是网格搜索,而是先看模型对数据的拟合状态。比如决策树的高度限制一下,随机森林的树数量调整一下,往往就能改善。

调参可以用GridSearchCV,但数据量小的时候网格搜索很容易变成抖机灵,调出来的参数换了数据就不行。我更建议先做特征筛选,去掉无关特征,再去网格搜索范围设置小一点,比如随机森林只看50、100、150三档。简单来说,模型复杂度要跟数据量匹配,别拿几百条数据硬套一个高复杂度模型。

5. 可视化与结果呈现

5.1 matplotlib画预测对比图

模型训练完,最终要能直观看出预测和实际的对比。我用matplotlib画了两条折线,一条是真实温度,一条是预测温度。最基础画法其实很简单:

import matplotlib.pyplot as plt plt.figure(figsize=(12, 5)) plt.plot(test_df["date"], y_test, label="actual", linewidth=2) plt.plot(test_df["date"], y_pred, label="predicted", linestyle="--") plt.xlabel("date") plt.ylabel("temperature") plt.legend() plt.title("Temperature Forecast vs Actual") plt.tight_layout() plt.show()

这里有个非常常见的问题,就是横坐标日期太多、太密集,标签叠在一起变成一片黑,根本看不清。解决办法有两个,一是设置刻度间隔,只显示一部分日期;二是旋转刻度标签。我通常两个一起用,效果最好:

import matplotlib.dates as mdates plt.gca().xaxis.set_major_locator(mdates.DayLocator(interval=7)) plt.xticks(rotation=45)

图不能光给自己看,如果给不懂技术的朋友看,最好把中文字体处理好,不然默认字体显示不了中文,全是方块。Windows下可以用simhei,Linux下可能要装中文字体,这一步记得检查。

5.2 导出结果到Excel或CSV

预测结果不能只停留在内存里,我用to_csv()把预测值和真实值存到文件里,方便后续查看或直接发给相关同事。代码非常简单:

result = pd.DataFrame({ "date": test_df["date"], "actual_max_temp": y_test, "predicted_max_temp": y_pred }) result.to_csv("weather_forecast_result.csv", index=False, encoding="utf-8-sig")

这里有个小坑:Windows下的Excel打开CSV文件,如果不加utf-8-sig参数,中文文件名和内容会乱码。这个编码参数如果你不知道,十有八九会踩到。

5.3 用图表发现模型问题

画图还有一个重要用途是帮助调参。我跑完模型后,如果发现预测曲线整体比实际偏低,往往是因为模型学到的平均温度偏低,这时候考虑添加截距项或者检查是否有特征把平均值带偏了。如果预测曲线的波动比实际小很多,则说明模型平滑过头了,可以考虑增加滞后特征或者改用对突变更敏感的模型。多画几次图,你就能慢慢培养出"看线识病"的感觉。

6. 常见问题与排查技巧实录

6.1 环境配置阶段的高频坑

这个项目最常见的安装问题集中在numpy和scikit-learn的版本冲突上,尤其在Python 3.12以上版本,某些库还没完全适配。遇到这类问题,我给你的建议是第一看Python版本,第二看库的官方文档支持的版本范围。装库时不要一股脑装最新版,优先装已经验证过兼容的版本组合。

还有一个常见问题就是pip下载超时。除了换镜像源,还可以给pip加超时时间参数,比如--timeout=60。跑代码之前,在命令行先执行一下import pandas和import sklearn,能提前发现环境问题,别到时候把报错跟业务逻辑混在一起,排查起来头大。

6.2 数据与模型层面的经典错误

我在开发过程中整理了一个问题速查表,每次项目卡住时都会对照一下,效率特别高:

现象可能原因处理办法
预测曲线是一条水平线特征与标签关系没学出来,或特征全被丢弃检查特征列是否包含日期等无效字段,重新做特征工程
训练集评分很高,测试集评分很低过拟合,模型太复杂或数据量太少减少特征维度,限制模型深度,改用随机森林并调n_estimators
数据导入后全是NaNAPI返回字段名变了,或者JSON解析出问题打印原始JSON,确认字段名;用pd.json_normalize转成DataFrame
模型预测值整体偏高或偏低数据存在季节性偏差,或训练集和测试集分布差异大检查时间段是否跨季节,尽量让训练集包含完整周期
matplotlib图表中文乱码默认字体不支持中文设置中文字体,或确认系统安装过中文字体文件
日期横坐标重叠看不清数据量大、刻度过密用DayLocator设置间隔,再用rotation调整角度

6.3 排查流程与实操心得

当你拿到一个报错,不要急着搜解决方案,先做三件事:读最后一行报错信息,看是哪个模块抛出的;看报错有没有提示具体的文件名和行号,直接跳过去;打印出错位置附近的关键变量的shape和值。很多所谓的疑难杂症,其实都是DataFrame列名拼错了,或者多了一个空格导致列不存在。

我个人比较推荐在代码里多写assert语句,比如确认df没有空值、确认训练集和测试集长度符合预期。这些断言能在早期发现问题,而不是等到模型跑完才看到结果异常。项目到了后期,数据清洗的函数可能比自己预想的复杂,这时候写单元测试能帮你省下大把时间。别把单元测试当成负担,对一个小项目来说,几个简单的assert就够用了。

从实际效果来看,这套系统的预测准确度在短期气温趋势上能控制在误差两度左右,应对日常活动安排、出行参考已经足够。如果你后续想继续扩展,建议往这几个方向走:一是接入实时预报接口做滚动预测,二是用Prophet或LightGBM这类更强的时间序列工具替换现在的模型,三是把预测结果做进一个简单的Flask网页服务,让朋友输入城市名就能看到预测曲线。我这个项目的代码整体没有特别华丽的技巧,但每一步都是踏踏实实踩出来的,希望这份复盘能帮你节省一些自己摸索的时间。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/5 7:55:09

压缩感知重构的梯度投影算法:原理、Matlab实现与调参实践

压缩感知这两年从论文走向工程落地的速度比我预想的快不少,特别是图像重构和雷达成像这类对采样资源敏感的场景,很多人开始把目光从经典的正交匹配追踪挪到稀疏重构优化算法上。而梯度投影(Gradient Projection)这套思路&#xff…

作者头像 李华
网站建设 2026/10/5 7:53:30

插件系统详解:从加载机制到failed to load plugins排查实战

搞了十多年软件,我越来越觉得 plugins 这类扩展机制是软件工程里最容易被低估的设计。你随手打开一个稍微有点深度的工具——嵌入式 IDE、CI/CD 平台、开源音乐播放器——背后都有一堆插件在默默干活。但插件又是典型的“不出事没人夸,一出事全网求人”的…

作者头像 李华
网站建设 2026/10/5 7:53:26

Flutter适配OpenHarmony:API测试工具开发实战与排障指南

做 OpenHarmony 上的 Flutter 应用,最容易被低估的其实是“HTTP 层”——大家一上来就盯着 UI、动画、组件树,真正一联调,卡在 API 测试上的时间比写界面还多。我最近把一个内部工具改造成了支持 OpenHarmony 的 Web 开发助手 App&#xff0c…

作者头像 李华
网站建设 2026/10/5 7:52:38

OpenShell 完整使用笔记:让 Windows 11 回归经典开始菜单和高效操作

最近帮朋友重装电脑,Windows 11 更新完毕后,他第一句话是:能不能把开始菜单弄回以前那种。我打开浏览器、下载 OpenShell、安装、改了两个选项,十秒钟后桌面左下角弹出的菜单干净得像 Windows 7。这种需求我太熟了。对于一个从 Wi…

作者头像 李华
网站建设 2026/10/5 7:51:48

改进粒子群算法求解建筑光储系统规划运行综合优化:Python复现实践

最近在复现一篇EI检索的论文,题目翻译过来是《基于改进粒子群算法求解的建筑集成光储系统规划运行综合优化方法》。原论文的思路很清晰:把屋顶光伏、储能电池和建筑负荷揉成一个优化问题,用改进粒子群算法在两个层面同时寻优,既决…

作者头像 李华