Python天气数据可视化平台:MySQL+Flask+Echarts爬虫联调
2026/9/18 4:33:59 网站建设 项目流程

简介:《基于Python的天气数据可视化平台》是一份完整的毕业设计论文文档,面向计算机、软件工程与云计算等专业需要完成课程设计或毕业设计的学生,适合以数据爬取与可视化展示为选题的读者参考。资源包仅含1个docx文件,体积约2.45MB,内含封面、中英文摘要、目录及章节正文,结构规范,可用于了解论文写作框架与排版格式。论文围绕Python网络爬虫、Pandas数据清洗、Flask后端框架与前端技术展开,重点介绍Echarts图表与WordCloud词云在天气信息呈现中的应用,涵盖温度、湿度、风速等数据的采集、整理与可视化流程。文档还讨论天气数据可视化对环境问题与日常决策的现实意义,并附有完整章节划分与关键技术说明,便于读者梳理实现思路、提炼创新点或作为同类项目的写作模板。目前已有318人学习下载。

1. 从一份毕业设计报告拆出的天气可视化平台

很多人拿到「基于 Python 的天气数据可视化平台」这类毕业设计题目,第一反应是去装个大屏模板套一下,结果卡在数据源上——天气数据从哪来、怎么存、怎么保证字段对得上前端图表,这三步没打通,模板再漂亮也是空壳。这份来自泉州信息工程学院云计算专业的毕业设计报告,给出的技术路线其实很实在:Python 爬虫取数,MySQL 落库,Flask 做后端,Echarts 加 WordCloud 做前端呈现,B/S 架构,不装客户端。它的目标不是做商业气象产品,而是把「温度、湿度、风速、风向、云量、降雨量、PM2.5、AQI、生活指数」这些多维数据整合到一个网页里,让普通访问者随手点开就能看趋势、看建议。适合拿它当课程设计或毕设底稿的人,也适合想练一遍「爬虫—清洗—入库—接口—图表」完整链路的开发者。

2. 爬虫取数与 MySQL 表结构设计

2.1 为什么先定表结构再写爬虫

报告里把数据分成了几块:7 天天气、空气质量指数、生活指数、天气新闻、景点推荐。新手常犯的错是先写爬虫把数据抓成一堆 CSV,等要建接口时才发现字段名对不上、量纲不统一。常见做法是先按业务实体把表定下来,让爬虫的产物直接映射到表字段,中间只隔一层清洗逻辑。

以报告里的表设计为准,几张核心表的字段可以直接抄:

表名关键字段用途
qitiantianqiriqi、tianqi、wendu、fengji7 天天气趋势
shenghuozhishuzhishu、jianyi、miaoshu生活指数与出行建议
tianqixinwenxinwenbiaoti、xinwenneirong、faburiqi天气新闻列表
yonghuyonghuzhanghao、mima、yonghuxingming、touxiang用户信息
usersusername、password、role后台管理员账号

建表时字段统一用 varchar 200 存文本型数值(报告里 wendu、fengji 都是 varchar),好处是解析容错高,代价是后续做数值计算要转类型。如果要做趋势图,建议在入库时就额外加一列 int 型的 wendu_num,避免在 SQL 里反复 CAST。

