简介:基于Python实现的天气数据爬取与可视化项目,面向计算机相关专业在校学生的课程设计、毕业设计及初期立项演示,也适合有基础爬虫与可视化学习需求的Python开发者。代码包含详细注释,配有界面演示,可直观了解从数据抓取、清洗存储到图表展示的完整流程。资源包共20个文件,压缩后约393KB,核心为3个Python脚本(天气数据爬取、两份数据分析与可视化)、2个CSV数据文件、1张界面演示截图,以及README说明、项目配置和依赖记录等,目录结构清晰。已有107人学习下载。项目代码经过测试运行成功,答辩评审平均分96分,便于直接使用或在原基础上二次开发。通过学习这份资源,可以掌握天气类数据的爬取思路、文件读写与可视化方法,并借助注释快速理解每段逻辑,适合作为课设作业的参考模板或实战练手项目。
1. 从课程设计到可运行的天气爬虫可视化程序
每年到课程设计季,天气数据爬取和可视化就会出现在大量 Python 选题里。你大概率见过这样的同学:兴致勃勃调好了爬虫,却卡在数据解析上;画好了图表,却不知道怎么放进界面里演示。其实这个题目真正考的不是“能不能抓到数据”,而是你能否把网络请求、HTML 解析、数据清洗、可视化展示四个环节串成一条完整链路,并且让代码能被别人看懂。本文就以标题里的“基于 Python 实现简单的天气数据爬取和可视化”为主线,写一套可以直接抄、能跑通、注释齐全的完整方案。我会选用 requests + BeautifulSoup 抓取公开网页数据,用 matplotlib 做可视化,再用 Tkinter 做一个最简单的界面演示。这套组合不依赖重型框架,环境搭建成本低,也足够应付高分课程设计。
2. 天气数据爬取的技术选型与反爬边界
2.1 数据源选择:直接爬网页还是调 API
做天气爬取时,第一个要拍板的事是数据源。常见做法有两种:调天气 API(如 OpenWeatherMap、心知天气)或者直接爬网页。API 的优点是数据结构化好、字段齐全,但通常需要注册 key,而且免费额度有限;直接爬网页则不需要 key,只要目标网站没有强反爬,就能用 requests 把页面抓下来,再用 BeautifulSoup 解析 HTML。课程设计里我更推荐后者,因为你能在报告中写清楚“如何分析请求头”“如何定位 DOM 节点”,这些过程才是评分老师愿意看到的内容。我一般会选那种结构稳定的公开天气页面,比如中国天气网的某个城市页面,或者 wttr.in 这类极简风格的天气服务,它们的 HTML 结构相对固定,适合做教学案例。
2.2 requests + BeautifulSoup 的解析模型与反爬识别
拿到一个网页以后,爬虫的核心就两件事:发请求、解析响应。requests 负责把 HTTP 请求发出去,BeautifulSoup 负责把返回的 HTML 字符串变成一棵可遍历的树。这两者组合的解析模型是:先soup.find()定位目标节点,再用.text取出文本,用.get('属性')取出标签属性。实际爬取时,很多页面会校验请求头里的 User-Agent,甚至校验 Referer、Cookie。如果直接发裸请求,大概率只能拿到一个 403 或者一个登录跳转页面。所以我一般会在请求头里带上浏览器信息,必要时再加一个间隔等待。下面是一张常用的请求头参数表,也是你调参时重点看的几个字段。
| 参数名 | 典型值 | 作用 |
|---|---|---|
| User-Agent | Mozilla/5.0 ... | 标识客户端类型,绕过最简单的 UA 校验 |
| Referer | https://target.com/ | 告诉服务器请求来源,部分站点会校验 |
| Cookie | session=xxx; ... | 维持会话状态,有些数据需要登录后可见 |
| Accept-Language | zh-CN,zh;q=0.9 | 让服务器返回中文内容,避免乱码 |
2.3 频率控制与合规边界
爬虫不是请求发得越快越好。对目标站点做高频请求,一是容易触发 IP 封禁,二是给对方服务器造成压力,这个在课程设计里也是忌讳。我一般会在两次请求之间加time.sleep(1),如果数据量小甚至加到 2 秒。另外,在代码注释里写清楚数据来源和抓取时间,这既是工程习惯,也是合规意识的体现。你要在报告中说明:仅抓取公开静态页面,不碰登录接口,不遍历敏感路径,不做商业用途。这套方案在课堂上使用没有任何问题,但如果你要放到生产环境,必须先去查看目标网站的robots.txt,确认是否允许爬取。
3. 实现天气数据爬虫:带详细注释的核心代码
3.1 定义数据模型与抓取函数
动手写爬虫前,先想清楚你要拿哪些字段。一个简单的天气数据模型,至少要有日期、最高温、最低温、天气状况、风力这几项。把它们定义成字典,后续可视化时直接按 key 取值,比用元组或散装变量清晰得多。下面这段代码我写了详细注释,你直接复制到项目里就能跑通。
import requests from bs4 import BeautifulSoup import time # 数据模型:用一个字典保存单日天气信息 def empty_weather(): return { "date": "", "high": 0, "low": 0, "condition": "", "wind": "" } # 核心抓取函数:传入目标URL,返回解析后的天气列表 def fetch_weather(url, headers=None): if headers is None: headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)" } resp = requests.get(url, headers=headers, timeout=10) resp.encoding = "utf-8" # 大部分中文天气站用utf-8,个别用gbk soup = BeautifulSoup(resp.text, "html.parser") return soup这段代码里,requests.get的timeout参数很关键,它控制了最长的等待时间,避免某个请求卡死整个程序。resp.encoding如果设错了,解析出来的中文会变成乱码,常见做法是先打印resp.apparent_encoding看一眼再决定。BeautifulSoup的第二个参数指定了解析器,html.parser是 Python 标准库自带的,不需要额外安装,比lxml更省事。
3.2 解析 HTML 提取关键字段
数据抓下来以后,真正的难点是定位 HTML 里的节点。你需要在浏览器里按 F12 找到目标数据的父节点,然后写选择器。我用过的最常见的结构是:每天的气象信息放在一个<ul class="t clearfix">下,每个<li>对应一天的数据。定位方式有两种:.find()只取第一个,.find_all()取所有。下面示例演示如何提取 7 天的天气列表,并处理空值和单位。
def parse_weather(soup): weather_list = [] # 定位到包含每日数据的列表容器,class名称以实际页面为准 days = soup.find_all("li", class_="sky skyid") if not days: days = soup.select(".t li") # 备选选择器 for day in days[:7]: # 只取前7天,控制数据量 item = empty_weather() # 日期在h1标签内,形如"7日(今天)" date_tag = day.find("h1") if date_tag: item["date"] = date_tag.text.strip() # 高温和低温分开放在两个span里 high_tag = day.find("span", class_="tem") if high_tag: # 把字符串里的"℃"去掉,转成整数 item["high"] = int(high_tag.text.replace("℃", "").strip()) low_tag = day.find("p", class_="tem") if low_tag: item["low"] = int(low_tag.span.text.replace("℃", "").strip()) # 天气状况在p标签的title属性里 cond_tag = day.find("p", class_="wea") if cond_tag: item["condition"] = cond_tag.text.strip() weather_list.append(item) return weather_list这里有两个常见的坑:一是class里有多个类名时,直接用class_="sky skyid"这样写,匹配的是完整字符串,不是子串,如果你不确定就改用.select(".sky");二是find_all返回的列表可能包含你不需要的元素,比如页脚的重复天气信息,所以我才加了[:7]切片,只取前 7 天。解析完成后,建议立刻打印一条记录看结构对不对,再进入下一步。
3.3 异常兜底与注释规范
写爬虫最容易忽略的是异常处理。网络请求可能超时、返回空页面、甚至目标节点被删改,这些都要兜住。我给你一个最小但完整的异常处理模板,能覆盖 80% 的失败场景。同时,课程设计对注释要求很高,我的注释原则是:说明“为什么这么做”,而不是“这句代码在做什么”,这样才能体现你对逻辑的理解。
def safe_fetch(url, retries=3): for i in range(retries): try: soup = fetch_weather(url) return parse_weather(soup) except requests.exceptions.Timeout: # 超时重试,等待时间逐次增加 wait = (i + 1) * 2 print(f"[警告] 请求超时,{wait}秒后重试") time.sleep(wait) except Exception as e: # 兜底捕获所有异常,避免程序崩溃 print(f"[错误] 抓取失败:{e}") return [] return [] # 重试耗尽,返回空列表这段代码的返回值设计很重要:失败时返回空列表,而不是抛出异常。这样主程序可以继续执行,可视化部分至少能显示一个空图或提示信息,而不是黑屏退出。retries参数控制重试次数,我一般设 3 次,再多就是对目标网站不礼貌了。把fetch_weather和parse_weather分开写,是为了让每层逻辑独立,方便你在报告里分别解释网络层和解析层的设计。
4. 可视化与界面演示:从数据到图表再到窗口
4.1 matplotlib 绘制温度曲线与天气分布
拿到 7 天数据后,可视化就顺理成章了。最常用的图是温度曲线:横轴日期,两根折线分别表示最高温和最低温。为了更直观,我会把天气状况作为图上的文本标注。matplotlib 的画图逻辑是:先准备数据列表,再plt.plot(),最后plt.show()。下面这段代码可以直接嵌入你的主程序。
import matplotlib.pyplot as plt def plot_temperature(weather_list): if not weather_list: print("没有数据,无法绘图") return dates = [w["date"] for w in weather_list] highs = [w["high"] for w in weather_list] lows = [w["low"] for w in weather_list] plt.figure(figsize=(10, 5)) plt.plot(dates, highs, marker="o", label="最高温") plt.plot(dates, lows, marker="s", label="最低温") # 在最高温折线上方标注天气状况 for i, cond in enumerate([w["condition"] for w in weather_list]): plt.text(i, highs[i] + 1, cond, ha="center", fontsize=9) plt.title("近7天天气趋势") plt.xlabel("日期") plt.ylabel("温度(℃)") plt.legend() plt.grid(True) plt.tight_layout() plt.show()这里marker参数给折线加了数据点标记,o是圆形,s是方形,可视化课设里常用。plt.text的坐标是(x, y),我让y取最高温加 1,这样文字不会盖住折线。tight_layout()解决标签被截断的问题,这个细节经常被忽略。如果你用的是中文标签,一定要在代码开头设置plt.rcParams["font.sans-serif"] = ["SimHei"],否则中文会变成方块。
4.2 Tkinter 嵌入图表实现界面演示
界面演示部分,很多同学会考虑 web 框架,其实课程设计里我用 Tkinter 就够了。它是 Python 自带的 GUI 库,不需要额外安装,启动快,演示时也不依赖浏览器环境。思路是:窗口里放一个按钮和一个画布,点击按钮后抓取最新数据并绘图,然后把图表保存成图片再显示到窗口里。matplotlib 直接嵌入 Tkinter 需要FigureCanvasTkAgg,最简单的是用如下方式。
import tkinter as tk from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg def show_interface(): root = tk.Tk() root.title("天气爬取与可视化演示") # 用于绘制图表的容器 fig = plt.Figure(figsize=(6, 4)) ax = fig.add_subplot(111) canvas = FigureCanvasTkAgg(fig, master=root) canvas.get_tk_widget().pack() def refresh(): # 点击刷新按钮时重新抓取和绘制 data = safe_fetch("https://example.com/weather") ax.clear() ax.plot([d["high"] for d in data], label="high") ax.plot([d["low"] for d in data], label="low") ax.legend() canvas.draw() btn = tk.Button(root, text="刷新数据", command=refresh) btn.pack() refresh() # 启动时先展示一次 root.mainloop()这段代码演示了按钮绑定事件和 canvas 刷新的核心逻辑。FigureCanvasTkAgg把 matplotlib 的 figure 对象嵌入到 Tk 窗口里,每次刷新时调用ax.clear()清空旧图,再画新图,canvas.draw()更新显示。如果你不想用 Tkinter,也可以改用pywebview或Flask做可视化界面,但我个人认为 Tkinter 在课设答辩时更稳,因为它不需要额外端口和服务配置,双击就能运行。
4.3 数据持久化与刷新逻辑
界面演示不只是看一个静态图表,你要让观看者知道数据是“实时抓取”的。所以刷新逻辑非常重要。我一般会加两个功能:一是把抓到的数据存成 CSV 文件,方便事后核对;二是每次刷新前清空旧数据,并用状态栏提示“更新完成”。这既能体现你对数据生命周期的理解,也能避免答辩时因为缓存问题被追问。写入 CSV 的代码很简单。
import csv def save_weather(weather_list, filename="weather.csv"): with open(filename, "w", newline="", encoding="utf-8") as f: writer = csv.DictWriter(f, fieldnames=["date", "high", "low", "condition", "wind"]) writer.writeheader() writer.writerows(weather_list) print(f"数据已保存到 {filename}")newline=""是为了避免 Windows 下 CSV 文件多出空行,这个坑很常见。encoding="utf-8"保留中文不乱码。在界面刷新时,先调用safe_fetch,再调用save_weather,最后更新图表,这样你的程序就形成了“抓取-保存-可视化”闭环。
5. 进阶技巧:验证数据正确性与提高爬虫稳定性
5.1 用日志和重试机制替换简单 print
你在课设答辩时,评委可能会问“你的程序怎么应对目标网站改版?”直接的回答是:我用了日志和重试。简单的print在代码跑完后什么痕迹都留不下,而logging模块能记录到文件,方便排查。我一般会做如下替换。
import logging logging.basicConfig( filename="weather.log", level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s" ) logging.info("开始抓取天气数据")更进一步的稳定性优化是设置请求频率限制,把time.sleep(1)改成从配置里读取的间隔值,这样你在演示时可以把间隔调小,在生产时调大。此外,还可以用requests.Session()复用连接,减少 TCP 握手次数,这个优化在抓取多个页面时效率提升明显。Session 对象会自动保存 cookie,某些需要先访问首页获得会话的站点也能正常处理。
5.2 3 个验证数据正确性的方法
可视化结果是不是可信,要看数据源头对不对。我常用三个方法验证爬虫抓到的数据:第一,打印原始 HTML 片段,确认选择的节点没有变化;第二,把抓到的日期和本地系统日期做对比,防止抓到旧数据;第三,用两个不同网站的数据做交叉对比,比如同时抓取同一个城市在两个站点的温度,差值在 2 度以内基本可以认为没问题。
# 验证HTML节点是否匹配,在命令行里直接测试选择器 python -c "from bs4 import BeautifulSoup; print(BeautifulSoup(open('page.html', encoding='utf-8'), 'html.parser').select_one('.tem').text)"如果这条命令能输出温度值,说明选择器写的没错。如果输出None,说明页面结构变了,你需要重新查看 DOM 节点。交叉对比时,注意两个站点的更新时间可能不同,最好选择同一小时的整点数据,避免因为时间差导致误差过大。
5.3 从课程设计到工程化的差距
课程设计做完后,如果你想把它变成能长期跑的小工具,还需要补三块:配置管理、单元测试、模块解耦。比如把 URL、User-Agent、刷新间隔等抽到一个config.py里,而不是写在主程序里;为parse_weather写一个简单的测试用例,用一段固定的 HTML 字符串验证解析逻辑是否正确。这些做法在课设里是加分项,在真实项目里是必备项。最后提醒你一点:爬虫代码的注释风格要统一,我习惯用中文注释,但变量名和函数名用英文,这样报告和代码都能兼顾可读性。如果你希望进一步优化界面演示的观感,可以在show_interface里加一个下拉框切换城市,或者用matplotlib的动画功能让曲线逐段出现,这些都能让演示效果更接近正式的成品。
本文还有配套的精品资源,点击获取