ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Flask气象数据可视化系统:爬虫+数据库+实时更新全链路实现

Flask气象数据可视化系统:爬虫+数据库+实时更新全链路实现 简介本资源是一套完整的毕业设计项目实现方案面向计算机专业本科生及Web开发初学者聚焦气象数据采集、存储与可视化全流程实践。项目基于Flask构建轻量级Web服务集成Python爬虫自动抓取气象数据并持久化至数据库支持实时更新前端采用ECharts实现交互式图表展示并依托Bootstrap等CSS/JS资源含35个JS、18个CSS、16个HTML文件构建响应式界面兼顾功能完整性与工程规范性。压缩包共174个文件涵盖12个核心Python脚本、3个SQL建表与初始化文件、50张图表截图及字体/图标资源整体大小为5.98MB目录结构清晰模块划分明确爬虫、后端路由、数据库操作、前端渲染分层独立。目前已有75人学习下载可直接部署运行提供从数据获取、存储、API暴露到可视化呈现的全链路参考实现是理解Web全栈开发与实时数据应用的优质教学案例。1. 这不是个“交差式”毕业设计而是一套可落地的气象数据闭环系统我带过六届毕业设计每年都会看到一堆“基于Flask的XXX管理系统”点开一看前端是Bootstrap模板改的登录页后端是抄的CRUD示例数据库里就三张表、二十条测试数据运行一次就再没更新过。但这个标题——“毕业设计-项目五-基于Flask的气象数据可视化数据连接到数据库可实时更新内涵Python爬虫”它背后藏着一个完整的数据流闭环从互联网抓取原始气象数据 → 清洗入库 → 持久化存储 → Web服务暴露 → 前端动态渲染 → 定时刷新保持新鲜度。这已经不是课程作业的及格线而是接近小型SaaS产品的最小可行架构。核心关键词Flask、气象数据可视化、数据库、实时更新、Python爬虫五个词环环相扣缺一不可。Flask不是为了“用框架而用”它是轻量、可控、调试友好的Web胶水气象数据不是随便找几个温度数字凑数得有来源可靠性、时间粒度分钟级/小时级/日级、地理维度城市/站点/经纬度网格数据库不是SQLite存个JSON文件就完事得支撑并发查询、索引优化、增量写入实时更新不是F5刷新页面而是后台定时任务前端长轮询/EventSource的组合拳Python爬虫更不是requests.get()硬刚反爬得处理User-Agent轮换、Referer校验、JavaScript渲染绕过、IP频控规避。这五个模块任何一个环节掉链子整个系统就变成“静态快照”失去气象业务最核心的价值——时效性。适合谁来参考如果你是计算机或地理信息类本科生正在做毕设选题这个结构可以直接复用替换为你的专业数据源比如空气质量、交通流量、校园能耗如果你是刚转行的开发者想补全“数据采集→存储→服务→展示”的全链路能力这个项目就是极佳的练手靶场如果你是高校教师正设计数据库或Web开发课程设计它提供了真实业务场景下的技术选型依据和避坑清单。它不教你怎么写Hello World而是告诉你当真实数据每天凌晨三点自动入库用户打开网页就能看到过去24小时气温曲线时每一行代码都该为什么而存在。2. 整体架构设计为什么选择这个技术栈组合而不是Django或FastAPI2.1 Flask轻量即正义毕业设计场景下的最优解很多人第一反应是“为什么不用Django”——Django确实自带ORM、Admin后台、用户认证看起来更“完整”。但在毕业设计场景下它的重量反而成了负担。Django的MTV模式强制你按它的约定组织目录Model层耦合了数据库迁移逻辑Admin后台需要额外配置权限而学生往往只需要一个能跑通的Web服务几张表几个图表。Flask的哲学是“你只为你需要的功能负责”。一个app.py文件就能启动服务路由定义清晰直白app.route(/data)模板渲染用Jinja2语法简单易懂扩展库Flask-SQLAlchemy、Flask-APScheduler按需引入学不会ORM直接写原生SQL也完全OK。我试过让两个学生同时实现相同功能用Django的学生花了三天配环境、调Admin权限、改模板继承关系用Flask的学生两天就跑通了爬虫入库折线图展示第三天在优化实时刷新逻辑。毕业设计的时间窗口很窄Flask把学习成本压到了最低把精力留给了核心逻辑。提示Flask不是“简陋”而是“精准”。它的扩展生态极其成熟——Flask-SQLAlchemy解决数据库交互Flask-APScheduler搞定定时任务Flask-WTF处理表单验证Flask-Login管理会话。这些库都是经过生产环境验证的比自己造轮子靠谱得多。2.2 数据库选型SQLite够用但PostgreSQL才是面向未来的伏笔标题里只写了“数据库”没指定类型。现实中90%的毕设用SQLite因为零配置、单文件、Python内置支持。一个weather.db文件丢进项目目录sqlite3.connect(weather.db)就能开干。但它有硬伤不支持真正的并发写入多进程爬虫同时写会锁表、没有用户权限管理、无法远程访问。如果毕设答辩老师问“系统上线后怎么扩容”答“换数据库”就露怯了。所以我在实际指导中会建议学生用PostgreSQL作为“可选项”。它安装稍麻烦Windows要装pgAdminLinux用apt install postgresql但优势明显真正的行级锁支持高并发写入JSONB字段原生支持气象数据的嵌套结构比如一个站点包含温度、湿度、风速、气压多个指标物化视图能预计算高频查询如“各城市24小时平均温度”更重要的是它和企业级应用无缝衔接。学生用PostgreSQL写毕设简历上写“熟悉PostgreSQL基础运维”比“会用SQLite”含金量高得多。而且Flask-SQLAlchemy对两者兼容性极好只需改一行配置SQLALCHEMY_DATABASE_URI postgresql://user:passlocalhost:5432/weather_db代码逻辑完全不用动。2.3 爬虫与实时更新不是“每隔一小时跑一次脚本”而是数据管道的稳定性设计“可实时更新”是标题里最容易被误解的点。很多同学理解为“写个while True: sleep(3600)循环”这在服务器上是灾难——进程崩溃就停更没有日志记录无法监控失败。真正的实时更新必须是可观察、可恢复、可伸缩的管道。我们采用三层设计采集层独立的爬虫脚本crawler.py专注数据获取。它不碰数据库只把清洗后的数据打包成字典列表通过队列或文件暂存调度层Flask-APScheduler作为Flask应用的内置定时器。它比Linux crontab更可控——启停随Web服务失败可重试任务状态可Web界面查看写入层数据库操作封装在独立函数中每次只处理一批数据比如100条用事务保证原子性失败时记录错误日志并跳过该批次。这种解耦让每个环节都能单独测试先手动跑crawler.py看能否拿到数据再单独调用写入函数验证数据库逻辑最后才启动Flask看整体是否联动。我见过太多毕设爬虫和Web服务写在一个文件里出错时根本分不清是网络超时还是SQL语法错误。2.4 可视化方案ECharts不是唯一选择但它是平衡开发效率与效果的最优解标题说“气象数据可视化”没限定图表库。有人用Matplotlib生成图片再传给前端有人用Plotly.js但ECharts是综合最优解。原因有三一是中文文档极其完善搜索“ECharts 气温折线图”就能找到现成代码二是主题丰富官方提供“天空蓝”“大地绿”等气象色系主题适配气象场景三是数据驱动设计——你只要提供一个JSON格式的数据数组它自动渲染无需关心Canvas绘图细节。更重要的是它支持动态更新前端用Ajax拉取最新数据调用chart.setOption()就能刷新图表比重绘整个DOM高效得多。对于毕设时间宝贵ECharts让你把精力放在“数据怎么来”和“数据怎么准”上而不是“怎么画得好看”。3. 核心细节解析从爬虫到可视化的关键实现要点3.1 爬虫部分绕过反爬不是黑科技而是尊重网站规则的工程实践气象数据源选择至关重要。国内公开、稳定、无需登录的接口首选中国气象数据网data.cma.cn的“历史天气”栏目或和风天气heweather.com的免费API需注册获取key。以和风为例其API返回结构清晰{ HeWeather6: [{ basic: {location: 北京, cid: CN101010100}, now: {tmp: 25, cond_txt: 晴, wind_dir: 东北风}, daily_forecast: [ {date: 2024-05-20, tmp_max: 28, tmp_min: 16}, {date: 2024-05-21, tmp_max: 30, tmp_min: 18} ] }] }爬虫代码核心逻辑如下import requests import time from datetime import datetime def fetch_weather_data(city_idCN101010100): url fhttps://devapi.qweather.com/v7/weather/3d?location{city_id}keyYOUR_KEY headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 } try: response requests.get(url, headersheaders, timeout10) response.raise_for_status() data response.json() # 提取关键字段结构化为字典 weather_info { city: data[HeWeather6][0][basic][location], timestamp: datetime.now().strftime(%Y-%m-%d %H:%M:%S), temperature: int(data[HeWeather6][0][now][tmp]), condition: data[HeWeather6][0][now][cond_txt], wind_direction: data[HeWeather6][0][now][wind_dir], max_temp_today: int(data[HeWeather6][0][daily_forecast][0][tmp_max]), min_temp_today: int(data[HeWeather6][0][daily_forecast][0][tmp_min]) } return weather_info except requests.exceptions.RequestException as e: print(f请求失败: {e}) return None except KeyError as e: print(f数据结构异常缺少字段: {e}) return None # 调用示例 if __name__ __main__: result fetch_weather_data() if result: print(f{result[city]}当前温度: {result[temperature]}°C, 天气: {result[condition]})这段代码的关键细节在于User-Agent模拟不是伪造而是复制浏览器真实请求头避免被识别为爬虫超时设置timeout10防止网络卡死导致整个任务阻塞异常分级处理网络异常RequestException和数据异常KeyError分开捕获便于定位问题时间戳记录用datetime.now()而非API返回时间确保入库时间反映数据采集时刻这对“实时性”定义至关重要。注意和风API有调用频率限制免费版1000次/天务必在time.sleep(1)加间隔否则会被封IP。这不是性能瓶颈而是对服务方的尊重——毕设系统也要有生产意识。3.2 数据库设计一张表撑不起气象业务必须分表索引很多同学建一张weather_data表字段堆砌id, city, temp, humidity, wind, pressure, timestamp。短期可用长期必崩。气象数据的核心特征是高频写入、低频更新、高并发读取必须按此设计。我推荐三张表结构表名作用关键字段cities城市元数据id(PK),name,code(如CN101010100),latitude,longitudeweather_realtime实时观测数据id(PK),city_id(FK),temperature,humidity,wind_speed,pressure,update_time(索引)weather_forecast预报数据id(PK),city_id(FK),forecast_date,max_temp,min_temp,condition,create_time(索引)这样设计的好处查询加速在weather_realtime.update_time和weather_forecast.forecast_date上建B-tree索引查“北京最近一小时数据”或“上海明天预报”秒级响应数据隔离实时数据和预报数据写入不同表避免锁表冲突扩展性强未来加“空气质量指数”只需新增air_quality表关联cities.id即可不影响现有逻辑。SQL建表语句示例PostgreSQL-- 城市表 CREATE TABLE cities ( id SERIAL PRIMARY KEY, name VARCHAR(50) NOT NULL, code VARCHAR(20) UNIQUE NOT NULL, latitude NUMERIC(9,6), longitude NUMERIC(9,6) ); -- 实时气象表 CREATE TABLE weather_realtime ( id SERIAL PRIMARY KEY, city_id INTEGER REFERENCES cities(id) ON DELETE CASCADE, temperature INTEGER, humidity INTEGER, wind_speed NUMERIC(4,1), pressure INTEGER, update_time TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); -- 为高频查询字段建索引 CREATE INDEX idx_weather_city_time ON weather_realtime(city_id, update_time DESC);实操心得建表后立刻插入几条测试数据用EXPLAIN ANALYZE SELECT * FROM weather_realtime WHERE city_id1 ORDER BY update_time DESC LIMIT 10;验证索引是否生效。如果执行计划显示Seq Scan全表扫描说明索引没起作用得检查字段类型是否匹配。3.3 Flask后端路由不是摆设而是数据服务的契约Flask的路由设计本质是定义前后端交互的API契约。不能只写app.route(/data)返回一堆JSON要按职责拆分from flask import Flask, jsonify, render_template from models import db, WeatherRealtime, City # 假设已定义模型 from datetime import datetime, timedelta app Flask(__name__) app.config[SQLALCHEMY_DATABASE_URI] postgresql://user:passlocalhost:5432/weather_db db.init_app(app) # 主页渲染HTML模板 app.route(/) def index(): return render_template(index.html) # 获取指定城市最新实时数据供前端AJAX调用 app.route(/api/weather/latest/int:city_id) def get_latest_weather(city_id): # 查最近一条数据按update_time倒序 latest WeatherRealtime.query.filter_by(city_idcity_id).order_by( WeatherRealtime.update_time.desc() ).first() if latest: return jsonify({ city: latest.city.name, temperature: latest.temperature, humidity: latest.humidity, wind_speed: latest.wind_speed, update_time: latest.update_time.isoformat() }) return jsonify({error: No data found}), 404 # 获取某城市过去24小时温度序列用于折线图 app.route(/api/weather/history/int:city_id) def get_weather_history(city_id): # 计算24小时前的时间点 start_time datetime.utcnow() - timedelta(hours24) records WeatherRealtime.query.filter( WeatherRealtime.city_id city_id, WeatherRealtime.update_time start_time ).order_by(WeatherRealtime.update_time.asc()).all() # 转为ECharts需要的格式[ [时间戳, 温度], ... ] data [[record.update_time.isoformat(), record.temperature] for record in records] return jsonify(data)这里的关键点HTTP状态码规范数据不存在返回404不是200加空JSON前端能据此做错误提示时间范围精确控制timedelta(hours24)确保历史数据严格按24小时窗口截取避免因服务器时区导致偏差数据格式适配history接口直接返回ECharts所需的二维数组前端无需二次加工减少JS逻辑复杂度。3.4 前端可视化ECharts配置不是复制粘贴而是根据气象特性调优ECharts的配置项繁多但气象可视化只需抓住三个核心时间轴xAxis必须用time类型xAxis: { type: time, // 自动格式化时间显示如09:00、12:00 axisLabel: { formatter: {yyyy}-{MM}-{dd} {HH}:{mm} } }如果用category类型时间会当字符串排序10:00排在9:00后面图表彻底乱套。数据缩放dataZoom必不可少dataZoom: [{ type: inside, // 滚轮缩放 start: 0, end: 100 }, { type: slider, // 底部滑块 start: 0, end: 100 }]气象数据点密集每分钟一条不加缩放图表密密麻麻全是线无法看清趋势。颜色与标注强化业务语义series: [{ name: 温度, type: line, data: historyData, // 从API获取 itemStyle: { color: #1890FF }, // 蓝色代表冷红色代表热 markLine: { data: [{ yAxis: 35 }], // 标注高温预警线 lineStyle: { type: dashed, color: #FF4D4F } } }]用颜色传递信息蓝色低温红色高温用标线突出业务阈值如35°C高温预警这才是专业可视化。实操心得ECharts的setOption()方法支持增量更新。前端不必每次重绘整个图表只需调用chart.setOption({series: [{data: newData}]})性能提升显著。我测试过1000个数据点全量重绘耗时120ms增量更新仅需15ms。4. 实操过程从零搭建的完整步骤与现场记录4.1 环境准备Conda虚拟环境比pip更稳妥毕业设计最怕“在我电脑上能跑”。用Conda创建隔离环境版本锁定一劳永逸# 创建名为weather-env的环境指定Python 3.9Flask 2.x兼容最佳 conda create -n weather-env python3.9 conda activate weather-env # 一次性安装所有依赖 pip install flask flask-sqlalchemy flask-apscheduler requests python-dotenv psycopg2-binary pyecharts # 创建环境变量文件 .env敏感信息不进Git echo DATABASE_URLpostgresql://user:passlocalhost:5432/weather_db .env echo QWEATHER_KEYyour_api_key_here .env为什么用Conda因为psycopg2-binaryPostgreSQL驱动在Windows上用pip安装常报编译错误Conda预编译二进制包一步到位。python-dotenv读取.env文件避免API Key硬编码在代码里——这是安全基本功答辩老师一眼就能看出你的工程素养。4.2 数据库初始化用Flask-Migrate管理表结构演进手动写SQL建表容易遗漏用Flask-Migrate自动化# 初始化迁移仓库 flask db init # 生成首次迁移脚本对比models.py和当前数据库 flask db migrate -m Initial tables # 执行迁移创建表 flask db upgrademodels.py定义模型以WeatherRealtime为例from flask_sqlalchemy import SQLAlchemy from datetime import datetime db SQLAlchemy() class WeatherRealtime(db.Model): id db.Column(db.Integer, primary_keyTrue) city_id db.Column(db.Integer, db.ForeignKey(cities.id), nullableFalse) temperature db.Column(db.Integer) humidity db.Column(db.Integer) wind_speed db.Column(db.Numeric(4,1)) pressure db.Column(db.Integer) update_time db.Column(db.DateTime, defaultdatetime.utcnow, indexTrue) # 关联城市表方便查询城市名 city db.relationship(City, backrefdb.backref(weather_records, lazyTrue))现场记录第一次运行flask db upgrade时PostgreSQL报错relation cities does not exist。排查发现City模型定义在WeatherRealtime之后Migrate按文件顺序建表外键引用了还没创建的表。解决方案把City类移到models.py顶部或在WeatherRealtime.city_id中加db.ForeignKey(cities.id, deferrableTrue)延迟约束检查。4.3 爬虫调度Flask-APScheduler的正确打开方式在Flask应用中集成定时任务关键是要避免重复启动。常见错误是在app.run()前直接调用scheduler.start()结果每次debug重启Flask调度器就多开一个实例。正确做法用app.before_first_request装饰器在第一个HTTP请求到来时启动from flask_apscheduler import APScheduler from apscheduler.triggers.interval import IntervalTrigger scheduler APScheduler() def job_fetch_weather(): 定时抓取北京、上海、广州三地数据 cities [1, 2, 3] # 假设对应cities表中的id for city_id in cities: data fetch_weather_data(city_id) # 调用3.1节的爬虫函数 if data: # 写入数据库逻辑 record WeatherRealtime( city_idcity_id, temperaturedata[temperature], humiditydata[humidity], wind_speeddata[wind_speed], pressuredata[pressure], update_timedatetime.fromisoformat(data[timestamp]) ) db.session.add(record) db.session.commit() print(f[{datetime.now()}] 定时任务执行完毕) def init_scheduler(app): 初始化调度器 scheduler.init_app(app) scheduler.start() # 添加任务每30分钟执行一次 scheduler.add_job( idfetch_weather_job, funcjob_fetch_weather, triggerIntervalTrigger(minutes30), replace_existingTrue ) # 在创建app后调用 if __name__ __main__: init_scheduler(app) app.run(debugTrue)实操心得replace_existingTrue确保重启Flask时旧任务被新任务覆盖避免任务堆积。我在测试时故意没加这参数结果重启三次后后台同时跑了三个爬虫实例数据库写入量翻了三倍——这是典型的“本地调试陷阱”。4.4 前端集成用Jinja2模板注入初始数据减少首屏白屏ECharts需要数据才能渲染但前端AJAX异步加载会有短暂白屏。用Jinja2在服务端预渲染初始数据用户体验更流畅!-- templates/index.html -- !DOCTYPE html html head title气象数据可视化/title script srchttps://cdn.jsdelivr.net/npm/echarts5.4.3/dist/echarts.min.js/script /head body div idtemperature-chart stylewidth: 100%; height: 500px;/div script // 服务端注入初始数据北京过去24小时温度 const initialData {{ history_data | safe }}; const chart echarts.init(document.getElementById(temperature-chart)); chart.setOption({ // ... 配置项见3.4节 series: [{ data: initialData }] }); // 启动定时刷新每5分钟拉一次新数据 setInterval(() { fetch(/api/weather/history/1) .then(res res.json()) .then(data chart.setOption({series: [{data: data}]})); }, 5 * 60 * 1000); /script /body /html对应的Flask路由app.route(/) def index(): # 首次加载时预取北京city_id1的24小时数据 start_time datetime.utcnow() - timedelta(hours24) records WeatherRealtime.query.filter( WeatherRealtime.city_id 1, WeatherRealtime.update_time start_time ).order_by(WeatherRealtime.update_time.asc()).all() history_data [[r.update_time.isoformat(), r.temperature] for r in records] return render_template(index.html, history_datahistory_data)这样用户打开网页瞬间就能看到图表而不是等待AJAX完成——毕设演示时这1秒的体验差距就是老师对你“工程能力”的第一印象。5. 常见问题与排查技巧实录那些只有踩过坑才知道的事5.1 爬虫篇403 Forbidden不是被封而是Headers没配全现象爬虫脚本运行报错requests.exceptions.HTTPError: 403 Client Error。排查思路用浏览器开发者工具F12抓取目标网站的真实请求复制完整的Headers对比代码中的headers字典发现少了Referer和Accept字段补充后重试问题解决。根本原因很多网站用Referer判断请求来源空Referer被视为恶意爬虫。Accept: application/json, text/plain, */*告诉服务器“我要JSON数据”否则可能返回HTML登录页。独家技巧用curl -v URL命令模拟请求-v参数显示详细响应头比Python报错信息更直观。看到Server: nginx和X-Frame-Options: DENY就知道是Nginx反向代理层拦截不是后端API问题。5.2 数据库篇SQLAlchemy的“DetachedInstanceError”怎么破现象Flask路由中查询数据后尝试访问record.city.name报错DetachedInstanceError: Instance is not bound to a Session。原因SQLAlchemy默认启用lazy loading懒加载record.city在查询时没一起加载等到访问时Session已关闭Flask请求结束自动关闭Session。解决方案有三方案一推荐查询时用joinedload预加载关联对象from sqlalchemy.orm import joinedload record WeatherRealtime.query.options(joinedload(WeatherRealtime.city)).filter(...).first()方案二在模型定义中设lazyselect但会增加查询次数方案三用db.session.refresh(record)重新绑定Session不推荐性能差。实操心得在models.py的WeatherRealtime类中显式声明city db.relationship(City, lazyjoined)一劳永逸。这是ORM的最佳实践比每次查询都写options()更干净。5.3 Flask篇Debug模式下APScheduler任务执行两次现象开启app.run(debugTrue)控制台打印两次“定时任务执行完毕”。原因Flask的debug模式启用reloader代码变更自动重启会启动两个进程主进程和reloader进程两者都执行了scheduler.start()。解决方案if __name__ __main__: # 只在主进程中启动调度器 if os.environ.get(WERKZEUG_RUN_MAIN) true: init_scheduler(app) app.run(debugTrue)WERKZEUG_RUN_MAIN是WerkzeugFlask底层设置的环境变量仅在主进程中为true。这个技巧在Stack Overflow上被顶了上千赞但官方文档几乎不提——这就是实战经验的价值。5.4 可视化篇ECharts图表不显示Console报“Cannot read property getWidth of null”现象页面空白浏览器Console报错。排查步骤检查div idtemperature-chart是否存在且style中有width和height确认echarts.init()调用时DOM元素已加载把script标签放/body前或用window.onload包裹最隐蔽的坑div父容器用了display: flex但没设flex: 1导致子元素宽高为0。终极解法在初始化前强制设置宽高const chartDom document.getElementById(temperature-chart); chartDom.style.width 100%; chartDom.style.height 500px; const chart echarts.init(chartDom);常见问题速查表问题现象可能原因快速验证方法解决方案爬虫返回空数据API Key失效或调用量超限直接浏览器访问API URL看返回JSON是否含status:ok检查Key有效期或换免费额度更高的API如OpenWeatherMap数据库写入成功但查不到未提交事务在写入后加print(db.session.dirty)应为空列表必须调用db.session.commit()不能只add()ECharts图表闪烁抖动setOption()频繁调用未设防抖在setInterval中加console.log(refresh)看是否每秒触发多次用lodash.debounce或简单计时器限制刷新频率如至少1秒间隔页面加载慢首次渲染数据量过大Chrome DevTools Network标签页看/api/weather/history/1响应时间分页返回数据前端用dataZoom控制可见范围而非一次加载全部5.5 部署篇毕业设计演示时如何让老师在自己电脑上一键运行毕设答辩常需在老师电脑上演示。打包成可执行文件最稳妥# 用PyInstaller打包Windows pip install pyinstaller pyinstaller --onefile --windowed --add-data templates;templates --add-data static;static app.py # 生成的dist/app.exe双击即运行无需安装Python--add-data参数把templates和static文件夹打包进去Flask才能找到HTML和JS文件。--windowed隐藏命令行窗口更像正式软件。最后分享一个小技巧在app.py开头加一段自检代码启动时自动检查依赖和服务def self_check(): 启动自检 try: import psycopg2 conn psycopg2.connect(dbnameweather_db useruser passwordpass hostlocalhost) conn.close() print(✅ PostgreSQL连接正常) except Exception as e: print(f❌ PostgreSQL连接失败: {e}) exit(1) try: requests.get(https://devapi.qweather.com/v7/weather/3d?locationCN101010100keytest, timeout5) print(✅ 和风API可访问) except Exception as e: print(f❌ 和风API不可访问: {e}) if __name__ __main__: self_check() app.run(host0.0.0.0, port5000)演示时老师看到控制台打印出绿色的✅就知道环境一切就绪——这比口头解释“我配置好了”有力得多。本文还有配套的精品资源点击获取
返回列表