☰
Python天气预报数据可视化:从API请求到ECharts展示全攻略
2026/10/1 4:28:58 网站建设 项目流程

简介:这是一份基于Python的天气预报与数据可视化项目源码包,面向高校期末大作业或课程设计场景,适合需要快速交付高分项目以及系统学习数据分析、可视化技术的学生使用。压缩包共24个文件,体积仅1.42MB,包含4个Python源码模块、4个CSV气象数据集、1个pkl模型文件、12张效果截图和1份使用说明文档,目录结构清晰,便于按模块定位代码。项目完整覆盖天气数据采集、清洗、建模预测与图表可视化流程,源码分为数据获取、数据处理、模型训练与主程序等多个部分,并附有可直接调用的训练模型和示例数据,解压后无需修改代码即可运行演示。目前已有62人浏览学习,对于需要完成期末大作业或课程设计的同学,既能快速获得一份可运行的高分项目,也能从中学习到爬虫、数据分析以及数据可视化的实际用法。

1. 期末大作业选题:Python天气预报与数据可视化,好做又稳拿高分

期末大作业最怕的不是题难,是方向选得别扭。Python实现的天气预报与数据可视化项目,属于“看起来普通、实际正好踩中课程考核点”的选题:代码量不算大,但完整覆盖了网络请求、数据处理、后端服务和前端展示这条链路。你不需要分布式、不需要机器学习,把 requests、Flask 和 ECharts 这三样用熟,就能交出一份在答辩现场挑不出硬伤的作业。这个方案对 Python 入门到进阶的选修课、数据分析课和 Web 开发课都适用,也是少数能从“能跑”一路做到“能讲出设计思路”的项目——而后者往往是高分的关键。

2. 先想明白再动手:从期末考核点反推需求,再选技术栈

2.1 期末大作业的三个隐藏评分项:完成度、技术密度、答辩说服力

很多同学拿到课程设计题目,第一反应是去 GitHub 找现成代码,这是最容易翻车的一条路。老师对本班作业的代码风格、注释习惯、依赖版本心里有数,一份八百年历史的开源仓库交上去,答辩时连里面的函数都解释不清,分只会往下走。我做课程助教那两年看到的评分依据,其实只有三个维度。

第一个维度是完成度:数据能不能拿到、页面能不能打开、图表能不能渲染,这是一个“0 或 1”的判定。第二个维度是技术密度:老师会看你的项目里有没有“数据获取 → 数据清洗 → 后端接口 → 前端展示”的完整分层。哪怕每一层都很薄,只要你分开了,就比把所有代码塞进一个脚本里强一个档次。第三个维度是答辩说服力:你能否说清楚“为什么用这个库、不用那个库”,这比代码本身更能拉分。

所以动手前,我一般会把需求先写成一张清单,贴在项目根目录的 README 里:实时天气、未来 3 天预报、多城市切换、可视化图表、数据更新缓存。每做一项就勾一项。期末大作业最怕的是做完才发现漏了老师明确提过的要求,比如“要有数据库”或“要有交互控件”,这种结构性返工比改 bug 痛苦得多。

2.2 数据从哪来:免费天气 API 与爬虫的取舍,我推荐前者

天气预报项目的第一步是拿数据。这里只有两条路:调免费天气 API,或者写爬虫去抓网页。我的建议非常明确:期末大作业选 API,别选爬虫。

爬虫的问题不在技术难度,而在“不可控”。你用 requests 去抓天气网站,今天能抓到,明天网站改了 class 名,XPath 全失效;对方加了反爬,你的 IP 被临时封掉;更重要的是,网页里混着广告、推荐位、动态加载的字段,清洗数据的成本比写爬虫本身还高。期末大作业的评分不看你有多少“惊险操作”,看的是你能不能稳定地把结果讲清楚。我见过好几个组死在爬虫上,答辩当天数据源挂了,页面一片空白,这种现场事故基本告别高分。

