1. 项目概述
最近在做一个很有意思的小项目——用Flask搭建一个城市天气可视化分析平台。这个项目的核心目标是把枯燥的天气数据变成直观的图表,帮助用户快速了解不同城市的天气变化趋势。作为一个经常需要出差的人,我特别需要一个能直观展示目的地天气状况的工具,而不是简单看几个数字。
这个项目虽然不大,但涉及的技术栈还挺全面的:前端用Bootstrap做响应式布局,后端用Flask处理数据请求,数据存储用SQLite,可视化部分用ECharts。最有趣的是天气数据的获取方式,我尝试了多种API接口,最后选定了一个稳定且免费的方案。
2. 技术选型与架构设计
2.1 为什么选择Flask
Flask作为Python轻量级Web框架,特别适合这种中小型项目。相比Django的全家桶,Flask更灵活,可以根据需求自由组合扩展。在这个项目中,我们只需要处理简单的路由和模板渲染,Flask完全够用。
另一个重要原因是Flask的扩展生态。我们需要用到的功能基本都有现成的扩展:
- Flask-SQLAlchemy:数据库ORM
- Flask-WTF:表单处理
- Flask-Login:用户认证(虽然这个项目暂时不需要)
2.2 数据获取方案
天气数据来源是这类项目的核心。经过对比测试,我最终选择了和风天气的免费API,它有以下几个优势:
- 免费版每天1000次调用足够个人使用
- 数据更新频率高(每小时)
- 提供未来7天预报和历史数据
- 响应速度快(平均200ms)
API调用示例:
python复制import requests
def fetch_weather(city):
url = f"https://free-api.heweather.net/s6/weather/now?location={city}&key=YOUR_KEY"
response = requests.get(url)
return response.json()
2.3 前端可视化方案
可视化部分我选择了ECharts,主要考虑:
- 丰富的图表类型(折线图、柱状图、雷达图等)
- 响应式设计,适配不同设备
- 活跃的社区和详细文档
- 与Flask集成简单
3. 核心功能实现
3.1 数据库设计
虽然SQLite性能不如MySQL,但对于这种小型项目完全够用。我们设计了两个主要表:
sql复制CREATE TABLE cities (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
code TEXT NOT NULL UNIQUE
);
CREATE TABLE weather_records (
id INTEGER PRIMARY KEY AUTOINCREMENT,
city_id INTEGER,
date TEXT,
temp REAL,
humidity INTEGER,
wind_speed REAL,
condition TEXT,
FOREIGN KEY (city_id) REFERENCES cities (id)
);
3.2 后端API实现
Flask路由设计遵循RESTful风格:
python复制@app.route('/api/weather/<city>', methods=['GET'])
def get_weather(city):
# 先查缓存
cached = cache.get(city)
if cached:
return jsonify(cached)
# 没有缓存则调用API
data = fetch_weather(city)
cache.set(city, data, timeout=3600) # 缓存1小时
return jsonify(data)
@app.route('/api/history/<city>', methods=['GET'])
def get_history(city):
# 查询数据库中的历史记录
records = db.session.query(WeatherRecord).filter_by(city_id=city).all()
return jsonify([r.to_dict() for r in records])
3.3 前端页面开发
主页面采用Bootstrap的卡片式布局,每个城市一个卡片。点击卡片会展开详细图表:
html复制<div class="city-card" onclick="showDetail('{{ city.code }}')">
<h5>{{ city.name }}</h5>
<p>当前温度: {{ weather.temp }}°C</p>
<p>天气状况: {{ weather.condition }}</p>
</div>
<div id="detail-{{ city.code }}" class="detail-panel">
<div id="chart-{{ city.code }}" style="width:100%;height:400px;"></div>
</div>
4. 可视化图表实现
4.1 温度变化折线图
展示最近7天温度变化:
javascript复制function initTempChart(cityCode, data) {
const chart = echarts.init(document.getElementById(`chart-${cityCode}`));
const option = {
title: { text: '温度变化趋势' },
tooltip: { trigger: 'axis' },
xAxis: { data: data.dates },
yAxis: { type: 'value' },
series: [{
name: '最高温度',
type: 'line',
data: data.maxTemps
},{
name: '最低温度',
type: 'line',
data: data.minTemps
}]
};
chart.setOption(option);
}
4.2 天气状况雷达图
展示各种天气指标的对比:
javascript复制function initRadarChart(cityCode, data) {
const chart = echarts.init(document.getElementById(`radar-${cityCode}`));
const option = {
radar: {
indicator: [
{ name: '温度', max: 40 },
{ name: '湿度', max: 100 },
{ name: '风速', max: 20 },
{ name: '降水概率', max: 100 }
]
},
series: [{
type: 'radar',
data: [{
value: [data.temp, data.humidity, data.windSpeed, data.precip],
name: '天气指标'
}]
}]
};
chart.setOption(option);
}
5. 性能优化技巧
5.1 数据缓存策略
天气数据变化不频繁,可以适当缓存:
- 内存缓存:使用Flask-Caching扩展
- 数据库缓存:每小时自动更新
- 前端本地存储:sessionStorage保存最近查看的城市
python复制from flask_caching import Cache
cache = Cache(config={'CACHE_TYPE': 'simple'})
cache.init_app(app)
@app.route('/api/weather/<city>')
@cache.cached(timeout=3600, key_prefix='weather_')
def get_weather(city):
return fetch_weather(city)
5.2 异步加载优化
对于历史数据等大量数据,采用分页加载:
javascript复制function loadHistory(cityCode, page=1) {
fetch(`/api/history/${cityCode}?page=${page}`)
.then(response => response.json())
.then(data => renderHistory(data));
}
5.3 图表渲染优化
ECharts图表在初次渲染时可能较慢,可以采用以下优化:
- 延迟加载非首屏图表
- 使用轻量级的SVG渲染器
- 适当降低动画效果
javascript复制// 只在卡片展开时初始化图表
function showDetail(cityCode) {
if (!window.charts[cityCode]) {
initCharts(cityCode);
}
}
6. 部署与扩展
6.1 生产环境部署
开发完成后,我选择了以下部署方案:
- 服务器:轻量级云服务器(1核2G足够)
- Web服务器:Nginx + Gunicorn
- 进程管理:Supervisor
Gunicorn启动命令:
bash复制gunicorn -w 4 -b 0.0.0.0:8000 app:app
Nginx配置示例:
nginx复制server {
listen 80;
server_name weather.example.com;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
}
location /static {
alias /path/to/static/files;
}
}
6.2 可能的扩展方向
这个项目还有很大的扩展空间:
- 增加用户系统,保存个人偏好城市
- 添加天气预警推送功能
- 实现多城市对比分析
- 接入空气质量数据
- 开发移动端APP版本
7. 踩坑记录与解决方案
7.1 API限流问题
免费API通常有调用限制,我的解决方案:
- 合理设置缓存时间
- 客户端限制频繁刷新
- 多个API备用方案
python复制# 备用API方案
def fetch_weather_backup(city):
try:
return fetch_heweather(city)
except:
return fetch_openweather(city)
7.2 时区处理
天气数据可能涉及不同时区,需要统一处理:
python复制from pytz import timezone
def convert_time(dt, tz='Asia/Shanghai'):
return dt.astimezone(timezone(tz))
7.3 移动端适配
Bootstrap虽然响应式,但图表需要额外处理:
- 监听窗口大小变化
- 图表自适应重绘
javascript复制window.addEventListener('resize', function() {
Object.values(window.charts).forEach(chart => {
chart.resize();
});
});
8. 项目总结
这个项目从构思到上线用了大约两周时间,期间最大的收获是学会了如何将多个技术栈有效整合。Flask的轻量灵活让我印象深刻,ECharts的强大可视化能力也让数据展示变得简单有趣。
几个特别实用的经验:
- API文档一定要仔细阅读,特别是免费服务的限制条款
- 数据库设计要考虑未来可能的扩展
- 缓存是提升性能的利器,但要合理设置过期时间
- 移动端适配不能只靠框架,需要实际设备测试
项目代码已经开源在GitHub,包含完整部署文档。对于想学习Flask全栈开发的同学,这个项目是个不错的练手选择。后续我计划增加用户登录和城市收藏功能,让平台更加实用。