Python天气可视化系统实战:Flask+PyEcharts+爬虫全链路解析
简介这份资源是一套基于Python的天气预报数据可视化分析系统完整源码包面向数据分析初学者、气象方向学生及需要搭建可视化平台的开发者。系统采用Flask提供Web服务借助PyEcharts实现图表展示并通过爬虫抓取实时天气数据涵盖实时查询、天气分析、历史数据查询等核心模块适合作为课程设计或毕业设计的参考项目。压缩包共231个文件约39.2MB包含18个py源码、58个pyc编译文件、59个js脚本以及pug模板、scss与less样式、css、html页面、sql建表脚本和docx说明文档等前端资源与后端逻辑分层清晰。目前已有177人学习下载。读者可从中获取完整项目结构、Flask路由与爬虫实现思路、Pandas与NumPy数据处理示例以及PyEcharts图表配置代码便于快速理解气象数据从采集、分析到可视化的全流程并在此基础上二次开发或排错调试。1. 从一份静态资源清单说起这套 Python 天气可视化系统到底能跑出什么如果你拿到手的是一堆bootstrap.css、sb-admin.css、dataTables.bootstrap4.css这类前端样式文件第一反应可能是“这不就是个后台模板吗”。但把这份资源清单和 Flask、PyEcharts、爬虫放在一起看它其实是一套完整的气象数据可视化分析系统的前端骨架——后端用 Python 抓数据、算指标前端用 SB Admin 这套经典后台主题把图表和表格撑起来。对做数据分析 python 方向的人来说这类项目的价值不在于算法多深而在于它把“数据采集 → 清洗入库 → 接口输出 → 图表渲染”这条链路完整跑通了拿来改一改就能变成自己的课程设计或内部工具。适合谁适合需要交一个能演示、能交互、能讲清楚数据流向的实战项目的人也适合想练 Flask PyEcharts 组合拳但不想从零搭前端的开发者。2. 拆开资源包Flask 后端与 SB Admin 前端怎么对上号2.1 后端骨架Flask 路由与数据接口的对应关系这套系统的后端核心是 Flask它负责三件事接收前端查询参数、调用爬虫或数据库取数、把结果以 JSON 格式吐给 PyEcharts。常见做法是建一个app.py作为入口里面定义几个关键路由。比如/返回主页面/api/realtime返回实时天气 JSON/api/history返回历史数据 JSON/api/analysis返回统计结果。下面是一个最小可跑的接口示例我一般会先把结构搭成这样再往里填业务from flask import Flask, jsonify, request import pandas as pd from datetime import datetime, timedelta app Flask(__name__) # 模拟一份历史数据实际项目里换成数据库查询或爬虫结果 def load_history(city, start_date, end_date): # 这里用 pandas 生成示例数据真实场景替换为 SQL 查询 date_range pd.date_range(startstart_date, endend_date, freqD) df pd.DataFrame({ date: date_range, temp_high: [20 i % 8 for i in range(len(date_range))], temp_low: [10 i % 5 for i in range(len(date_range))], weather: [晴, 多云, 阴, 小雨][:len(date_range)] }) return df app.route(/api/history) def history(): city request.args.get(city, 北京) days int(request.args.get(days, 7)) end datetime.now().strftime(%Y-%m-%d) start (datetime.now() - timedelta(daysdays)).strftime(%Y-%m-%d) df load_history(city, start, end) # 转成 PyEcharts 友好的列表结构 result { dates: df[date].dt.strftime(%m-%d).tolist(), high: df[temp_high].tolist(), low: df[temp_low].tolist(), weather: df[weather].tolist() } return jsonify(result) if __name__ __main__: app.run(debugTrue, port5000)这段代码的逻辑很直白前端用fetch或 Ajax 请求/api/history?city北京days7后端把日期、最高温、最低温、天气现象四个数组打包返回。参数说明上city默认北京days控制回溯天数实际项目里这两个参数会直接传给爬虫或 SQL 查询。注意debugTrue只适合开发阶段部署时要关掉否则会暴露调试信息。2.2 前端资源清单每个 CSS 文件在页面里管什么资源包里那一串 CSS 不是随便堆的它们有明确的层级关系。bootstrap.css和bootstrap-grid.css提供栅格和基础组件样式font-awesome.css负责图标sb-admin.css是整套后台主题的核心dataTables.bootstrap4.css专门管表格分页和排序的样式bootstrap-reboot.css做浏览器默认样式重置。我一般会在base.html里按这个顺序引入!-- 基础框架样式必须最先加载 -- link relstylesheet href{{ url_for(static, filenamecss/bootstrap.min.css) }} link relstylesheet href{{ url_for(static, filenamecss/font-awesome.min.css) }} !-- 后台主题样式覆盖 Bootstrap 默认外观 -- link relstylesheet href{{ url_for(static, filenamecss/sb-admin.css) }} !-- 表格插件样式只在有 DataTable 的页面引入 -- link relstylesheet href{{ url_for(static, filenamecss/dataTables.bootstrap4.css) }}顺序不能乱Bootstrap 打底Font Awesome 补图标SB Admin 覆盖配色和布局DataTables 最后补表格细节。如果sb-admin.css在 Bootstrap 之前加载页面布局会直接崩掉这是血泪经验。另外bootstrap-reboot.css一般不用单独引bootstrap.min.css里已经包含了 reboot 部分重复引入反而可能引起样式冲突。2.3 数据流串联从爬虫到 PyEcharts 的完整链路把前后端串起来看数据流向是这样的爬虫模块定时抓取气象数据源清洗后写入 SQLite 或 MySQLFlask 接口从数据库读取并做聚合计算前端页面加载时调用接口拿到 JSON 后交给 PyEcharts 渲染。PyEcharts 的用法有两种一种是后端生成图表配置直接嵌入模板另一种是前端用 ECharts 原生 JS 接收 JSON 再画图。这套资源更偏向后者因为 SB Admin 本身是纯前端主题PyEcharts 在这里主要用来生成options配置。常见做法是在 Flask 里写好图表配置字典用json.dumps传给模板前端echarts.init时直接使用。3. 把数据跑起来爬虫取数、Pandas 清洗与 PyEcharts 出图3.1 爬虫取数请求构造与反爬边界实时天气数据靠爬虫拿目标通常是公开的气象数据接口或页面。我一般用requests加BeautifulSoup的组合先请求页面再解析表格或 JSON。下面是一个抓取示例注意这里用的是公开的天气数据页面结构实际地址要替换成你选的数据源import requests from bs4 import BeautifulSoup import pandas as pd def fetch_realtime(city_code101010100): # 构造请求头模拟浏览器访问 headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 } # 这里用示例 URL实际替换为你要抓的数据源地址 url fhttp://www.example.com/weather/{city_code}.shtml resp requests.get(url, headersheaders, timeout10) resp.encoding utf-8 soup BeautifulSoup(resp.text, html.parser) # 假设数据在 class 为 tem 的标签里 temp soup.find(div, class_tem).text.strip() weather soup.find(div, class_wea).text.strip() return {city_code: city_code, temp: temp, weather: weather}参数说明city_code是城市编码不同数据源编码规则不同headers里的 User-Agent 必须带否则很多站点直接返回 403timeout设 10 秒避免请求卡死。注意不要高频请求同一个页面加个time.sleep(1)是最低成本的礼貌。如果目标站点有验证码或动态加载这套静态解析就不够用了得换 Selenium 或直接找它的 XHR 接口。3.2 Pandas 清洗缺失值、异常值和日期对齐爬回来的数据往往不干净温度字段可能带“℃”符号日期可能缺天风向可能写成“东北风 3 级”这种混合格式。Pandas 在这里的作用是把这些杂数据转成规整的 DataFrame。我一般会写一个清洗函数import pandas as pd import numpy as np def clean_weather(df): # 去掉温度里的单位符号并转 float df[temp_high] df[temp_high].str.replace(℃, ).astype(float) df[temp_low] df[temp_low].str.replace(℃, ).astype(float) # 日期列统一转 datetime无法解析的置为 NaT df[date] pd.to_datetime(df[date], errorscoerce) # 删除日期缺失的行 df df.dropna(subset[date]) # 温度超出合理范围的置为 NaN再用前后值填充 df.loc[df[temp_high] 50, temp_high] np.nan df[temp_high] df[temp_high].fillna(methodffill) # 按日期排序 df df.sort_values(date).reset_index(dropTrue) return df关键点errorscoerce让无法解析的日期变成 NaT 而不是报错温度阈值 50 度是经验值超过基本可以判定为脏数据ffill用前一个有效值填充适合时间序列。清洗完的数据再传给 PyEcharts折线图才不会出现断点或乱序。3.3 PyEcharts 出图折线图、柱状图和饼图的配置差异PyEcharts 封装了 ECharts 的配置项用 Python 字典就能生成图表。折线图适合看温度趋势柱状图适合对比月度降水饼图适合看天气现象占比。下面是一个折线图的配置示例from pyecharts import options as opts from pyecharts.charts import Line def build_temp_line(dates, highs, lows): line ( Line() .add_xaxis(dates) .add_yaxis(最高温, highs, is_smoothTrue, markpoint_optsopts.MarkPointOpts( data[opts.MarkPointItem(type_max, name最大值)])) .add_yaxis(最低温, lows, is_smoothTrue) .set_global_opts( title_optsopts.TitleOpts(title温度趋势), tooltip_optsopts.TooltipOpts(triggeraxis), yaxis_optsopts.AxisOpts(name温度(℃)), xaxis_optsopts.AxisOpts(name日期) ) ) return line参数说明is_smoothTrue让折线变平滑但气象数据我建议关掉因为平滑会掩盖真实波动markpoint_opts标出最大值方便快速定位极端天气triggeraxis让 tooltip 按轴触发鼠标移到日期上同时显示两条线的值。柱状图和饼图的区别主要在add_yaxis的图表类型和set_series_opts的标签配置饼图不需要 x 轴直接传[(晴, 30), (多云, 20)]这种二元组列表即可。4. 避坑与排查样式冲突、接口 500 和图表不显示4.1 页面样式错乱栅格挤在一起现象引入 SB Admin 后原本 Bootstrap 的col-md-6不生效两列变一列。原因sb-admin.css里可能重定义了.row或container的宽度或者bootstrap-grid.css和bootstrap.css同时引入导致重复。解决只保留bootstrap.min.css删掉单独的bootstrap-grid.css检查sb-admin.css是否在 Bootstrap 之后加载用浏览器开发者工具看 computed 样式里width被谁覆盖了。4.2 接口返回 500日志指向 JSON 序列化失败现象前端请求/api/history返回 500Flask 控制台报TypeError: Object of type Timestamp is not JSON serializable。原因Pandas 的Timestamp类型不能直接被jsonify序列化。解决在返回前把日期列转成字符串df[date] df[date].dt.strftime(%Y-%m-%d)或者用json.dumps时指定defaultstr。我一般直接在接口层做转换避免把 Pandas 类型漏出去。4.3 PyEcharts 图表空白控制台报echarts is not defined现象页面加载后图表区域一片空白F12 看到Uncaught ReferenceError: echarts is not defined。原因PyEcharts 生成的 HTML 依赖 ECharts 的 JS 文件如果模板里只引了 PyEcharts 的 Python 包而没引 JS 库或者 CDN 被拦截就会报这个错。解决确认模板里引入了echarts.min.js可以用 PyEcharts 自带的cdn链接也可以下载到本地static/js/目录。离线环境必须用本地文件。4.4 爬虫突然拿不到数据返回 403 或空列表现象昨天还能跑的爬虫今天全部返回 403或者soup.find返回 None。原因目标站点改了页面结构或者加了反爬策略识别了你的请求头。解决先手动访问目标 URL 确认页面是否正常检查 User-Agent 是否过期如果页面结构变了重新定位数据所在的标签如果加了 Cookie 校验用requests.Session()保持会话。注意不要用高频请求去试探容易被封 IP。4.5 DataTables 表格不显示分页和搜索框现象表格数据出来了但分页、搜索、排序功能都没有。原因dataTables.bootstrap4.css和dataTables.bootstrap4.js没有同时引入或者初始化代码没写。解决确认 JS 文件在bootstrap.min.js之后加载然后在页面底部加$(document).ready(function() { $(#dataTable).DataTable(); });。如果表格 ID 写错初始化会静默失败控制台不一定报错这点很容易翻车。5. 进阶技巧把实时刷新和历史对比做成可复用的组件这套系统跑通之后最值得加的两个功能是实时刷新和历史对比。实时刷新不用 WebSocket 那么重用setInterval定时请求接口就行但要注意别把间隔设太短30 秒到 1 分钟比较合理。下面是一个前端定时刷新的示例// 每 60 秒请求一次实时数据并更新图表 function refreshRealtime() { fetch(/api/realtime?city北京) .then(res res.json()) .then(data { // 假设 chart 是已经初始化好的 ECharts 实例 chart.setOption({ series: [{ data: [data.temp_high, data.temp_low] }] }); }) .catch(err console.error(刷新失败, err)); } setInterval(refreshRealtime, 60000);历史对比的做法是让用户选两个时间段后端返回两组数据前端用 ECharts 的grid多图或者双 Y 轴叠在一起。我一般会在接口里加一个comparetrue参数返回结构里多一个previous字段前端判断有这个字段就画对比线。验证方法很简单选同一时间段对比两条线应该完全重合选不同时间段观察趋势差异是否符合预期。还有一个容易被忽略的点是数据缓存。爬虫不要每次请求都去抓用functools.lru_cache或者简单的内存字典缓存 5 分钟能显著降低被封的风险。我习惯在爬虫函数外面包一层from functools import lru_cache import time lru_cache(maxsize32) def get_cached_weather(city_code, _ts): # _ts 是时间戳参数用来控制缓存过期 return fetch_realtime(city_code) # 调用时传入当前时间戳的 5 分钟取整 ts int(time.time() // 300) data get_cached_weather(101010100, ts)这样同一个 5 分钟窗口内相同城市的请求只会真正抓一次。参数maxsize32控制缓存城市数量_ts参数本身不参与逻辑只是用来让缓存键随时间变化。从那以后我每次做爬虫类项目都强制走一遍“缓存 超时 异常兜底”这三步不然线上跑着跑着就断了。希望帮到你。本文还有配套的精品资源点击获取