免费天气 API 里,国内学生用得最多的是和风天气。注册开发者账号后,它会给你一个 API Key,免费开发版对期末作业完全够用。它的文档结构清晰,返回的是标准 JSON,字段语义明确,这对后续数据可视化非常友好。另一类选择是高德开放平台的天气接口,优点是和地图功能联动方便,但如果你只做天气展示,它的字段比和风天气少,后续想画“逐小时温度曲线”会缺数据。还有一些聚合数据类平台也提供天气接口,注册流程更繁琐,接口风格不统一,不推荐。

API 与爬虫的取舍可以简单总结成一张表:

对比维度免费天气 API网页爬虫
数据稳定性高,字段格式固定低,页面改版即失效
开发成本低,读文档就能调通高,需要解析 HTML 与反爬对抗
数据质量结构化 JSON,干净混入页面噪声,清洗麻烦
合规风险按平台条款使用即可可能违反目标站点条款
答辩话术“基于开放 API 实现”“逆向解析网页结构”

答辩时“基于 API 实现”这种表述,明显比“我爬了某某网站”更体面,老师挑不出合规问题。

2.3 技术栈选型:requests + Flask + ECharts 为什么比一堆框架更稳

数据源定了之后,技术栈的选型逻辑也很直接。网络请求用 requests,这是 Python 生态里最基础的 HTTP 客户端,比 urllib 好用太多,也是老师默认你会用的库。后端用 Flask 而不是 Django,原因很简单:期末作业不需要 Django 的 Admin 后台、ORM、迁移机制等重型能力,Flask 三个文件就能起一个服务,代码量少意味着你能在答辩前把每一行都看懂。启动项目时也不会遇到“类不存在”这类 Java 式框架配置问题,Python 轻量框架对初学者友好得多。

前端可视化是另一个关键决策。很多课程教过 Matplotlib,但期末大作业展示时,Matplotlib 生成的静态图片放在网页里,不管怎么调样式,都显得像数据分析报告,不像“系统”。这里我更推荐 ECharts:它是一个纯前端图表库,用 JavaScript 渲染出交互式图表,折线图、饼图、地图都自带动画,视觉上直接碾压静态图。ECharts 的官网有在线编辑器和海量示例,复制一份示例改成自己的数据,前后端一接,项目的完成度立刻上一个台阶。

如果你不希望前端写太多 JavaScript,也可以考虑 Pyecharts。它是 ECharts 的 Python 封装,在 Python 里写配置就能生成图表,但它的劣势是图表配置藏在后端,动态更新相对绕。我的习惯是:只要时间允许,就正经用 ECharts 写前端,数据接口留给 Flask 返回 JSON。这样分层清楚,答辩时“前后端分离”几个字一说出来,老师就会觉得你理解 Web 开发的核心模型。最终选型可以固定为:requests 获取数据,Python 字典做清洗,Flask 提供页面和 JSON 接口,原生 ECharts 负责渲染。

3. 用 requests 把天气数据拿下并洗净:API 调用与缓存的最小实现

3.1 申请 API Key 并跑通第一次请求:三行代码验证通路

先用浏览器打开和风天气控制台,注册账号后创建一个项目,你会拿到一串由数字和字母组成的 API Key。这个 Key 相当于你调用接口的通行证,不要硬编码在代码里发给别人。期末大作业虽然不涉及生产环境,但养成把密钥放进配置文件的习惯,老师在检查代码时会看到你的工程意识。

和风天气的调用方式是标准的 HTTP GET 请求。请求实时天气的核心参数是两个:location代表城市位置,key是你的认证凭证。location可以直接传城市名,但更推荐先调用一次城市搜索接口,拿到城市的 LocationID,再用它去请求天气数据,因为城市名重名率不低,直接传字符串容易取到同名城市。下面这段代码完成了从城市名到实时天气的最小通路:

import requests API_KEY = "你的_key" def get_location_id(city: str) -> str: """用城市名换 LocationID,避免重名城市取错数据""" url = "https://geoapi.qweather.com/v2/city/lookup" params = {"location": city, "key": API_KEY} resp = requests.get(url, params=params, timeout=10) data = resp.json() return data["location"][0]["id"] def get_now_weather(location_id: str) -> dict: """请求实时天气,返回原始 JSON""" url = "https://devapi.qweather.com/v7/weather/now" params = {"location": location_id, "key": API_KEY} resp = requests.get(url, params=params, timeout=10) return resp.json() if __name__ == "__main__": loc_id = get_location_id("北京") weather = get_now_weather(loc_id) print(weather["now"]["temp"], weather["now"]["text"])