CREATE TABLE qitiantianqi ( id BIGINT PRIMARY KEY AUTO_INCREMENT, addtime TIMESTAMP DEFAULT CURRENT_TIMESTAMP, riqi VARCHAR(200) COMMENT '日期', tianqi VARCHAR(200) COMMENT '天气', wendu VARCHAR(200) COMMENT '温度', wendu_num INT COMMENT '温度数值,用于绘图', fengji VARCHAR(200) COMMENT '风级' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

这段建表语句的关键点有三个:ENGINE=InnoDB保证事务和外键支持,utf8mb4避免中文和表情符号乱码,wendu_num是给 Echarts 折线图用的冗余字段。参数上addtimeCURRENT_TIMESTAMP自动填充,省掉后端每次手动写时间。

2.2 用 requests 加解析库抓取并清洗

爬虫部分报告只提到「基于 Python 的网络爬虫技术」,没写具体库。这个场景下合格的做法是 requests 发请求、lxml 或 BeautifulSoup 解析,pandas 做清洗。下面是一段可直接改选择器复用的骨架:

import requests, pandas as pd from lxml import etree HEADERS = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"} def fetch_weather(url): # 加超时,避免个别站点慢响应把整个脚本挂死 resp = requests.get(url, headers=HEADERS, timeout=10) resp.encoding = resp.apparent_encoding # 中文站点常需手动指定编码 html = etree.HTML(resp.text) rows = [] for li in html.xpath('//ul[@class="tianqi-list"]/li'): rows.append({ "riqi": li.xpath('./span[1]/text()')[0], "tianqi": li.xpath('./span[2]/text()')[0], "wendu": li.xpath('./span[3]/text()')[0], "fengji": li.xpath('./span[4]/text()')[0], }) df = pd.DataFrame(rows) # 把 "23℃" 这类字符串转成纯数字,供图表使用 df["wendu_num"] = df["wendu"].str.extract(r'(-?\d+)').astype(int) return df.drop_duplicates(subset=["riqi"]) # 去重,防止重复抓取插重复行

逻辑上分四步:请求、解码、XPath 抽取、pandas 清洗。apparent_encoding是容错点,很多国内站点 header 里写的编码和实际不一致,不加这行中文会变成乱码。timeout=10是必须的,爬虫脚本最怕单个请求卡死。drop_duplicates针对的是重跑脚本时的重复插入问题,比在数据库层加唯一索引更早拦截。

提示:抓取前先看目标站点的 robots.txt,控制请求频率,加 time.sleep(1) 间隔,别把生产环境当练手场。

2.3 数据落库的两种写法

清洗完落到 MySQL,用 pymysql 直连或 SQLAlchemy 都行。小项目我一般直接 pymysql,依赖少、看得清:

import pymysql conn = pymysql.connect(host="127.0.0.1", user="root", password="你的密码", database="weather", charset="utf8mb4") cursor = conn.cursor() for _, r in df.iterrows(): cursor.execute( "INSERT INTO qitiantianqi(riqi,tianqi,wendu,wendu_num,fengji) VALUES(%s,%s,%s,%s,%s)", (r["riqi"], r["tianqi"], r["wendu"], r["wendu_num"], r["fengji"]) ) conn.commit()

charset="utf8mb4"必须和建表时一致,否则中文写进去是问号。iterrows适合小数据量,如果抓几千行以上换成executemany批量插入,性能差好几倍。

3. Flask 后端接口与 Echarts 前端联调

3.1 Flask 路由怎么给图表喂数据

报告选了 Flask 而不是 Django,理由是可扩展、分层少,这个判断对头。天气平台的数据接口逻辑很直白:查库、拼 JSON、返回。Echarts 要的数据格式是「类目轴 + 数值轴」两个数组,所以后端返回结构要提前对上,别让前端再去拆对象。

from flask import Flask, jsonify import pymysql app = Flask(__name__) def get_conn(): return pymysql.connect(host="127.0.0.1", user="root", password="你的密码", database="weather", charset="utf8mb4", cursorclass=pymysql.cursors.DictCursor) @app.route("/api/weather/7day") def weather_7day(): conn = get_conn() with conn.cursor() as cur: cur.execute("SELECT riqi, wendu_num FROM qitiantianqi ORDER BY riqi LIMIT 7") rows = cur.fetchall() conn.close() # 拆成 Echarts 需要的两个数组 return jsonify({ "dates": [r["riqi"] for r in rows], "temps": [r["wendu_num"] for r in rows] })

DictCursor让查询结果直接是字典,省掉手动映射下标。接口只返回图表真正要用的两个数组,别把整行数据糊给前端,字段越少联调越快。ORDER BY riqi排序很重要,数据库默认返回顺序不保证和写入顺序一致,不排序折线图会乱跳。

3.2 Echarts 折线图和柱状图的对接

前端拿到datestemps后直接塞进 Echarts 的xAxis.dataseries.data。下面是最小可跑的配置:

fetch('/api/weather/7day') .then(res => res.json()) .then(data => { const chart = echarts.init(document.getElementById('tempChart')); chart.setOption({ tooltip: { trigger: 'axis' }, // 鼠标悬停显示数值 xAxis: { type: 'category', data: data.dates }, yAxis: { type: 'value', name: '温度(℃)' }, series: [{ name: '温度', type: 'line', smooth: true, // 平滑曲线,观感更好 data: data.temps, areaStyle: {} // 面积填充,突出趋势 }] }); });

几个参数值的留意:trigger: 'axis'是折线图该用的提示方式,用item会只在点上触发,体验差。smooth: true会牺牲一点数据准确感换视觉流畅,如果做的是严谨分析图建议关掉。柱状图换type: 'bar'即可,空气质量指标(AQI、PM2.5)用柱状比折线更直观。

空气质量模块报告提到要展示 PM2.5 和 AQI,这两个量级差很大,放同一坐标系会被压扁,常见做法是双 Y 轴:

yAxis: [ { type: 'value', name: 'AQI' }, { type: 'value', name: 'PM2.5', position: 'right' } ], series: [ { name: 'AQI', type: 'bar', data: aqiData }, { name: 'PM2.5', type: 'line', yAxisIndex: 1, data: pmData } ]

yAxisIndex: 1指向第二个 Y 轴,这是双轴图的核心参数,漏写就会两个系列都挂在左轴。

3.3 WordCloud 词云在气象文本上的用法

报告的天气新闻模块用到了 WordCloud,这块主要面向新闻标题和内容的词频统计。流程是:把新闻文本取出来,jieba 分词,去掉停用词,再用 WordCloud 生成图片或交给前端 wordcloud2.js 渲染。

import jieba from wordcloud import WordCloud text = " ".join([r["xinwenbiaoti"] for r in news_rows]) words = " ".join(jieba.cut(text)) # 过滤单字和常见无意义词 stop = set(["的", "了", "和", "是", "在"]) filtered = " ".join([w for w in words.split() if len(w) > 1 and w not in stop]) wc = WordCloud(font_path="msyh.ttc", width=800, height=400, background_color="white").generate(filtered) wc.to_file("static/wordcloud.png")

font_path是中文词云最容易踩的坑,不指定中文字体生成出来全是方块。len(w) > 1过滤掉单字,气象文本里单字多是虚词,去掉能让高频实词更突出。

4. 角色权限、测试与几个真实坑位

4.1 管理员与用户双角色的落法

报告里功能和用例图都写得很清楚:管理员管用户、新闻、空气质量、7 天天气、生活指数、景点推荐;用户看首页、个人中心、各类数据。落到代码里,最省事的做法是users表里的role字段做区分,登录后把角色写进 session,路由上加装饰器校验。

from functools import wraps from flask import session, redirect, url_for def admin_required(f): @wraps(f) def wrapper(*args, **kwargs): if session.get("role") != "管理员": return redirect(url_for("login")) return f(*args, **kwargs) return wrapper

注意:session 里存角色比每次查库快,但角色变更后要重新登录才生效,做权限管理时心里要有数。

密码别明文存,报告里mimapassword都是 varchar 直存,实际部署前一定要用 werkzeug 的generate_password_hash做哈希,登录时用check_password_hash比对,这是毕设答辩时评委最容易追问的点。

4.2 功能测试该盯哪几个点

报告的测试章节写得比较简略,只提了功能测试。真跑起来至少要覆盖三类:数据类接口返回字段是否齐全、越权访问是否被拦截、边界数据是否报错。数据接口测试可以直接用 curl 过一遍:

# 验证 7 天天气接口返回结构与长度 curl -s http://127.0.0.1:5000/api/weather/7day | python -c " import sys, json d = json.load(sys.stdin) assert len(d['dates']) == len(d['temps']), '日期与温度数量不一致' print('接口字段校验通过,共', len(d['dates']), '条') "

这种自校验脚本比人眼翻页面靠谱,字段错位、长度不匹配能立刻暴露。越权测试就是拿普通用户 session 去访问管理员路由,看是否被重定向;边界测试是往接口塞空数组和超长字符串,看后端有没有做异常捕获,不捕获就直接 500 暴露堆栈。

4.3 上生产前的三个高频坑

第一个是爬虫目标站点改版导致 XPath 失效,接口不再报错但数据为空。加一层「抓取条数为 0 就告警」的判断,比等用户反馈早发现问题。第二个是 MySQL 连接没关,Flask 调试模式反复重载会耗尽连接数,所有connect都配with或 try/finally 关闭。第三个是时间字段时区,addtime用数据库CURRENT_TIMESTAMP时注意容器和宿主机的时区差异,否则「今天」的天气可能因为差几个小时显示成昨天。这三处不解决,答辩演示那天很可能当着老师的面翻车。

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

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

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

立即咨询