考试通知
Python天气预测系统:从爬虫采集到回归建模的完整实战 简介这是一套基于Python实现的天气预测分析系统源码面向具备一定Python基础的数据分析学习者、爬虫爱好者以及需要处理时间序列预测的开发者。资源整合了从天气数据采集、预处理、统计分析到模型预测与可视化展示的完整流程覆盖网络爬虫、Pandas数据处理、时间序列分析、机器学习建模及Matplotlib绘图等关键知识点可用于课程设计、实战演练或作为天气相关项目改造的参考基底。压缩包内共14个文件约2.21MB主要包含4个Python脚本对应爬虫、线性回归、逻辑回归及数据分析模块、2个CSV数据文件、5张PNG结果图表、2份Markdown说明文档及1份实验报告结构清晰、便于对照运行。已有194人学习下载随包附带的实验报告与说明文档有助于理解各脚本功能与实验结论图表和输出数据可直接检验预测效果节省自行整理数据与调试环境的时间。1. 天气预测分析系统从爬虫到回归模型的一条龙样本天气数据是典型的时间序列数据拿它练手 Python 爬虫、数据分析和预测建模能一次性把数据项目的完整链路走通。这份基于 Python 的天气预测分析源码包含爬虫抓取、CSV 存储、数据分析、回归预测和可视化图表五个环节适合刚学完 Pandas 和 Matplotlib 但缺少完整项目经验的开发者也适合需要快速搭建天气数据 Demo 的从业者。整套代码拆开看并不复杂难的是把五个环节串起来的时候那些隐蔽的坑——比如中文编码、日期字段解析失败、回归模型输出全 NaN。这篇文章就从实际运行角度逐个模块拆开讲清楚并给出可直接复现的修改路径。2. 爬虫与数据清洗把城市历史天气抓成本地 CSV2.1 爬虫脚本的结构与入库逻辑项目里的天气爬虫.py是数据源头。常见的实现方式是用 requests 请求天气网站的历史数据接口再用 BeautifulSoup 解析 HTML 表格最后把结果写入天气数据.csv。它的核心流程分三步构造请求 URL、解析响应、落盘 CSV。import requests from bs4 import BeautifulSoup import csv import time def fetch_weather(city_code, year): url fhttps://tianqi.2345.com/Pc/GetHistory?areaInfo%5BareaId%5D{city_code}areaInfo%5BweatherType%5D1date{year}-1-1 headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 } resp requests.get(url, headersheaders, timeout10) soup BeautifulSoup(resp.text, html.parser) rows soup.select(table tbody tr) data [] for row in rows: cols [c.get_text(stripTrue) for c in row.find_all(td)] if cols: data.append(cols) return data def save_to_csv(data, filename天气数据.csv): with open(filename, a, newline, encodingutf-8-sig) as f: writer csv.writer(f) writer.writerows(data) if __name__ __main__: result fetch_weather(54511, 2024) save_to_csv(result)这段代码里几个细节值得注意encodingutf-8-sig写入 CSV 后用 Excel 直接打开不会乱码这是很多爬虫脚本容易漏掉的地方。请求头必须带User-Agent否则部分天气站点会直接拒绝请求返回 403。城市代码54511是北京站点代号换成其他城市需要去站点页面找对应的areaId。连续爬取多个月份数据时循环里务必加time.sleep(2)之类间隔否则容易被封 IP。爬下来的原始数据通常包含日期、最高温度、最低温度、天气状况、风力风向等字段。但字段名可能是中文也可能夹杂空格这就要交给数据分析.py去清洗。2.2 数据清洗与特征构造数据分析.py做的事情可以概括为三步读 CSV、纠正类型、构造建模用的特征列。天气数据最常见的两个问题是日期列被读成字符串、温度列里混了单位或者空格。import pandas as pd import numpy as np df pd.read_csv(天气数据.csv, encodingutf-8-sig) print(df.info()) print(df.head()) # 列名去空格统一为英文方便后续建模引用 df.columns [date, max_temp, min_temp, weather, wind, air_quality] # 日期转成 datetime 类型并拆出月份和星期特征 df[date] pd.to_datetime(df[date], format%Y-%m-%d, errorscoerce) df[month] df[date].dt.month df[weekday] df[date].dt.dayofweek # 温度列转数值非数字强制为 NaN 后统一处理 df[max_temp] pd.to_numeric(df[max_temp], errorscoerce) df[min_temp] pd.to_numeric(df[min_temp], errorscoerce) # 删除空值过多的行温度列做线性插值兜底 df df.dropna(subset[date, max_temp]) df[max_temp] df[max_temp].interpolate(methodlinear) df.to_csv(Output.csv, indexFalse, encodingutf-8-sig)清洗逻辑说明pd.to_datetime里指定format可以避免部分日期被解析成错误格式errorscoerce让解析失败的日期变成 NaN后续好定位问题。pd.to_numeric是处理温度列混入非数字字符的最快手段。比如原始数据里有 12℃ 或者空格直接转数值会报错加了errorscoerce之后非法值全部变 NaN再用interpolate做线性插值补洞。month和weekday是给回归模型用的特征。温度预测里月份 是最基础的季节性特征星期几 对天气影响其实不大但保留下来可以在逻辑回归里做一个尝试性对比。清洗完的数据会输出成Output.csv后面所有建模和绘图都基于这份文件。唯一需要确认的是清洗后数据量不能太少——少于 200 条记录时回归模型的预测结果基本没有统计意义。3. 预测模型线性回归与逻辑回归的边界3.1 单线性回归用日期/月份预测最高温度单线性回归分析.py里做的是一元回归输入特征是日期序列或者月份序列输出是最高温度。这种模型简单直接适合展示趋势但预测精度上限很低因为天气受气压、湿度、风速等多因素影响不可能仅凭日期拟合出准确温度。import pandas as pd from sklearn.linear_model import LinearRegression from sklearn.model_selection import train_test_split from sklearn.metrics import mean_absolute_error import numpy as np df pd.read_csv(Output.csv, encodingutf-8-sig) # 用月份作为特征温度本身作为标签 X df[[month]].values y df[max_temp].values X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42 ) model LinearRegression() model.fit(X_train, y_train) y_pred model.predict(X_test) mae mean_absolute_error(y_test, y_pred) print(MAE:, round(mae, 2)) print(斜率:, round(model.coef_[0], 4)) print(截距:, round(model.intercept_, 2))参数说明test_size0.2留出 20% 数据做验证。天气数据有季节性更严谨的做法是按时间切分而不是随机切分比如前 80% 训练、后 20% 验证避免未来数据泄漏到训练集。random_state42固定随机种子保证每次运行结果可复现。model.coef_输出斜率正值表示随月份增加温度上升负值则相反这里判断季节性趋势很直观。MAE是平均绝对误差单位是摄氏度。单线性回归在这个数据集上通常会得到 5~8℃ 的 MAE这个数值本身就说明了模型的局限。实际跑完你会发现单线性回归只抓到了一条直线趋势四季节奏完全体现不出来。原因是月份作为数值特征模型只能学到一个单调关系。要体现夏天热、冬天冷这种非线性波动得引入多项式特征或者改用时间序列方法。3.2 逻辑回归为什么它更适合分类而不是数值预测逻辑回归.py放在这个天气项目里容易让人误以为逻辑回归也能做温度数值预测。实际上逻辑回归输出的是概率本质是个分类器。把它用在天气合理的方式是预测明天会不会降温 或 明天最高温是否超过 20℃ 这样的二分类问题。import pandas as pd from sklearn.linear_model import LogisticRegression from sklearn.model_selection import train_test_split from sklearn.metrics import accuracy_score, classification_report df pd.read_csv(Output.csv, encodingutf-8-sig) # 构造二分类标签最高温度是否超过 20 度 df[hot] (df[max_temp] 20).astype(int) features [month, min_temp, humidity] X df[features].values y df[hot].values X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42 ) clf LogisticRegression(max_iter1000) clf.fit(X_train, y_train) y_pred clf.predict(X_test) print(Accuracy:, round(accuracy_score(y_test, y_pred), 3)) print(classification_report(y_test, y_pred))逻辑回归在天气场景里更合理的用法是判断天气状态而不是预测温度数值。比如根据当月最低温度和湿度判断明天最高温是否会超过 20℃——这个任务逻辑回归做得比线性回归更稳。如果在实际运行时报ConvergenceWarning: Maximum iterations reached查看max_iter参数默认 100 次迭代经常不够。调成 1000 之后模型一般能收敛。温度数值预测请使用线性回归或者时间序列模型逻辑回归硬套数值预测会得到一团概率值完全不可用。4. 数据可视化四类基础图表的绘制方式4.1 折线图与趋势图最高温度趋势图.png和最高温度.png是用 Matplotlib 绘制的折线图。趋势图展示全年温度变化曲线适合看整体走势单点的折线图看的是实际温度波动两者差别在于是否做了平滑处理。import pandas as pd import matplotlib.pyplot as plt plt.rcParams[font.sans-serif] [SimHei] plt.rcParams[axes.unicode_minus] False df pd.read_csv(Output.csv, encodingutf-8-sig) df df.sort_values(date) fig, ax plt.subplots(figsize(12, 5)) ax.plot(df[date], df[max_temp], label最高温度, linewidth1.5, color#d62728) ax.plot(df[date], df[min_temp], label最低温度, linewidth1.5, color#1f77b4) ax.set_xlabel(日期) ax.set_ylabel(温度 (℃)) ax.set_title(全年最高/最低温度趋势) ax.legend() ax.grid(True, linestyle--, alpha0.4) fig.autofmt_xdate() plt.tight_layout() plt.savefig(最高温度趋势图.png, dpi200)绘图参数说明plt.rcParams[font.sans-serif]必须设置中文字体否则图表标题和坐标轴中文全变方块。axes.unicode_minus设置为 False 是为了让负号正常显示不设的话负温度会显示成乱码。fig.autofmt_xdate()自动旋转日期刻度标签避免日期挤成一团。这是处理时间序列折线图最省事的方式。dpi200保证保存图片清晰度够用做报告插图可以直接用。4.2 热力图与风力分布图最高温度热力图.png用 Seaborn 绘制通常展示月份与温度的关系矩阵或者各特征之间的相关性。热力图适合快速发现特征之间的线性关系比如最高温度和最低温度几乎必然强相关。import pandas as pd import seaborn as sns import matplotlib.pyplot as plt plt.rcParams[font.sans-serif] [SimHei] plt.rcParams[axes.unicode_minus] False df pd.read_csv(Output.csv, encodingutf-8-sig) # 选取数值列做相关性矩阵 num_cols [max_temp, min_temp, month, weekday] corr df[num_cols].corr() fig, ax plt.subplots(figsize(8, 6)) sns.heatmap(corr, annotTrue, fmt.2f, cmapcoolwarm, linewidths0.5, linecolorwhite, axax) ax.set_title(特征相关性热力图) plt.tight_layout() plt.savefig(最高温度热力图.png, dpi200)annotTrue在热力图格子里显示数值fmt.2f保留两位小数。cmapcoolwarm用红蓝渐变色红色正相关蓝色负相关看起来直观。风力情况.png一般用柱状图或饼图展示风力等级占比。如果数据里风力按等级分列比如北风 3~4 级建议先做归一化处理再统计否则图里会出现大量无意义的类别。5. 避坑与常见问题从环境安装到数据质量的 5 条踩坑记录5.1 中文编码问题导致爬虫解析不到数据现象fetch_weather()请求成功返回 HTML 也正常但解析出来的表格是空的或者数据全是乱码。原因部分天气站点的历史数据接口返回的是 GBK 或 GB2312 编码直接用resp.text解码时按 UTF-8 处理导致中文字段全部变乱码select 匹配不到有效节点。解决请求后用resp.encoding检查响应编码改成resp.apparent_encoding或直接resp.content.decode(gbk, errorsignore)再交 BeautifulSoup 解析。resp requests.get(url, headersheaders, timeout10) resp.encoding gbk # 按站点实际编码调整 soup BeautifulSoup(resp.text, html.parser)这属于爬虫的高频翻车点。遇到解析结果为空第一反应不是改选择器而是确认响应文本是否正确解码。5.2 CSV 字段类型不对导致回归结果全 NaN现象LinearRegression跑完model.predict()输出的预测值全是 NaN或者fit阶段直接报could not convert string to float。原因CSV 里温度列有 15℃ 这样的带单位字符串Pandas 默认读成 object 类型Sklearn 不接受字符串特征。解决建模前强制pd.to_numeric转换同时把非法字符替换成 NaN 再插值双保险。df[max_temp] df[max_temp].astype(str).str.replace(℃, ) df[max_temp] pd.to_numeric(df[max_temp], errorscoerce)5.3 Matplotlib 中文显示成方块现象图表的标题和坐标轴标签全部显示为空心方块英文正常。原因Matplotlib 默认字体不支持中文而系统中文环境与默认字体配置不匹配。解决在绘图入口统一设置字体。Linux 服务器上用SimHei不一定存在可以改成Noto Sans CJK SC或者WenQuanYi Zen Hei。Windows 上用SimHei最省事。import matplotlib.pyplot as plt plt.rcParams[font.sans-serif] [Noto Sans CJK SC, SimHei] plt.rcParams[axes.unicode_minus] False5.4 逻辑回归被误用做温度数值预测现象拿逻辑回归拟合温度模型不报错但预测结果要么是 0 要么是 1完全不是温度值。原因逻辑回归解决的是分类问题输出是概率。把它用在连续值回归上等于让分类器干回归的活底层数学就不成立。解决回归用LinearRegression、RandomForestRegressor或者单独做时间序列建模逻辑回归留给天气状态分类。这种问题不报错、能运行最难排查。遇到预测结果只有两个值时第一件事就是检查模型是不是逻辑回归。5.5 特征数量级差异大导致回归模型收敛异常现象线性回归模型拟合出的系数特别大或特别小预测结果震荡明显。原因月份范围是 1~12温度范围是 -10~35两者量级有差异但在合理范围内如果加入了湿度、气压这类 0~100 的数据量级差异会被放大。解决用StandardScaler做标准化让所有特征处于同一量级回归过程更稳定。from sklearn.preprocessing import StandardScaler scaler StandardScaler() X_scaled scaler.fit_transform(X)6. 让项目更实用预测结果验证与查询接口扩展6.1 用 MAE 和后视预测验证模型可信度前文已经提到单线性回归的 MAE 偏高说明模型精度有限。想验证模型是否真的学到了规律不能只看训练集表现。常见的做法是后视预测用前 70% 的数据训练预测后 30% 的数据再和真实值对比画在一张图上。这样能直观看到模型在未知数据上的表现。import pandas as pd import matplotlib.pyplot as plt from sklearn.linear_model import LinearRegression df pd.read_csv(Output.csv, encodingutf-8-sig) df df.sort_values(date).reset_index(dropTrue) split_idx int(len(df) * 0.7) train df[:split_idx] test df[split_idx:] model LinearRegression() model.fit(train[[month]], train[max_temp]) test_pred model.predict(test[[month]]) fig, ax plt.subplots(figsize(12, 5)) ax.plot(test[date], test[max_temp], label实际温度, color#1f77b4) ax.plot(test[date], test_pred, label预测温度, color#ff7f0e, linestyle--) ax.legend() fig.autofmt_xdate() plt.savefig(预测验证对比.png, dpi200)预测和实际两条线如果趋势一致但偏移大说明模型抓到了季节性方向但精度有瓶颈。这时可以换思路加入更多特征、改用多项式回归、或者直接上 ARIMA 做时间序列预测。项目里有实验报告.docx写实验结论的时候建议保留这张验证对比图比单独贴 MAE 数字更有说服力。6.2 把爬虫加预测封装成一个简单查询脚本项目自带的模块化结构适合扩展。最常见的升级方向是去掉手动改代码的习惯写一个交互式脚本输入城市和年份就能输出预测结果。import pandas as pd from sklearn.linear_model import LinearRegression df pd.read_csv(Output.csv, encodingutf-8-sig) def predict_temperature(city_name, target_month): city_data df[df[city] city_name] if len(city_data) 30: print(数据量太少无法预测) return None model LinearRegression() model.fit(city_data[[month]], city_data[max_temp]) pred model.predict([[target_month]]) return float(pred[0]) if __name__ __main__: city input(请输入城市名: ) month int(input(请输入目标月份(1-12): )) result predict_temperature(city, month) print(f{city} {month} 月份预测最高温度: {result:.1f}℃)封装时的取舍city列在原始爬虫数据里可能不存在需要把城市名作为常量写进 DataFrame。更妥帖的做法是在清洗阶段加一列city用爬虫请求时传入的城市名填充这样后续按城市筛选才有依据。这套源码我从下载到跑通大约花了两个晚上大部分时间消耗在编码和字段类型上而不是模型本身。从那以后我每次处理类似天气项目第一件事就强制走一遍检查 CSV 字段类型、检查中文字体、检查时间序列是否按日期排序这三步能省掉后面七成返工。整个项目最有价值的部分不只是预测模型而是爬虫、清洗、建模、绘图四个环节各自独立又能串联的设计改造成自己的工具链很顺手。希望这篇拆解能帮你在复现时少绕几个弯。本文还有配套的精品资源点击获取