这段代码的意图很直接:先用城市名查到北京对应的 LocationID,再带着这个 ID 去拿实时天气,最后打印出气温和天气现象文字。timeout=10是个容易被忽视但很重要的参数,它规定了最多等服务器 10 秒,避免校园网波动时程序卡死在那里。resp.json()会把返回的文本解析成 Python 字典,之后的一切操作都建立在这个字典之上。

如果你发现返回结构里没有now字段,说明请求失败了。此时应该先打印完整的weather字典,看code字段的值:404代表 LocationID 不存在,401代表 Key 无效。我第一次调这个接口时,就是忘了在控制台把 Key 复制完整,多了一个空格,报了半天 401。

3.2 数据清洗与结构转换:把 JSON 变成后端、前端都顺手的字典

API 返回的原始 JSON 虽然是结构化的,但不能直接丢给前端。原因有两个:第一,里面带有code、refer这类接口元数据,对页面展示没有意义;第二,字段是英文缩写,比如temp、feelsLike、windDir,前端渲染时还要再翻译一层。所以清洗这一步,本质上是在“API 返回结构”和“前端展示需求”之间做一个翻译层。

我一般会写一个独立的服务模块,专门负责把原始数据转换成项目内部的统一字典结构。以下代码把实时天气、当天 24 小时预报和未来 3 天预报组装成一个干净的 Python 字典:

def clean_weather(raw: dict, daily_raw: dict) -> dict: """ 清洗实时天气与预报数据 :param raw: /weather/now 接口的原始返回 :param daily_raw: /weather/3d 接口的原始返回 :return: 只包含展示字段的字典 """ now = raw.get("now", {}) daily_list = daily_raw.get("daily", []) cleaned = { "current_temp": now.get("temp"), "feels_like": now.get("feelsLike"), "weather_text": now.get("text"), "wind_dir": now.get("windDir"), "wind_scale": now.get("windScale"), "humidity": now.get("humidity"), "daily": [] } for day in daily_list: cleaned["daily"].append({ "date": day.get("fxDate"), "max_temp": day.get("tempMax"), "min_temp": day.get("tempMin"), "day_text": day.get("textDay"), "night_text": day.get("textNight") }) return cleaned

这段清洗逻辑有两个关键点。一是大量使用get方法而不是直接下标取值,因为接口在极端情况下可能缺字段,get会返回None而不是抛出异常让整个程序崩溃。二是把“预报天数”这一层循环抽出来独立处理,后续如果你想从 3 天预报改成 7 天预报,只需要换接口地址和对应的城市 ID 参数,清洗函数不用动。

参数说明方面,now.get("temp")取的是实时气温,单位是摄氏度;daily数组里每个元素代表一天,tempMax和tempMin是当天最高最低温;textDay和textNight是白天和夜间的天气现象描述。清洗后的字典结构匹配的是前端图表的展示需求,比如折线图需要“日期 + 最高温 + 最低温”三个字段并列,这个结构直接就能用。

3.3 多城市管理与文件缓存:config.py 里藏着的三个设计细节

期末大作业如果只做一个城市,功能会显得单薄。常见做法是支持 4 到 6 个城市切换,这样前端的下拉框和数据可视化都有文章可做。多城市带来的直接问题是管理复杂度——每个城市都有 LocationID,每份数据都有有效期,如果每次刷新页面都去请求一遍 API,免费额度很快见底,答辩现场断网时更是全场尴尬。

我的方案是用一个config.py集中管理城市列表,再加一个基于 JSON 文件的本地缓存。城市列表用字典维护,键是城市中文名,值是对应的 LocationID,这样前端只需要把城市名传回后端,后端就能找到 ID 并发起请求。缓存机制的核心逻辑是:每个城市的数据单独存成一个 JSON 文件,文件名就是城市名,访问时先检查文件最后修改时间,如果不超过 30 分钟就直接读文件内容,否则重新请求 API 并覆盖缓存文件。

