天气预报毕业设计全链路实战:从数据采集清洗到预测可视化

发布时间:2026/10/13 15:58:20
天气预报毕业设计全链路实战:从数据采集清洗到预测可视化
简介这份天气预报毕业设计源码包面向计算机相关专业学生与入门开发者提供一套可直接参考的完整项目实现帮助理解天气预报系统从数据获取、处理分析到界面展示的整体构建流程。压缩包共123个文件约6.18MB以62个png界面素材、29个class编译文件、11个java源码、9个xml布局配置为主另含apk安装包、dex、db数据库及properties等资源覆盖Android客户端与后端逻辑的典型结构。项目涉及气象数据接口调用、数据清洗与统计分析、GIS地图可视化、前后端交互及数据库存储等知识点并包含城市切换、天气组件、列表适配与数据库操作等模块适合作为课程设计或毕业设计的实践案例。目前已有654人学习下载读者可据此梳理项目目录结构、理解各模块职责划分并在此基础上进行功能扩展与二次开发。1. 天气预报毕业设计从数据拉取到可视化大屏的完整落地路径很多同学拿到“天气预报毕业设计”这个题目第一反应是去网上找一个现成的天气 API调通之后把温度、湿度、风力往页面上一贴就觉得大功告成。但真正到了答辩现场老师问一句“你的数据从哪来、中间怎么处理、预测逻辑在哪”立刻就卡住了。天气预报毕业设计真正要解决的不是“显示天气”而是数据采集、清洗、存储、预测、可视化这条完整链路能不能跑通以及你能不能讲清楚每个环节为什么这么选。它适合有一定编程基础、想做数据类或 Web 类毕设的本科生也适合想用一个小项目把前后端和数据处理串起来的开发者。下面我按实际做过的路径把这件事拆开讲。2. 数据源选型与采集免费接口怎么挑、怎么拉、怎么存做天气预报毕业设计第一步不是写代码而是确定数据从哪来。常见做法有三类公开气象数据接口、网页结构化抓取、本地历史数据集。对于毕设来说最稳妥的是公开气象数据接口 本地历史数据备份的组合既能保证实时性又能在断网或接口限流时有数据可用。2.1 三类数据源的对比与选择理由先看一张对比表把选型逻辑说清楚数据源类型获取难度数据稳定性适合场景主要风险公开气象接口低较高实时天气展示、短期预报免费额度有限、字段不统一网页结构化抓取中一般补充历史数据、特定城市页面改版导致解析失效本地历史数据集低高模型训练、趋势分析时效性差、需要自行清洗我一般会建议实时展示用接口模型训练用本地数据集。接口负责“现在怎么样”数据集负责“过去怎么样”两者结合才能支撑一个像样的毕业设计。选接口时重点看三个指标返回字段是否包含温度、湿度、风速、天气状况是否支持按城市或经纬度查询免费额度是否够你每天定时拉取。不要一上来就追求“全球覆盖”毕设能把一个城市或几个城市做透比铺一堆城市但每个都只有温度要有说服力得多。2.2 用 Python 拉取天气数据并落库的最小实现下面是一个可复现的采集脚本用requests拉数据用sqlite3落库。代码里做了重试和字段映射避免接口偶尔抖动导致数据断档。import requests import sqlite3 import time from datetime import datetime # 配置区替换成你实际使用的接口地址和参数 API_URL https://example-weather-api.com/current CITY beijing API_KEY your_api_key_here # 建表字段按你接口实际返回调整 def init_db(): conn sqlite3.connect(weather.db) cur conn.cursor() cur.execute( CREATE TABLE IF NOT EXISTS weather_records ( id INTEGER PRIMARY KEY AUTOINCREMENT, city TEXT NOT NULL, temperature REAL, humidity REAL, wind_speed REAL, condition TEXT, record_time TEXT NOT NULL ) ) conn.commit() return conn # 带重试的请求最多试 3 次间隔 2 秒 def fetch_weather(city, retries3): params {city: city, key: API_KEY} for i in range(retries): try: resp requests.get(API_URL, paramsparams, timeout10) resp.raise_for_status() data resp.json() # 字段映射不同接口字段名不一样这里统一成自己的命名 return { temperature: data.get(temp), humidity: data.get(humidity), wind_speed: data.get(windSpeed), condition: data.get(weather), } except Exception as e: print(f第 {i1} 次请求失败: {e}) time.sleep(2) return None # 写入数据库 def save_record(conn, city, weather): if not weather: return cur conn.cursor() cur.execute( INSERT INTO weather_records (city, temperature, humidity, wind_speed, condition, record_time) VALUES (?, ?, ?, ?, ?, ?) , ( city, weather[temperature], weather[humidity], weather[wind_speed], weather[condition], datetime.now().strftime(%Y-%m-%d %H:%M:%S) )) conn.commit() if __name__ __main__: conn init_db() weather fetch_weather(CITY) save_record(conn, CITY, weather) conn.close() print(采集完成)这段代码的逻辑很直白先建表再请求最后写入。关键参数有三个timeout10防止请求卡死retries3应对偶发失败record_time用本地时间而不是接口时间方便后续按采集时间做聚合。字段映射那一步不要省不同接口的字段名差异很大统一成自己的命名后后面换接口只需要改映射不用动数据库和前端。2.3 定时任务与数据断档的处理单次采集没有意义天气预报毕业设计需要的是连续时间序列。常见做法是用crontab或APScheduler每小时拉一次。我一般会在脚本里加一个“补采”逻辑如果发现数据库里最近一条记录超过 2 小时就手动触发一次补采避免因为机器重启或网络波动导致曲线中间缺一块。提示免费接口通常有每日调用上限定时任务不要设得太密每小时一次足够毕设使用。拉取频率过高反而容易被限流导致关键时段数据丢失。3. 数据清洗与存储把“脏数据”变成能画曲线的表采集回来的数据很少能直接用来画图。温度字段可能是字符串湿度可能带百分号风速单位可能不统一甚至偶尔返回空值。这一章的核心目标是把原始数据整理成时间对齐、单位统一、缺失可控的结构化表。3.1 常见脏数据类型与清洗规则我遇到过几类典型问题温度返回26℃而不是26湿度返回65%风速有的接口用m/s有的用km/h时间戳有的是秒级有的是毫秒级。清洗规则可以固定成下面这张表问题类型原始示例清洗方式目标格式带单位数值26℃正则提取数字26.0百分比65%去掉%后转浮点65.0风速单位不统一12 km/h统一换算成m/s3.33时间戳精度不一致1710000000000判断位数后除以 1000秒级时间戳空值或异常值null/-999标记为缺失不直接删除None清洗不是一次性的建议写成独立函数每次采集后立即执行。这样数据库里存的永远是干净数据前端和模型都不用再操心格式问题。3.2 用 Pandas 做批量清洗与缺失值处理下面这段代码演示如何把原始记录读出来、清洗、再写回一张新表。重点看缺失值的处理策略不删除而是标记并插值因为毕设数据量本来就不大删一条少一条。import pandas as pd import sqlite3 import numpy as np conn sqlite3.connect(weather.db) # 读取原始记录 df pd.read_sql(SELECT * FROM weather_records, conn) # 清洗温度去掉非数字字符后转浮点 df[temperature] df[temperature].astype(str).str.extract(r(-?\d\.?\d*))[0] df[temperature] pd.to_numeric(df[temperature], errorscoerce) # 清洗湿度去掉百分号 df[humidity] df[humidity].astype(str).str.replace(%, , regexFalse) df[humidity] pd.to_numeric(df[humidity], errorscoerce) # 风速统一假设原始有 km/h 和 m/s 混用这里按数值大小粗略判断 def normalize_wind(val): try: v float(val) # 大于 30 的按 km/h 处理换算成 m/s return v / 3.6 if v 30 else v except: return np.nan df[wind_speed] df[wind_speed].apply(normalize_wind) # 时间列转 datetime 并设为索引 df[record_time] pd.to_datetime(df[record_time]) df df.sort_values(record_time).set_index(record_time) # 缺失值线性插值最多补 3 个点 df[temperature] df[temperature].interpolate(methodlinear, limit3) df[humidity] df[humidity].interpolate(methodlinear, limit3) # 写回新表 df.reset_index().to_sql(weather_clean, conn, if_existsreplace, indexFalse) conn.close() print(清洗完成记录数, len(df))逻辑说明str.extract用正则把数字抠出来errorscoerce把无法转换的值变成NaN而不是报错。风速归一化那里用了一个粗略阈值实际项目中最好在采集阶段就统一单位清洗阶段只做兜底。interpolate的limit3表示最多连续补 3 个缺失点超过就保留NaN避免用插值掩盖长时间断档。3.3 存储方案SQLite 够不够用对于单机毕设SQLite 完全够用零配置、单文件、方便打包。如果要做多城市高频采集可以换成 MySQL 或 PostgreSQL但对毕设来说没必要。我一般会保留两张表weather_records存原始数据weather_clean存清洗后数据。这样即使清洗逻辑写错了原始数据还在有后悔药可吃。注意SQLite 在并发写入时容易锁库定时采集脚本不要同时开多个进程写同一张表。如果确实需要并发加一个简单的文件锁或改用消息队列。4. 预测模型与可视化让毕设从“展示”变成“分析”如果只做数据展示这个毕设的深度是不够的。加一个简单的预测模块哪怕只是线性回归或移动平均也能让整个项目从“天气查询页面”升级成“天气分析系统”。这一章讲怎么用历史数据做短期温度预测以及怎么把结果可视化出来。4.1 用移动平均和线性回归做基线预测不要一上来就上 LSTM毕设时间有限数据量也未必支撑复杂模型。我一般会先做两个基线7 天移动平均和线性回归。移动平均用来平滑短期波动线性回归用来捕捉趋势。两者对比着看答辩时也能讲清楚“为什么选这个模型”。import pandas as pd import numpy as np from sklearn.linear_model import LinearRegression import sqlite3 conn sqlite3.connect(weather.db) df pd.read_sql(SELECT * FROM weather_clean, conn, parse_dates[record_time]) df df.set_index(record_time).sort_index() # 按小时重采样取均值保证时间间隔均匀 hourly df[temperature].resample(1h).mean().interpolate(limit3) # 移动平均窗口 24 小时 hourly_ma hourly.rolling(window24, min_periods1).mean() # 线性回归用过去 72 小时预测下一小时 def make_features(series, lag72): X, y [], [] for i in range(lag, len(series)): X.append(series[i-lag:i]) y.append(series[i]) return np.array(X), np.array(y) values hourly.dropna().values if len(values) 100: X, y make_features(values, lag72) model LinearRegression() model.fit(X, y) # 用最后 72 小时预测下一小时 last_window values[-72:].reshape(1, -1) next_temp model.predict(last_window)[0] print(f下一小时预测温度{next_temp:.2f}) else: print(数据量不足先积累几天再跑预测) conn.close()参数说明resample(1h)把不规则采集时间对齐到整点interpolate(limit3)补短缺口。lag72表示用过去 72 小时预测下一小时这个窗口可以根据数据量调整数据少就降到 24。线性回归的输入是 72 维输出是 1 维训练前记得检查values长度不够就跳过预测避免报错。4.2 用 ECharts 做温度趋势与预测对比图可视化部分前端用 ECharts 最省事。把历史温度、移动平均线、预测点三条线画在同一张图上一眼就能看出模型有没有跟上趋势。数据结构建议后端返回 JSON前端直接setOption。// 假设从后端拿到三个数组times, history, forecast const option { title: { text: 温度趋势与预测 }, tooltip: { trigger: axis }, xAxis: { type: category, data: times }, yAxis: { type: value, name: 温度(℃) }, series: [ { name: 历史温度, type: line, data: history, smooth: true, lineStyle: { width: 2 } }, { name: 移动平均, type: line, data: movingAvg, smooth: true, lineStyle: { type: dashed } }, { name: 预测值, type: scatter, data: forecastPoints, symbolSize: 10, itemStyle: { color: #c23531 } } ] };这段配置的关键是smooth: true让曲线不那么锯齿type: scatter把预测点单独标出来避免和连续曲线混淆。后端返回数据时时间轴要对齐预测点的时间戳要放在历史数据之后否则图上会重叠。4.3 把预测结果写回数据库并做误差记录预测不是跑一次就完事每次采集新数据后都应该重新预测并把预测值和实际值都存下来方便后面算误差。我一般会建一张prediction_log表字段包括预测时间、预测温度、实际温度、绝对误差。这样答辩时可以直接展示“我的模型平均误差是多少”比空口说“效果不错”有说服力得多。提示误差记录不要只存最终结果把每次预测的输入窗口长度、模型参数也存下来。后面调参时能对比不同配置的效果不用靠记忆。5. 避坑与排查天气预报毕业设计里最容易翻车的 5 个点这一章是我自己踩过的坑也是带别人做毕设时见过最多的问题。每条按“现象 → 原因 → 解决”写方便你直接对照排查。现象一接口突然返回 401 或 403。原因通常是 API Key 过期、调用次数超限或者请求头里少了必要的字段。解决方式是先在浏览器或 Postman 里单独测一次接口确认 Key 有效然后在代码里加异常捕获把失败请求的响应体打印出来不要只打印“请求失败”。现象二数据库里时间字段乱序画图时曲线来回跳。原因是采集脚本写入时用了本地时间但不同机器时区不一致或者手动补采时插入了旧时间戳。解决方式是在写入前统一转成 UTC 时间戳存储展示时再转回本地时区查询时强制ORDER BY record_time ASC。现象三清洗后的数据比原始数据少了一大半。原因是pd.to_numeric(errorscoerce)把大量非数字字段变成了NaN后续dropna()直接删掉了。解决方式是先统计每列缺失比例超过 30% 的列要考虑换数据源或改解析规则不要盲目删行。缺失值优先插值插不了的再标记最后才考虑删除。现象四预测结果是一条直线完全不跟着温度变化。原因是线性回归的输入窗口太长而数据量太少模型欠拟合。解决方式是缩短lag到 24 或 12或者改用移动平均作为预测基线。如果数据量确实不够就先不做预测把展示和清洗做扎实。现象五前端图表加载慢页面卡顿。原因是后端一次性返回了全量历史数据几千个点直接塞给 ECharts。解决方式是在后端做分页或按时间范围聚合比如只返回最近 7 天的数据或者按小时聚合后再返回。前端也可以开启dataZoom让用户自己选择时间范围。注意免费接口的字段和返回结构可能随时调整不要把解析逻辑写死在代码里。把字段映射抽成配置字典接口变了只改配置不改主流程。6. 进阶技巧用 Docker 打包与自动化验证如果你想让这个天气预报毕业设计在答辩时更有竞争力我建议做两件事用 Docker 把采集、清洗、预测串成一条流水线以及加一个自动化验证脚本每次跑完自动检查数据完整性和预测误差。这两件事花不了太多时间但能让老师看到你具备工程化思维。6.1 用 Docker Compose 编排采集与预测任务不需要把每个步骤都做成独立服务一个docker-compose.yml加两个容器就够了一个跑定时采集一个跑预测和验证。数据库用挂载卷持久化避免容器重启后数据丢失。version: 3.8 services: collector: build: ./collector volumes: - ./data:/app/data restart: unless-stopped command: python collect.py --interval 3600 predictor: build: ./predictor volumes: - ./data:/app/data depends_on: - collector command: python predict.py --checkcollector容器每小时跑一次采集predictor容器依赖采集完成后跑预测和验证。volumes把./data挂到容器里SQLite 文件放在这个目录下容器删了数据还在。restart: unless-stopped保证机器重启后任务自动恢复。6.2 自动化验证脚本检查数据完整性与误差阈值验证脚本不需要复杂核心是三个检查最近 24 小时是否有记录、缺失率是否超过阈值、预测误差是否在可接受范围内。下面是一个最小实现import sqlite3 import pandas as pd from datetime import datetime, timedelta conn sqlite3.connect(data/weather.db) df pd.read_sql(SELECT * FROM weather_clean, conn, parse_dates[record_time]) # 检查一最近 24 小时是否有数据 last_time df[record_time].max() if datetime.now() - last_time timedelta(hours24): print(警告超过 24 小时没有新数据) else: print(数据时效性正常) # 检查二温度缺失率 missing_rate df[temperature].isna().mean() if missing_rate 0.1: print(f警告温度缺失率 {missing_rate:.2%}超过 10%) else: print(f温度缺失率 {missing_rate:.2%}正常) # 检查三预测误差假设 prediction_log 表里有 actual 和 predicted try: pred_df pd.read_sql(SELECT * FROM prediction_log, conn) if len(pred_df) 0: mae (pred_df[actual] - pred_df[predicted]).abs().mean() print(f平均绝对误差{mae:.2f}℃) if mae 5: print(警告预测误差偏大建议检查模型或数据质量) except Exception as e: print(预测日志读取失败, e) conn.close()这个脚本可以挂在predictor容器里每次预测后自动跑一遍。输出直接打到日志里答辩时截一张图就能说明你的系统有自我检查能力。误差阈值5℃是我根据经验设的你可以根据自己数据的情况调整但不要设得太宽松否则验证就失去意义了。6.3 一个我常犯的错误我刚开始做这类项目时总想把所有功能一次性写完结果采集还没稳定就急着上预测预测还没验证就急着做前端最后每个环节都半成品。后来我改成一个习惯每完成一个环节先写一个最小验证脚本跑通再进入下一个环节。采集跑通了再写清洗清洗跑通了再写预测预测跑通了再写前端。这样即使时间不够至少前面的环节是扎实的答辩时也能讲清楚哪部分做到了什么程度。希望帮到你。本文还有配套的精品资源点击获取