资讯动态

Python天气数据分析系统:API抓取、清洗与Plotly可视化

发布时间:2026/9/11 23:00:11 来源:尧图企业网站定制
简介本资源是一套完整的Python课程设计项目源码面向计算机专业本科生及数据可视化初学者聚焦天气数据的采集、建模与可视化分析全流程实践。项目基于机器学习实现天气预测并集成多维度图表展示如温度趋势、降水分布、风速热力图等适合作为数据可视化分析期末大作业或课程设计参考方案。压缩包共24个文件含4个核心Python脚本main.py、GetData.py、ProcessData.py等、4个CSV数据集train/test/valid及中国今日天气、12张可视化结果JPG图、1个HTML交互页面、1个训练好的Model.pkl模型文件及README说明文档整体仅1.42MB轻量易部署。已有1487人学习下载提供从原始数据获取、特征工程、模型训练到结果可视化的完整闭环代码结构清晰、注释充分且经导师指导获97分高分具备较强教学示范性与工程复用价值。1. 这不是“画个折线图就交差”的天气项目一个能跑通真实API、自动清洗异常值、按城市/时段分层聚合、并支持交互式导出的Python可视化分析系统你手头可能正压着一份名为weather_analysis_system.zip的压缩包解压后看到main.py、config.yaml和requirements.txt——但直接python main.py却报错ModuleNotFoundError: No module named plotly或者数据拉下来全是None或-999。这不是代码写错了而是天气数据可视化分析系统天然具备的三重门槛数据源不可靠性API限频、字段缺失、单位混杂、时空维度复杂性经纬度网格 vs 行政区划、UTC时间戳 vs 本地时区、逐小时 vs 日均值、分析目标模糊性是看某市未来7天温度趋势还是对比长三角16城过去30年降水极值。本系统不是教学Demo它默认对接中国气象数据网CMA公开接口或OpenWeatherMap国际源内置空值插补策略时间序列线性填充空间邻近加权、单位统一引擎℃/℉/K 自动识别与转换、以及基于Pandas MultiIndex的多维切片器——这意味着你改一行配置就能从“北京2023年逐日最高温”切换到“华东地区2020–2024年梅雨期降水量箱线图”。适合需要交付可复用分析模块的数据工程师、气象方向研究生以及要快速搭建部门级天气看板的运维/产品团队。2. 为什么选Requests Pandas Plotly组合避开Matplotlib硬编码陷阱用声明式语法定义时空分析逻辑2.1 天气数据的特殊结构决定技术栈必须分层解耦天气数据不是标准CSV表格原始响应常为嵌套JSON如OpenWeatherMap返回{city:{name:Beijing,coord:{lat:39.9,lon:116.4}},data:[{dt:1712345678,main:{temp:12.5,humidity:65},weather:[{main:Clear}]}]}其中data数组含数百条时间序列每条含main、weather、wind等子对象。若用纯Matplotlib逐点绘制需手动展开三层嵌套、处理时间戳转换、拼接城市名字段——代码易错且无法复用。而Pandas DataFrame天然支持json_normalize()扁平化嵌套结构并通过set_index([city, dt])构建双索引使df.loc[(Shanghai, slice(2024-01-01, 2024-01-07)), temp]一句即可切片。Plotly则提供px.line(df, xdt, ytemp, colorcity)声明式绘图无需管理Figure对象生命周期。这种组合让分析逻辑聚焦在“我要什么数据”而非“怎么把数据塞进图表”。提示不要用matplotlib.pyplot.plot()硬编码坐标轴——当需求变为“叠加历史同期均值虚线”时Matplotlib需重写ax.plot()和ax.axhline()而Plotly只需追加fig.add_hline(ydf[temp].mean(), line_dashdot)。2.2 实现最小可运行天气抓取与解析5行代码验证API连通性import requests import pandas as pd from datetime import datetime # 替换为你的API KeyOpenWeatherMap免费版限60次/分钟 API_KEY your_api_key_here CITY_ID 1816670 # 上海City ID url fhttp://api.openweathermap.org/data/2.5/forecast?id{CITY_ID}appid{API_KEY}unitsmetric response requests.get(url) data response.json() # 验证关键字段存在性避免后续KeyError assert list in data, API响应缺少list字段请检查API Key或网络 assert len(data[list]) 0, 未获取到预报数据请检查城市ID # 提取核心字段并转为DataFrame df pd.json_normalize( data[list], sep_ ).rename(columns{ main_temp: temp_c, main_humidity: humidity_pct, wind_speed: wind_mps, dt: timestamp_utc }) print(f成功获取{len(df)}条预报记录时间范围{datetime.fromtimestamp(df[timestamp_utc].min())} ~ {datetime.fromtimestamp(df[timestamp_utc].max())})这段代码输出应类似成功获取40条预报记录时间范围2024-04-05 12:00:00 ~ 2024-04-09 09:00:00关键点在于pd.json_normalize()自动展开list数组sep_将main.temp转为main_temp列名避免后续访问data[main][temp]引发的嵌套键错误。若报错requests.exceptions.ConnectionError说明网络或API域名问题若data[list]为空需确认CITY_ID是否有效可通过https://openweathermap.org/find 搜索城市获取ID。2.3 数据清洗必须前置用Pandas链式操作处理常见天气数据脏点天气API返回的脏数据有三类典型模式温度异常值temp_c出现-273.15绝对零度或1000传感器故障湿度越界humidity_pct超出0–100范围时间重复同一timestamp_utc出现多条记录API缓存或重试导致以下清洗逻辑封装为可复用函数def clean_weather_data(df: pd.DataFrame) - pd.DataFrame: 清洗天气DataFrame返回无异常值、时间去重、单位标准化的版本 # 步骤1过滤明显异常温度-50℃ ~ 60℃为合理区间 df df[(df[temp_c] -50) (df[temp_c] 60)].copy() # 步骤2修正湿度越界值截断至0-100 df[humidity_pct] df[humidity_pct].clip(lower0, upper100) # 步骤3去重时间戳保留第一条因后续预报数据通常按时间排序 df df.drop_duplicates(subset[timestamp_utc], keepfirst) # 步骤4添加本地时间列上海UTC8 df[timestamp_local] pd.to_datetime(df[timestamp_utc], units).dt.tz_localize(UTC).dt.tz_convert(Asia/Shanghai) # 步骤5添加日期和小时字段便于分组 df[date] df[timestamp_local].dt.date df[hour] df[timestamp_local].dt.hour return df # 应用清洗 clean_df clean_weather_data(df) print(f清洗后剩余{len(clean_df)}条记录温度范围{clean_df[temp_c].min():.1f}℃ ~ {clean_df[temp_c].max():.1f}℃)参数说明clip(lower0, upper100)比np.where()更高效且自动处理NaNtz_localize(UTC).tz_convert(Asia/Shanghai)确保时区转换正确避免8硬编码导致夏令时错误drop_duplicates(keepfirst)针对OpenWeatherMap偶发的重复响应若需保留最新值则设keeplast执行后输出应显示清洗前后记录数对比例如清洗后剩余38条记录证明脏数据已被剔除。3. 构建可配置的时空分析管道从单城市趋势到跨区域对比的4种核心视图3.1 基于YAML的配置驱动分析逻辑修改config.yaml即可切换分析维度系统通过config.yaml定义分析行为避免硬编码城市名或时间范围。典型配置如下# config.yaml data_source: api: openweathermap # 支持 openweathermap / cma中国气象数据网 api_key: your_api_key_here cities: - name: Beijing id: 1816670 - name: Shanghai id: 1816670 - name: Guangzhou id: 1816670 time_range: start_date: 2024-04-01 end_date: 2024-04-07 interval: hourly # hourly / daily analysis: aggregation: mean # mean / max / min / sum对daily适用 metrics: [temp_c, humidity_pct, wind_mps] export: format: html # html / png / json output_dir: ./output加载配置的Python代码import yaml def load_config(config_path: str config.yaml) - dict: 加载YAML配置返回字典 with open(config_path, r, encodingutf-8) as f: return yaml.safe_load(f) config load_config() print(f配置加载成功分析{len(config[cities])}个城市时间范围{config[time_range][start_date]}至{config[time_range][end_date]})此设计使非开发人员如气象分析师仅需编辑YAML即可调整分析范围无需触碰Python代码。若新增城市只需在cities列表中添加name和id若需改为日均值分析将interval改为daily并确保aggregation设为mean。3.2 实现城市温度趋势对比图Plotly动态图例与悬停提示使用清洗后的数据生成多城市温度对比图关键在于px.line()的color参数绑定城市名import plotly.express as px # 假设clean_df已包含多个城市的记录且有city_name列标识来源 fig px.line( clean_df, xtimestamp_local, ytemp_c, colorcity_name, # 自动为每个城市生成不同颜色曲线 titlef{config[cities][0][name]}等{len(config[cities])}城温度趋势对比, labels{timestamp_local: 时间本地, temp_c: 温度℃}, markersTrue, # 显示数据点 line_shapespline # 平滑曲线更符合气象变化特征 ) # 增强交互悬停显示完整时间戳和湿度 fig.update_traces( hovertemplateb%{x}/bbr温度: %{y:.1f}℃br湿度: %{customdata[0]:.0f}%extra/extra, customdataclean_df[[humidity_pct]] ) # 导出为HTML支持缩放、下载PNG fig.write_html(f{config[export][output_dir]}/temperature_comparison.html) print(f温度对比图已保存至 {config[export][output_dir]}/temperature_comparison.html)注意hovertemplate中%{x}自动格式化时间为本地时区字符串%{customdata[0]}引用传入的湿度列extra/extra隐藏默认的trace名。若发现图例文字重叠可在fig.update_layout(legenddict(orientationh, yanchorbottom, y1.02, xanchorright, x1))中调整。3.3 构建区域降水热力图用Plotly Heatmap展示时空分布密度当分析目标转向“某区域降雨强度时空分布”需将时间序列转为二维矩阵行日期列小时值降水量。OpenWeatherMap不直接提供降水但可通过weather.main字段统计“Rain”出现频次# 统计每小时“Rain”出现次数模拟降水强度 rain_count clean_df.groupby([date, hour]).apply( lambda x: (x[weather_0_main] Rain).sum() ).unstack(fill_value0) # 转为date×hour矩阵 # 生成热力图 fig_heat px.imshow( rain_count, labels{x: 小时, y: 日期, color: Rain次数}, title区域降雨频次热力图按小时统计, aspectauto ) fig_heat.write_html(f{config[export][output_dir]}/rain_heatmap.html)此处unstack(fill_value0)确保缺失小时如某日无0点数据填充为0避免热力图出现空白列。aspectauto防止长宽比失真——当日期跨度大时自动拉伸高度以保证可读性。3.4 生成城市气候箱线图用Plotly Box比较多年温度离散度若需评估城市间气候差异箱线图比折线图更直观。假设已有2020–2024年每日最高温数据daily_max_temp.csv# 加载多年日最高温数据示例结构date, city, max_temp_c daily_data pd.read_csv(data/daily_max_temp.csv, parse_dates[date]) daily_data[year] daily_data[date].dt.year # 按城市和年份分组计算箱线图所需统计量 box_data daily_data.groupby([city, year])[max_temp_c].agg( [min, q1, median, q3, max, count] ).reset_index() # Plotly箱线图需将数据转为长格式 fig_box px.box( daily_data, xcity, ymax_temp_c, colorcity, pointsoutliers, # 显示离群值 title各城市历年日最高温分布箱线图 ) fig_box.update_yaxes(title_text日最高温℃) fig_box.write_html(f{config[export][output_dir]}/city_climate_box.html)关键点pointsoutliers保留极端高温/低温点便于识别异常年份如2022年长江流域极端高温colorcity自动匹配图例颜色与趋势图保持视觉一致性。4. 解决高频落地问题时区错乱、API限频、中文标签乱码的3个硬核修复方案4.1 时区转换失效用pytz显式绑定时区而非简单8常见错误pd.to_datetime(df[timestamp_utc], units) pd.Timedelta(hours8)问题未考虑夏令时如欧洲城市且8在跨年时可能出错。正确做法from pytz import timezone def convert_to_local_time(timestamp_series: pd.Series, tz_name: str Asia/Shanghai) - pd.Series: 安全转换UTC时间戳为指定时区本地时间 utc_tz timezone(UTC) local_tz timezone(tz_name) return pd.to_datetime(timestamp_series, units, utcTrue).dt.tz_convert(local_tz) # 应用 clean_df[timestamp_local] convert_to_local_time(clean_df[timestamp_utc])pd.to_datetime(..., utcTrue)强制将时间戳解释为UTCdt.tz_convert()调用pytz数据库处理夏令时规则。测试方法取timestamp_utc16094592002021-01-01 00:00:00 UTC转换后应为2021-01-01 08:00:00 CST非08:00因CST是标准时间缩写。4.2 API请求被限频用tenacity实现指数退避重试OpenWeatherMap免费版限60次/分钟连续请求易触发429 Too Many Requests。tenacity库提供优雅重试from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception_type import time retry( stopstop_after_attempt(3), # 最多重试3次 waitwait_exponential(multiplier1, min2, max10), # 初始等待2s指数增长至10s retryretry_if_exception_type(requests.exceptions.RequestException) ) def fetch_weather_data(city_id: str) - dict: url fhttp://api.openweathermap.org/data/2.5/forecast?id{city_id}appid{API_KEY}unitsmetric response requests.get(url, timeout10) response.raise_for_status() # 抛出HTTP异常 return response.json() # 使用 try: data fetch_weather_data(1816670) except Exception as e: print(fAPI请求失败已重试3次仍失败{e})wait_exponential确保第二次重试等待2*24s第三次4*28s避免雪崩式请求。timeout10防止网络卡死阻塞主线程。4.3 中文图表标题乱码强制指定Noto Sans CJK字体Plotly默认字体不支持中文导致标题显示方块。解决方案import plotly.io as pio # 设置全局字体需系统安装Noto Sans CJKLinux/macOS通常预装Windows需手动下载 pio.templates[custom] pio.templates[plotly] pio.templates[custom].layout.font.family Noto Sans CJK SC, sans-serif pio.templates[custom].layout.title.font.size 18 # 应用模板 fig px.line(clean_df, xtimestamp_local, ytemp_c, title上海温度趋势图) fig.update_layout(templatecustom) # 强制使用中文字体 fig.write_html(shanghai_temp.html)验证方法打开生成的HTML检查标题是否正常显示“上海温度趋势图”。若仍乱码需确认系统字体路径——Linux执行fc-list :langzhmacOS检查/System/Library/Fonts/Windows下载 Noto Sans CJK 并安装。5. 进阶技巧用Plotly Dash构建可交互的天气分析仪表盘支持实时筛选与导出5.1 构建基础Dash应用框架3个核心组件定义交互逻辑Dash将Plotly图表封装为Web组件用户可通过下拉框、滑块实时筛选数据。最小可行仪表盘代码import dash from dash import dcc, html, Input, Output, callback import plotly.express as px # 初始化Dash应用 app dash.Dash(__name__) # 布局包含城市选择下拉框、日期范围滑块、图表容器 app.layout html.Div([ html.H1(天气数据分析仪表盘), html.Div([ html.Label(选择城市), dcc.Dropdown( idcity-dropdown, options[{label: c[name], value: c[name]} for c in config[cities]], valueconfig[cities][0][name] ), html.Label(选择日期范围), dcc.DatePickerRange( iddate-range, start_dateconfig[time_range][start_date], end_dateconfig[time_range][end_date] ) ]), dcc.Graph(idweather-graph) ]) # 回调函数当用户操作控件时更新图表 callback( Output(weather-graph, figure), Input(city-dropdown, value), Input(date-range, start_date), Input(date-range, end_date) ) def update_graph(selected_city, start_date, end_date): # 筛选对应城市和日期的数据 filtered_df clean_df[ (clean_df[city_name] selected_city) (clean_df[date] pd.to_datetime(start_date).date()) (clean_df[date] pd.to_datetime(end_date).date()) ] # 生成温度折线图 fig px.line( filtered_df, xtimestamp_local, ytemp_c, titlef{selected_city}温度趋势{start_date} 至 {end_date} ) fig.update_layout(templatecustom) # 复用中文字体 return fig # 启动服务调试模式 if __name__ __main__: app.run_server(debugTrue, host0.0.0.0, port8050)启动后访问http://localhost:8050即可看到带下拉框和日期选择器的网页界面。callback装饰器将UI控件与图表逻辑绑定Input监听用户操作Output更新图表——这是Dash的核心范式。5.2 添加一键导出功能用dcc.Download组件实现前端触发文件下载用户常需将当前视图导出为PNG或CSV。Dash提供dcc.Download组件# 在app.layout中添加下载按钮和Download组件 app.layout html.Div([ # ... 原有布局 ... html.Button(导出当前图表为PNG, idbtn-png), html.Button(导出数据为CSV, idbtn-csv), dcc.Download(iddownload-data) ]) # 定义导出回调 callback( Output(download-data, data), Input(btn-png, n_clicks), Input(btn-csv, n_clicks), prevent_initial_callTrue ) def download_file(png_clicks, csv_clicks): # 判断哪个按钮被点击 ctx dash.callback_context if not ctx.triggered: return None button_id ctx.triggered[0][prop_id].split(.)[0] if button_id btn-png: # 截图当前图表需前端js支持此处简化为占位 return dict(contentPNG截图功能需集成browser-shot库, filenameweather_chart.png) elif button_id btn-csv: # 导出筛选后的数据 filtered_df clean_df.head(100) # 实际应复用回调中的筛选逻辑 return dcc.send_data_frame(filtered_df.to_csv, weather_data.csv)dcc.send_data_frame()自动生成CSV并触发浏览器下载prevent_initial_callTrue避免页面加载时自动下载。生产环境需集成browser-shot实现真正的图表截图。5.3 性能优化用Redis缓存API响应降低重复请求开销当多人同时访问仪表盘频繁调用API会快速耗尽配额。用Redis缓存响应import redis import json # 初始化Redis连接需提前启动redis-server cache redis.Redis(hostlocalhost, port6379, db0, decode_responsesTrue) def get_cached_weather(city_id: str) - dict: 从Redis获取缓存天气数据未命中则请求API并缓存 cache_key fweather:{city_id} cached cache.get(cache_key) if cached: print(f从Redis缓存获取{city_id}) return json.loads(cached) # 缓存未命中请求API data fetch_weather_data(city_id) # 复用前述tenacity重试函数 cache.setex(cache_key, 3600, json.dumps(data)) # 缓存1小时 print(fAPI请求{city_id}并写入Redis) return data # 在Dash回调中使用 callback(Output(weather-graph, figure), Input(city-dropdown, value)) def update_graph_with_cache(selected_city): # 根据城市名映射到ID需维护city_name→city_id映射表 city_map {c[name]: c[id] for c in config[cities]} data get_cached_weather(city_map[selected_city]) # ... 后续解析与绘图逻辑cache.setex()设置过期时间3600秒避免缓存永久占用内存。首次访问某城市时触发API请求后续1小时内相同请求直接从Redis读取响应时间从秒级降至毫秒级。Redis缓存键TTL秒用途weather:18166703600单城市预报数据1小时更新一次config:latest86400YAML配置文件内容1天更新一次agg:beijing_daily_20241800北京日均值聚合结果30分钟更新缓存策略依据数据时效性设定预报数据1小时足够配置文件可缓存1天而实时监控类数据TTL应设为60秒以内。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价