import os import json import time CITIES = { "北京": "101010100", "上海": "101020100", "广州": "101280101", "深圳": "101280601" } CACHE_DIR = "data/cache" CACHE_TTL = 30 * 60 # 缓存有效期 30 分钟 def get_city_cache(city: str): """读取缓存,过期则返回 None""" path = os.path.join(CACHE_DIR, f"{city}.json") if not os.path.exists(path): return None if time.time() - os.path.getmtime(path) > CACHE_TTL: return None with open(path, "r", encoding="utf-8") as f: return json.load(f) def save_city_cache(city: str, data: dict) -> None: """写入缓存,按城市名分文件存储""" os.makedirs(CACHE_DIR, exist_ok=True) path = os.path.join(CACHE_DIR, f"{city}.json") with open(path, "w", encoding="utf-8") as f: json.dump(data, f, ensure_ascii=False)

这三个设计细节值得说一下。第一,把城市名作为缓存文件名,天然实现了数据隔离,不需要额外的索引表。第二,os.path.getmtime拿到的是文件最后修改时间,它在这里充当了“数据时间戳”,配合CACHE_TTL实现过期判断,逻辑直白,答辩时也能讲清楚。第三,ensure_ascii=False必须写,否则中文字段会被转成\u转义序列,JSON 文件打开时没法看,排查数据时体验很差。

这里踩过的一个坑是:缓存目录没有先创建,导致程序第一次运行时open(path, "w")直接抛FileNotFoundError。解决办法就是os.makedirs(CACHE_DIR, exist_ok=True),它在每次写缓存前都确保目录存在,成本极低但能避免一次现场翻车。

4. 用 Flask 把数据变成可视化页面:路由、模板与 ECharts 落地

4.1 Flask 最小工程结构与路由规划:一个 app.py 加一个 templates

后端服务我用 Flask 实现。它的核心价值在于把“处理好的天气数据”和“用户看到的页面”连接起来。整个项目的结构应该保持简单,让老师一眼能看清每个文件是干什么的。我建议按下面的目录组织:

weather_project/ ├── app.py # Flask 主应用,定义路由 ├── config.py # 城市列表、API Key、缓存配置 ├── weather_service.py # 天气数据获取、清洗、缓存逻辑 ├── requirements.txt # 依赖清单 ├── templates/ │ └── index.html # 页面模板 └── static/ ├── css/style.css # 页面样式 ├── js/echarts.min.js # 本地 ECharts 库 └── js/main.js # 页面交互逻辑

这个结构的核心原则是“按职责分层”。weather_service.py只做数据处理,不关心页面长什么样;app.py只做路由转发,不写业务逻辑;前端三个文件各自负责结构、样式和交互。老师问起时,你直接说“我把数据层和展示层分开了”,这比“我是照着教程写的”有说服力得多。

路由规划上,只需要两个路由。一个GET /返回页面本身,一个GET /api/weather返回 JSON 数据。把数据接口独立出来,是为了演示“前后端分离”的设计思路,也为答辩时现场演示接口调用留了余地。用浏览器直接访问/api/weather?city=北京,就能看到 JSON 格式的天气数据,这一下就能让老师明白你的系统不是“页面写死的假数据”。

4.2 写后端接口并注入模板:JSON 接口和页面路由各司其职

Flask 主程序的代码量很少,但每一行都要写清楚注释,因为这是答辩时老师大概率会读的代码。核心逻辑是:页面路由负责渲染 HTML 模板,数据接口负责返回 JSON。注意数据接口返回的是jsonify包装后的对象,它会把 Python 字典自动序列化成 JSON 字符串,并设置正确的Content-Type响应头。

