Python天气预报系统:从API数据采集到可视化分析完整实现
简介这是一份基于Python的天气预报系统与可视化数据分析完整项目为98分高分课程设计作品主要面向计算机相关专业正在进行课程设计、期末大作业的学生以及需要项目实战练习的开发者。资源共包含59个文件压缩包大小仅4.16MB目录规划清晰多个Word文档覆盖需求分析、可行性报告、设计文档、用户使用手册11个Python源文件实现核心功能HTML文件呈现可视化结果PNG图片保存系统架构图、流程图、ER图等设计图表CSV数据文件提供天气数据支撑。已有249人学习下载。项目提供了从需求分析到系统实现的全流程参考包括软件工程文档、前后端代码、可视化图表模块以及环境配置说明覆盖了开发过程中常用的文档模板与代码结构。可以帮助读者理清天气预报系统的整体架构与数据分析思路适合作为课程设计模板、期末大作业参考或Python数据分析与可视化练手项目直接借鉴其中的文档结构与代码逻辑快速完成相似课题的开发与文档撰写。1. 天气预报系统不只是调接口那么简单很多同学做课程设计习惯先找 Python 源码然后跑起来截图交差。但这套基于 Python 的天气预报系统和可视化数据分析源码内部逻辑比想象中完整它包含需求分析 v1~v5、可行性分析报告 v1~v4、设计文档 v2、用户使用手册以及 weather-master 代码工程。后端houduan 目录负责数据抓取与接口前端 home.html / result.html 负责展示。与其说是作业不如说是一个可答辩的完整软件工程样本。实际项目里最有价值的部分不是天气折线图而是“数据怎么从 API 落到 SQLite再被 pandas 聚合成可视化图表”的这条链路。下面我就把数据采集、可视化方案、文档组织顺序和源码运行注意事项依次过一遍。如果你正在做期末大作业或者想用 python 做数据分析与可视化项目练手可以直接照着改。这套源码能拿高分关键不在于代码量而在于它把软件工程的文档和实现对齐了。也就是说你在设计文档里画的每个模块都能在 weather-master 里找到对应代码。因此在拆解代码之前先看文档目录中的命名规律会很有帮助。2. 数据链路设计从天气 API 到 SQLite 的完整落库过程2.1 为什么选 requests Pandas SQLite 这套组合这套系统的定位是课程设计不是生产级爬虫。所以技术选型没有过度设计requests 负责 HTTP 请求Pandas 负责清洗与聚合SQLite 负责持久化。没有引入 Redis、Celery 或重型 ORM因为 16 个城市、每小时抓一次的数据量用 SQLite 足够且方便打包提交给老师。如果换成 Scrapy需要维护爬虫框架的配置文件对可视化和数据分析场景没有额外收益。如果换 MySQL则助教运行时要额外安装数据库失去开箱即用的优势。Pandas 在这里也不是替代品而是和 SQLite 配合它读出的 DataFrame 可以直接转成 JSON 给前端省去手写循环拼接。下表是这套系统里核心模块的职责划分模块职责核心依赖数据抓取请求天气API处理超时与重试requests数据清洗缺失值填充、字段类型转换pandas数据存储创建表、增量写入、去重sqlite3统计分析最高温、降雨概率、天气频率pandas可视化展示折线图、柱状图、地图标记Flask, ECharts, Matplotlib从这里可以看到真正需要写代码的部分集中在抓取和存储两层可视化展示虽然有 Flask 路由和 ECharts 配置但本质上是把聚合结果翻译成图表。这也意味着如果你后期想换成 MySQL只需要改动 save_weather 函数和查询语句前端几乎不用动。2.2 天气数据源与字段映射项目里没有硬编码真实 API Key而是把请求参数留在了配置文件中。常见做法是申请高德开放平台或和风天气的接口它们的免费额度能覆盖课程演示。无论你选哪一家返回 JSON 的结构大同小异核心字段是温湿度、天气现象、风向风速和更新时间。抓回来的数据不能直接入库因为 API 返回的是扁平结构且部分字段在小数精度、时区处理上有差异。我在实际改造时通常先写一个字段映射表例如原始字段英文存储字段中文类型示例值obsTime观测时间TEXT2025-06-01T12:0008:00temp温度REAL28.5feelsLike体感温度REAL30.2humidity湿度INTEGER68windSpeed风速REAL12.0text天气现象TEXT多云这张表解决了三个问题一是在清洗时可以直接 rename 列名二是统一时间格式便于后续按小时/天聚合三是避免混用英文键导致前端模板出错。项目文档中“天气数据映射”相关的表格基本也是按照这个思路设计的。2.3 抓取与清洗代码实现下面是我从 weather-master 里抽出的核心流程去掉了与具体 API 绑定的细节保留了结构。这里以函数方式组织方便复用import time import requests import pandas as pd def fetch_weather(city, api_key, base_urlhttps://api.qweather.com/v7/weather/now): params { location: city, key: api_key } for attempt in range(3): # 简单重试机制 try: resp requests.get(base_url, paramsparams, timeout5) resp.raise_for_status() data resp.json().get(now, {}) if not data: raise ValueError(接口未返回 now 字段) return { city: city, obs_time: data[obsTime], temp: float(data[temp]), humidity: int(data[humidity]), text: data[text] } except (requests.RequestException, ValueError) as e: print(f[{city}] 第 {attempt1} 次请求失败: {e}, flushTrue) time.sleep(2) return None # 调用后得到列表交给清洗函数 raw_records [fetch_weather(city, key) for city in cities] df pd.DataFrame([r for r in raw_records if r])这段逻辑主要做了三件事requests.get 封装了请求参数和超时time.sleep(2) 控制重试间隔避免被接口限流返回字典时直接做类型转换把字符串 temp 转 float为后续画图消除隐患。实际项目中可能有更多异常分支比如城市编码不符合规范、返回空列表这些可以统一用 ValueError 截获。清洗通常紧跟着抓取我习惯在同一层完成def clean_weather_df(df): df[obs_time] pd.to_datetime(df[obs_time], errorscoerce) df df.dropna(subset[obs_time, temp]) # 删除时间或温度缺失的行 df[date] df[obs_time].dt.strftime(%Y-%m-%d) df[hour] df[obs_time].dt.hour return df[[city, date, hour, temp, humidity, text]]参数说明obs_time 用 pandas 的 to_datetime 统一为时间类型errorscoerce 保证非法值变成 NaT随后 dropna 删除这些行。date 和 hour 是分开抽出的维度因为后面可视化数据分析需要按天聚合也要按小时看温度变化提前拆列能让查询语句更简单。2.4 数据落库与增量更新清洗后的 DataFrame 写 SQLite表结构要防止重复。这里用 city date hour 作为唯一键采用 INSERT OR REPLACEimport sqlite3 def save_weather(df, db_pathweather.db): conn sqlite3.connect(db_path) df.to_sql(weather_hourly, conn, if_existsappend, indexFalse) conn.execute( DELETE FROM weather_hourly WHERE rowid NOT IN ( SELECT MIN(rowid) FROM weather_hourly GROUP BY city, date, hour ) ) conn.commit() conn.close()to_sql 第一次会自动建表但字段类型由 pandas 推断可能把日期存成 object。更稳妥的做法是先执行 CREATE TABLE IF NOT EXISTS再逐行 INSERT OR REPLACE。上面用 rowid 去重是一种偷懒但有效的“增量更新”方案先全部追加再按分组保留最早一条。如果你要应对长期运行建议改成 UPSERT 语法显式声明唯一索引。这样既能避免重复数据也能保留最新抓取结果而不是最早结果。实际项目中 houduan 目录里存在多个版本houduan、houduanv2说明作者在迭代过程中也遇到过数据重复导致的统计异常最终版本大概率采用了类似方案。3. 可视化数据分析从静态图表到 Web 看板3.1 用 pandas 做天气聚合统计可视化不是直接把原始数据丢给前端。以 result.html 中展示的图表为例它的数据来源是后端接口返回的聚合结果。我用 pandas 的 groupby 做最常见三类聚合按城市算平均温度、按日期算最高最低温、按天气现象算频次。import pandas as pd df pd.read_sql_query(SELECT * FROM weather_hourly, conn) daily df.groupby([city, date]).agg( avg_temp(temp, mean), max_temp(temp, max), min_temp(temp, min), avg_humidity(humidity, mean) ).reset_index() weather_freq df.groupby([city, text]).size().reset_index(namecount)这段聚合代码返回的 DataFrame 可以通过 to_dict(orientrecords) 直接转成 JSON。下面是我在文档里常用的可视化映射关系聚合维度计算指标对应图表城市日期平均温、最高最低温折线图城市天气现象出现次数饼图/柱状图城市小时温度变化散点图/热力图第一段代码里的 agg 同时计算多个指标比多次 groupby 更高效。第二段用来做天气现象占比图比如“多云 34 次、晴 28 次”这类数据适合用饼图或横向柱状图。注意 count 列名可能会和 SQL 保留字冲突所以显式用 namecount 避免后续操作歧义。3.2 Matplotlib 绘制温度趋势图为了在文档和答辩 PPT 中直接使用系统也用 matplotlib 生成了静态绘图。这里的关键是设置中文字体否则会出现乱码import matplotlib.pyplot as plt import matplotlib matplotlib.rcParams[font.sans-serif] [SimHei, Microsoft YaHei] matplotlib.rcParams[axes.unicode_minus] False plt.figure(figsize(10, 4)) daily_one_city daily[daily[city] 北京] plt.plot(daily_one_city[date], daily_one_city[max_temp], markero, label最高温) plt.plot(daily_one_city[date], daily_one_city[min_temp], markers, label最低温) plt.xticks(rotation45) plt.legend() plt.tight_layout() plt.savefig(temperature_trend.png, dpi150) plt.close()保存用 plt.close() 是关键否则在循环或 Web 环境下会导致内存里 matplotlib 的 Figure 数量不断增长。dpi150 是为打印清晰度准备的如果想嵌入网页可以把 dpi 降到 100 以获得更小的图片体积。这里的日期格式来自前面清洗时生成的 date 字段如果直接使用 obs_timex 轴会挤成一团。3.3 Flask ECharts 构建交互式看板静态图只能看结果演示时需要交互。这套系统的 houduan 目录用 Flask 提供 JSON 接口前端 home.html 和 result.html 通过 ECharts 绘图。后端接口的最小结构如下from flask import Flask, jsonify from flask_cors import CORS app Flask(__name__) CORS(app) app.route(/api/daily) def api_daily(): df pd.read_sql_query(SELECT * FROM weather_hourly, conn) daily df.groupby([city, date])[temp].mean().reset_index() return jsonify(daily.to_dict(orientrecords))api_daily 返回的每条记录形如 {city: 北京, date: 2025-06-01, temp: 27.3}。前端拿到后按城市过滤就能绘制。这里用 to_dict(orientrecords) 而不是默认的 orientdict是因为 records 模式生成的是列表套字典javascript 直接 forEach 即可不需要额外处理索引。ECharts 的核心配置在项目前端 main.js 里其实只需要把 xAxis 的 data 换成日期数组series 的 data 换成温度数组fetch(/api/daily) .then(res res.json()) .then(data { const cityData data.filter(d d.city 北京); chart.setOption({ xAxis: { data: cityData.map(d d.date) }, series: [{ data: cityData.map(d d.temp) }] }); });这里没有任何复杂的动画配置但对于课程设计已经足够。如果需要加分可以增加城市切换下拉框通过 js 重新过滤数据而不必重新请求后端接口。实际上数据总量不大时一次加载所有数据再在浏览器端过滤体验比反复请求好很多。3.4 可视化结果如何反哺数据模型有一个容易被忽略的环节绘制图表时如果发现某天的最高温和最低温一样说明原始数据里 temp 字段可能出现脏数据或时区转换错误。这时应该回到第 2 节的清洗层修正而不是在前端 CSS 里强行显示。我在实际调试这套源码时曾经遇到 humidity 超过 100 的情况。通过聚合统计发现是某个城市接口返回了异常值最终在 clean_weather_df 里增加一行 df df[df[humidity] 100]。这类“可视化反推数据质量”的经验是课程设计答辩中最能体现工程能力的地方。文档中需求分析 v5 里新增的“异常数据提示”功能本质上就是为这种场景设计的。4. 课程设计文档怎么组织才能拿高分4.1 可行性分析报告三维论证框架项目里有多份可行性分析报告 v1~v4迭代痕迹明显。最终报告建议从技术、经济、操作三个维度展开。技术可行性重点说明 Python 生态成熟、requests 和 pandas 文档多经济可行性强调免费 API 和开源组件成本为零操作可行性论证“使用者只需要懂浏览器和简单的配置文件”。可以做成一个表格来对比不同方案表格不仅能压缩篇幅还能让答辩老师一眼看到你的决策过程。我在这里列了三种常见路线实际项目里只推荐第一种方案技术难度成本运行环境结论调用天气 API Python 处理中低本地 Python推荐使用商用气象数据低高云端不推荐自建爬虫抓取网页高低需要维护选择器不建议答辩时说明“为什么排除其他方案”比罗列优点更重要。可行性分析报告 v3 和 v4 的区别就在这里v3 只写了“可用”v4 补上了“与现有系统相比优势”。4.2 需求分析用例图、数据流图与用例描述需求分析 v1~v5 从最初只有“查看天气”一个用例扩展到了“历史数据查询”“城市对比”“异常提示”。建议用软件工程的标准模板组织业务规则、数据描述、功能需求、非功能需求。非功能需求包括响应时间小于 1 秒、支持至少 10 个城市并发查询。数据流图方面系统有 0 层图、1 层图和顶层图项目文件夹里正好有对应图片。0 层图描述“用户→系统→数据源”的高层流动1 层图细化到“抓取模块→清洗模块→存储模块→展示模块”。答辩时可以直接指着图片讲比读代码高效。需求分析中每个用例都要配一个用例描述表包含主流程、异常流程和后置条件这部分是分值差距最大的地方。4.3 设计文档架构分层、ER 图与模块接口设计文档 v2 里关于数据库设计核心 ER 图包含城市表、天气记录表、聚合统计表。城市表是基础字典天气记录表的主键是 (city, date, hour)聚合统计表可以存储每日均值避免前端每次都跑全表聚合。模块划分上这套源码可以分成三层数据层SQLite、逻辑层抓取、清洗、聚合、表现层Flask HTML。设计文档必须包含接口约定例如/api/weather?cityxxx返回 JSON 结构这样前端和后端可以并行开发。下面是文档中常见的接口定义示例{ code: 0, # 0 表示成功 data: { city: 北京, temperature: 27.5, humidity: 60, wind: 东南风3级 } }接口定义里建议保留 code 字段不要只返回 data。这样前端可以统一判断错误码而不是通过解析异常来定位问题。设计文档 v2 中还有一个“模块 1 的程序设计”图那部分其实就是画出每个模块的输入输出和核心算法步骤对应到代码就是 fetch_weather 和 save_weather 两个函数。4.4 用户使用手册与测试报告用户使用手册不需要写代码但要覆盖三种人群普通用户、管理员、开发者。普通用户只需要知道打开 home.html 输城市管理员需要知道如何配置 API Key开发者需要知道数据库迁移步骤。项目里的 README.md 可以充当开发者快速上手文档。测试报告要包含功能测试和边界测试例如城市名不存在时是否给出友好提示网络断开时程序是否异常退出同一城市不同时间抓取的数据是否正确覆盖。这些测试结果可以用表格罗列不仅体现工作量也显得严谨。项目源码中 result.txt、index.txt 这类文件实际上就是运行过程中的输出日志把它们整理到测试报告里能证明代码真的运行过。5. 运行源码的排错指南与二次开发技巧5.1 环境准备与依赖安装拿到项目后先看 requirements.txt如果没有就根据代码引用的包手动安装。通常需要 pandas、flask、requests、matplotlib。建议用虚拟环境python -m venv venv source venv/bin/activate # Windows 用 venv\Scripts\activate pip install pandas flask requests matplotlib注意 Python 版本不要低于 3.8否则 f-string 和类型注解的部分写法会报错。安装时如果 pip 下载慢可以用国内镜像比如-i https://pypi.tuna.tsinghua.edu.cn/simple。这个步骤虽然基础但大多数源码运行不起来都是环境问题而不是代码问题。5.2 常见运行错误与定位最常见的错误是运行 weather-master 时提示ModuleNotFoundError: No module named flask这是没有在激活的虚拟环境里安装依赖。第二常见的错误是抓取数据后绘图出现中文乱码因为 matplotlib 默认字体不包含中文字符需要在代码中设置 rcParams。第三是 Flask 端口被占用默认 5000 端口可能被其他程序占用启动失败时可以改用 5001python app.py --port 5001如果源码里没有 CLI 参数可以直接在app.run()中修改port5001。定位这些问题时建议先看日志输出位置再回查函数调用链不要盲目改代码。5.3 把系统扩展成短期预测工具课程设计到这里已经完整但如果想体现更多 python 数据分析能力可以在现有数据基础上加一个简单回归模型。用 sklearn 的线性回归预测未来 24 小时温度特征是历史温度、小时数、湿度from sklearn.linear_model import LinearRegression import numpy as np df[hour_sin] np.sin(2 * np.pi * df[hour] / 24) X df[[hour_sin, humidity]].values y df[temp].values model LinearRegression().fit(X, y) future np.array([[np.sin(2 * np.pi * 10 / 24), 50]]) print(model.predict(future))这里把“小时”转成 sin 编码是为了让模型理解 23 点和 0 点是连续的而不是让时间被当作线性连续的整数。回归结果虽然精度有限但足以在答辩中展示你不仅会调接口还懂特征工程。最后提一个很实用的小技巧无论你在文档里写了多少页数据库设计演示时一定要把 weather.db 放在和源码相同的相对路径。很多同学在自己电脑上正常拷给老师后找不到数据库就是因为用了绝对路径。把路径写成os.path.join(os.path.dirname(__file__), weather.db)可以避免这类问题。本文还有配套的精品资源点击获取