from flask import Flask, render_template, jsonify, request import weather_service app = Flask(__name__) @app.route("/") def index(): """渲染主页面""" return render_template("index.html") @app.route("/api/weather") def api_weather(): """获取指定城市的天气数据,返回 JSON""" city = request.args.get("city", "北京") data = weather_service.get_weather(city) if data is None: return jsonify({"error": "数据获取失败"}), 500 return jsonify({"city": city, "data": data, "updated_at": weather_service.get_update_time(city)}) if __name__ == "__main__": app.run(host="0.0.0.0", port=5000, debug=True)

这段代码里,request.args.get("city", "北京")取的是 URL 查询参数,也就是http://localhost:5000/api/weather?city=上海里的city值,给了默认值“北京”,所以直接访问接口也不会报错。weather_service.get_weather(city)是我们上一章封装好的函数,它内部会先查缓存、再请求 API、最后清洗数据。返回的jsonify字典里除了天气数据,还加了updated_at时间字段,前端可以把它显示在页面上,告诉用户“这份数据是几点更新的”。

app.run的debug=True在期末答辩时特别有用。它开启了 Flask 的自动重载,你改一行代码保存,服务自动重启,不用手动重启终端。但要注意,debug=True只能在本地开发用,答辩部署到演示环境时一定要改成debug=False,否则错误堆栈会直接暴露在页面上,非常不专业。

4.3 前端展示层:ECharts 渲染折线图和饼图的完整配置

前端是整个项目最直观的加分项。页面打开后,左边是当前天气信息卡片,右边是未来 3 天温度的折线图,下面再放一个 24 小时天气现象占比的饼图,这个布局在视觉上就足够撑起“系统”两个字。模板文件index.html的核心结构是引入 ECharts 库、放两个图表容器、再引入业务 JS。

<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>天气预报数据可视化</title> <link rel="stylesheet" href="/static/css/style.css"> <script src="/static/js/echarts.min.js"></script> </head> <body> <div class="container"> <h1>天气预报数据可视化</h1> <select id="city-select"> <option value="北京">北京</option> <option value="上海">上海</option> <option value="广州">广州</option> <option value="深圳">深圳</option> </select> <div id="now-card"></div> <div id="temp-chart" style="width:100%;height:400px;"></div> <div id="weather-pie" style="width:100%;height:350px;"></div> </div> <script src="/static/js/main.js"></script> </body> </html>

这里要注意,echarts.min.js必须放在本地static/js/目录下,而不是用 CDN 链接。原因很简单:期末答辩现场的网络质量不可控,一旦教室 Wi-Fi 无法访问公网 CDN,图表就全部加载不出来,这是最典型的现场翻车事故。把库文件下载到本地后,整个系统完全离线可用,演示时从容得多。

图表配置的主体逻辑在main.js里。折线图的 X 轴是日期,两条线分别代表当天最高温和最低温。ECharts 的配置项看似复杂,其实核心就三个部分:xAxis定义 X 轴数据,yAxis定义 Y 轴指标,series定义图表类型和具体数据。下面这段代码展示了怎样把后端返回的 JSON 数据绑定到图表上:

async function loadWeather(city) { const resp = await fetch(`/api/weather?city=${city}`); const result = await resp.json(); const daily = result.data.daily; // 折线图:未来3天最高温与最低温 const chart = echarts.init(document.getElementById("temp-chart")); chart.setOption({ title: { text: "未来3天温度趋势" }, tooltip: { trigger: "axis" }, legend: { data: ["最高温", "最低温"] }, xAxis: { type: "category", data: daily.map(d => d.date) }, yAxis: { type: "value", name: "温度(°C)" }, series: [ { name: "最高温", type: "line", data: daily.map(d => d.max_temp) }, { name: "最低温", type: "line", data: daily.map(d => d.min_temp) } ] }); // 刷新当前天气卡片 const now = result.data; document.getElementById("now-card").innerHTML = ` <p>${now.weather_text} ${now.current_temp}°C</p> <p>体感 ${now.feels_like}°C,${now.wind_dir} ${now.wind_scale}级</p> <p>湿度 ${now.humidity}%</p>`; } document.getElementById("city-select").addEventListener("change", function () { loadWeather(this.value); }); loadWeather("北京");

这段前端代码里,fetch是浏览器自带的异步请求 API,向 Flask 服务的/api/weather接口发起请求,拿到 JSON 后解析出daily数组,再用map把日期、最高温、最低温分别抽出来填充到 ECharts 的data字段里。城市下拉框的change事件绑定了重新加载函数,实现了多城市切换。整个页面的数据流是:用户操作 → 前端 fetch 请求 → Flask 转发 → weather_service 返回数据 → 前端渲染图表,链路清晰,答辩时按这个顺序讲就行。

参数说明上有两个容易出错的地方。tooltip配置成trigger: "axis"后,鼠标悬停时会把同一 X 轴上的多条数据线一起展示,比默认的item模式更适合折线图。legend里的data数组必须和series中每条数据的name一致,否则图例项显示空白,这是 ECharts 初学阶段最常见的配置坑。

5. 避坑清单:python 环境、请求失败、图表不显示的四个典型翻车现场

5.1 VSCode 配置 Python 解释器失败,代码明明对却跑不起来

现象:在 VSCode 里打开项目,点运行,终端一直报“ModuleNotFoundError: No module named 'flask'”,但命令行里pip show flask又能看到包已安装。

原因:VSCode 右下角或命令面板里选择的 Python 解释器,和你在终端里执行pip时使用的解释器不是同一个。最常见的情况是机器上装了多个 Python,一个来自官网安装包,一个来自 Microsoft Store,VSCode 默认选了 Store 版,而包装在了官方版里。另一个变体是用了虚拟环境目录.venv,但 VSCode 没有识别到它,直接用了全局解释器。

解决:按Ctrl+Shift+P,输入Python: Select Interpreter,在弹出的列表里选择带.venv字样的那个;如果没有,点击“Enter interpreter path”手动指向虚拟环境里的python.exe。选完之后,重新打开终端,执行python --version确认解释器路径已经切换。这里我还会顺手执行一遍pip install -r requirements.txt,保证虚拟环境里的依赖齐全。

5.2 天气 API 返回结构变化与请求超时:永远按 dict 的 get 写

现象:项目开发时一切正常,答辩前一天再跑,控制台抛出KeyError: 'now',页面直接 500。

原因:远程 API 的返回结构并非一成不变。和风天气在接口升级或参数异常时,返回的 JSON 里可能没有now字段,而是多了一个error字段。如果你在代码里写死了raw["now"]["temp"],一旦字段缺失,整个程序当场崩溃。另一个场景是校园网到 API 服务器的链路不稳定,requests.get默认会无限等待,网络抖动时请求挂住,页面一直转圈。

解决:写数据解析代码时,强制自己使用.get()方法访问字典字段。在weather_service.py里再包一层 try-except,捕获requests.RequestException和ValueError,失败时返回None,让 Flask 接口层直接返回错误提示而不是崩溃。我给这个兜底逻辑取名叫“静默降级”:有数据就展示数据,没数据就展示错误信息,至少页面不是白屏。

5.3 ECharts 折线图 X 轴乱序、中文不显示:两个必查配置

现象:折线图画出来了,但 X 轴日期顺序是乱的,比如“3号、1号、2号”;或者在 Linux 服务器上部署后,图表里的中文全部变成方框。

原因:X 轴乱序是因为后端返回的daily数组没有按日期排序,和风天气接口偶尔会返回非严格时间顺序的数据,而 ECharts 对category类型 X 轴是直接按数据原顺序渲染的,它不会帮你排序。中文变方框则是因为服务器操作系统缺少中文字体,ECharts 渲染文字时找不到对应的字形,只能画出一个空心框。

解决:在weather_service.py清洗数据时,对daily列表按fxDate字段做一次显式排序。中文乱码的解决则分两步:本地开发如果遇到,先检查浏览器编码是否为 UTF-8;服务器部署遇到,就在系统里安装中文字体,命令是apt install fonts-noto-cjk(Debian/Ubuntu 系)或yum install fontconfig后补充字体。做一次排序、确认一次字体,这两个习惯能让图表在各种环境下都表现稳定。

5.4 Flask 端口被占用与模板修改不生效:调试期的两个低级错误

现象:执行python app.py启动服务,终端报OSError: [Errno 98] Address already in use;或者明明改了index.html里的标题,刷新浏览器却还是旧内容。

原因:端口占用是因为上一个 Flask 进程没有正常终止,常见的场景是你用 Ctrl+Z 挂起了终端进程,但它其实还在后台占用 5000 端口。模板修改不刷新则是 Flask 的一个默认行为:模板文件编译后会做缓存,即使你关闭了浏览器标签页,服务端缓存还在,除非改文件名或重启服务。

解决:端口占用时,在终端执行kill -9 $(lsof -t -i:5000)强制结束占用进程,或者干脆app.run(port=5001)换一个端口。模板缓存问题的最优解是设置app.config["TEMPLATES_AUTO_RELOAD"] = True,让 Flask 在模板文件被修改后自动重新加载;更省事的做法是开发期间保持debug=True,它本身就会监听模板变动并触发重载。这两个问题虽然低级,但在答辩调试现场非常容易消耗时间,一旦出现要能快速反应。

5.5 答辩前网络不稳:用本地缓存 Mock 数据兜底

现象:答辩现场教室的 Wi-Fi 只能访问局域网,无法连接外部天气 API,所有图表加载失败。

原因:很多同学只把网络请求写在启动阶段,没有考虑 API 完全不可达的离线场景。课堂演示环境的网络策略通常比开发环境严格,外部 API 域名可能被防火墙限制,而你的程序没有兜底方案,整个展示就卡在第一步。

解决:利用前面写好的文件缓存机制作为降级策略。在weather_service.py中,当 API 请求抛出异常、且当前不存在有效缓存时,不要直接返回None,而是读取项目根目录下预置的mock_data.json作为假数据返回。这份假数据你在开发阶段就生成好,格式与真实清洗结果完全一致,页面照常渲染,只是数据源变成了本地文件。答辩时如果出现断网,你只需要说“接口不可达时系统会自动降级到本地缓存”,这一句话反而是个加分项,因为它体现了你对异常场景的处理意识。

6. 答辩现场加分技巧:四个细节把“做完”变成“做好”

期末大作业答辩的时间通常只有五到八分钟,如何在这么短的时间里让老师记住你的项目,靠的不是口才,而是四个可验证的细节。

第一个细节是数据接口可浏览。答辩前把 Flask 服务起好,老师走近时,你直接在浏览器地址栏输入localhost:5000/api/weather?city=广州,把 JSON 数据展示在屏幕上,然后切回页面说“前端图表的数据就来自这个接口”。这一步能直观地证明你的系统不是静态页面,而是真正有数据链路的。

第二个细节是加上空气质量维度。在天气卡片里增加 AQI 指数和空气质量等级,数据源同样来自和风天气的空气质量接口,把air字段里的aqi和category清洗进config.py,前端多显示一行颜色标签。这一项会显著拉开你和其他只做温度展示的同学的差距,因为“空气质量”意味着你对数据源的理解不局限于基础天气字段。

第三个细节是页面底部标注数据更新时间。很多同学忽略了这个 10 秒就能做完的功能,但它的效果立竿见影。在接口返回的updated_at字段渲染成“数据更新于 14:32”一行小字,老师看了会觉得你考虑到了数据的时效性,这属于“答辩瞬间就能感知到的专业度”。

第四个细节是准备好一张架构示意图。不需要复杂的图表工具,用 PPT 画一个三层的箭头图:数据源 → Flask 服务 → 浏览器 ECharts。讲项目时先花三十秒指着图把链路讲一遍,再实际演示页面。这个顺序让老师先获得全局认知,再看具体实现,理解成本大幅降低。

这四件事做完,你的期末大作业就不只是“代码能跑”,而是“可解释、可演示、有设计感”。我每一届带学生做课程项目都会强调一句:高分不是靠炫技,是靠让老师在三分钟内看懂你做的是什么、怎么做出来的、遇到问题怎么处理。这条经验屡试不爽,希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询