
简介这是一份面向计算机、电子信息工程及数学类专业本科生的机器学习综合实践资源聚焦真实场景下的数据获取与分析闭环从爬取重庆历史天气与空气质量数据到完成质量趋势建模与可视化分析。资源包含6个核心文件3个Python脚本3个Excel数据表总大小仅369KB轻量易部署——其中爬虫脚本QualitySpider.py、ReadCQWeather.py支持参数化配置城市与时间范围预测脚本Prediction.py封装基础时序分析逻辑配套Excel文件涵盖原始爬取数据、清洗合并结果及空气质量指标结构清晰、用途明确。已有367人下载学习适合作为课程设计、期末大作业或毕业设计的参考范例。所有代码均经实测运行通过注释详尽、思路分层附带完整执行结果截图便于理解数据流向与算法落地过程作者为具备十年算法仿真经验的大厂工程师内容兼顾工程规范性与教学可读性。1. 项目缘起从一份作业到一个完整的数据分析项目最近在整理过往项目时翻到了一个挺有意思的“老古董”——一个关于重庆天气数据爬取与分析的机器学习作业。说它老是因为代码和思路现在看来有些稚嫩说它有意思是因为这个项目麻雀虽小五脏俱全完整地走了一遍从数据获取、清洗、分析到可视化的全流程非常适合刚接触数据科学或者想找个练手项目的朋友。这个项目的核心目标很明确获取重庆地区的历史天气数据并尝试用一些基础的机器学习方法对空气质量进行简单的分析和预测。听起来是不是有点像 Kaggle 上的入门竞赛没错它的本质就是一个微缩版的、有明确地域指向的数据分析项目。对于学生党来说这是一个绝佳的课程作业或毕业设计选题对于职场新人或转行朋友这也是一个能写在简历里、体现实操能力的完整案例。为什么是重庆一方面山城独特的地理环境和气候特征让它的天气数据尤其是空气质量分析变得有挑战性也更有趣。另一方面公开、稳定、结构化的天气数据源相对好找降低了项目启动的门槛。整个项目会涉及到几个关键环节Python 网络爬虫负责从公开网站抓取原始数据Pandas 和 NumPy进行数据清洗与预处理Matplotlib 和 Seaborn完成探索性数据分析和可视化最后Scikit-learn中的一些经典模型会登场尝试对空气质量等级进行分类或预测。接下来我会把这个项目的完整实现过程拆解开包括当初踩过的坑、做出的取舍以及事后回看可以优化的地方。无论你是想复现这个项目还是借鉴其中的思路应用到其他城市或领域相信都能有所收获。我们不仅会看到代码怎么写更重要的是理解为什么这么写以及在实际操作中可能会遇到什么。2. 数据基石如何稳定、合规地爬取重庆天气数据数据是分析的血液。第一步也是最关键的一步就是获取可靠、连续、字段丰富的重庆天气历史数据。市面上有很多提供天气数据的平台有收费的API也有免费的公开页面。对于学习项目我们当然优先考虑后者。2.1 目标网站分析与请求策略当初我选择的是一个大型气象数据网站的“历史天气”查询页面。这类页面通常通过城市和日期参数来动态加载数据。我们的目标是爬取重庆主城区过去几年的逐日天气数据包括日期、最高/最低气温、天气状况、风向风力、空气质量指数AQI及等级等。第一步是分析网页请求。打开浏览器的开发者工具F12切换到 Network网络标签然后查询某一天重庆的天气。你会发现数据很可能不是直接嵌在初始HTML里的而是通过一个额外的XHR异步请求获取的。这个请求的URL、请求方法通常是GET或POST、以及携带的参数如城市代码cityCode、日期date就是我们爬虫需要模拟的关键。这里有个重要的经验务必仔细检查请求头Headers。除了常见的User-Agent模拟浏览器有些网站会校验Referer来源页或使用Cookie进行会话管理。直接请求数据接口而不携带这些信息很可能返回空数据或错误页面。我的做法是把浏览器中成功请求到的所有Headers信息有选择地复制到爬虫的请求头字典里。import requests import pandas as pd from datetime import datetime, timedelta import time headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36, Referer: https://www.目标网站.com/weather/101040100.shtml, # 示例需替换 # 可能还有其他必要的头信息如 Accept, Accept-Language 等 } def fetch_single_day(city_code, query_date): 抓取指定城市单日天气数据 url https://api.目标网站.com/数据接口路径 # 示例需替换为真实接口 params { city: city_code, date: query_date.strftime(%Y-%m-%d) } try: response requests.get(url, headersheaders, paramsparams, timeout10) response.raise_for_status() # 检查HTTP错误 # 假设返回的是JSON格式 data response.json() return parse_weather_data(data) # 解析函数将JSON转为字典 except requests.exceptions.RequestException as e: print(f请求{query_date}数据失败: {e}) return None except ValueError as e: print(f解析{query_date}的JSON数据失败: {e}) return None2.2 循环抓取与友好访问我们需要爬取多日数据这就需要一个循环。但切记不要用for循环无间隔地狂发请求这会被网站视为攻击导致IP被暂时封禁。必须设置延迟。def fetch_date_range(city_code, start_date, end_date): 抓取指定日期范围内的数据 current_date start_date all_data [] while current_date end_date: daily_data fetch_single_day(city_code, current_date) if daily_data: all_data.append(daily_data) print(f成功获取 {current_date} 数据) else: print(f跳过 {current_date}数据获取失败) # 关键设置延迟模拟人类操作。时间间隔可以随机化更友好。 time.sleep(2 random.random()) # 随机延迟2-3秒 current_date timedelta(days1) return pd.DataFrame(all_data)延迟时间如2-3秒是一个平衡艺术。太短有风险太长效率低。对于学习项目数据量不大比如抓一年365条慢一点更稳妥。此外可以考虑使用random.uniform(1.5, 4)来让延迟时间随机化进一步降低被识别为机器人的概率。2.3 数据解析与字段提取接口返回的数据结构需要仔细解析。通常是一个嵌套的JSON。我们需要从中提取出有用的字段并转换成平坦的、适合表格存储的结构。def parse_weather_data(raw_json): 解析原始JSON提取所需字段 # 这是一个示例解析逻辑实际结构需根据目标网站调整 try: day_data raw_json[data][0] # 假设数据在data列表的第一个元素 parsed { date: day_data.get(date), high_temp: day_data.get(tempHigh), # 最高温 low_temp: day_data.get(tempLow), # 最低温 weather_day: day_data.get(dayWeather), # 白天天气 weather_night: day_data.get(nightWeather), # 夜间天气 wind_dir_day: day_data.get(dayWindDir), # 白天风向 wind_force_day: day_data.get(dayWindPower), # 白天风力 aqi: day_data.get(aqi), # 空气质量指数 aqi_level: day_data.get(quality), # 空气质量等级如优、良 pm25: day_data.get(pm25) # PM2.5浓度 } # 处理可能的缺失值或异常值例如‘-’代表无数据 for key, value in parsed.items(): if value - or value : parsed[key] None return parsed except (KeyError, IndexError, TypeError) as e: print(f解析数据结构时出错: {e}, 原始数据: {raw_json}) return None解析环节最容易出错。网站的数据结构可能微调字段名可能变化。因此异常捕获try-except和日志打印至关重要。它能让你的爬虫在遇到意外数据格式时不至于崩溃而是跳过该条记录并告诉你哪里出了问题便于后续排查。2.4 数据存储与增量更新抓取到的DataFrame应及时保存到本地推荐使用CSV格式因为它易于用文本编辑器查看也方便Pandas再次读取。# 假设 df 是 fetch_date_range 返回的 DataFrame if not df.empty: file_name fchongqing_weather_{start_date}_{end_date}.csv df.to_csv(file_name, indexFalse, encodingutf-8-sig) # utf-8-sig 解决Excel打开中文乱码 print(f数据已保存至 {file_name}) else: print(未获取到有效数据未保存文件。)对于需要长期维护的数据集可以考虑实现增量爬虫。即每次运行时先读取本地已存在的数据文件找出最新的日期然后只爬取该日期之后的新数据再合并保存。这能节省时间和网络资源也是对目标网站更友好的做法。3. 数据清洗与预处理从原始数据到分析就绪爬虫抓回来的数据通常是“脏”的直接用于分析会问题百出。数据清洗Data Cleaning是数据分析中耗时最长、也最考验耐心的环节。3.1 缺失值处理AQI数据的常见坑天气数据中缺失值很常见。尤其是AQI和PM2.5数据可能因为监测站点故障或数据传输问题在某些日期完全缺失。首先用Pandas加载数据并查看概况import pandas as pd import numpy as np df pd.read_csv(chongqing_weather_20220101_20231231.csv) print(df.info()) # 查看各字段非空数量、类型 print(df.isnull().sum()) # 统计各字段缺失值数量处理缺失值有多种策略删除如果某一行关键字段如日期、AQI缺失且缺失量很少5%可以直接删除该行。df.dropna(subset[aqi, pm25], inplaceTrue)填充对于数值型数据如温度可以用前后几天的平均值移动平均或中位数来填充。df[high_temp].fillna(df[high_temp].rolling(3, min_periods1).mean(), inplaceTrue)标记对于分类数据如天气状况可以创建一个新的类别如“未知”来填充。df[weather_day].fillna(未知, inplaceTrue)不处理针对模型有些机器学习算法如XGBoost可以原生处理缺失值。但更多时候我们需要先处理。我的经验是对于AQI这类核心分析目标如果某天完全缺失且无法通过可靠方法插补宁愿删除该条记录也不要随意填充以免引入噪音影响后续分析的结论可靠性。3.2 异常值检测与修正异常值可能是真实的极端天气也可能是数据错误。例如重庆夏季最高温超过45度冬季最低温低于-5度这需要结合常识和重庆的气候特点来判断。# 通过描述性统计和可视化发现异常 print(df[[high_temp, low_temp, aqi]].describe()) import matplotlib.pyplot as plt plt.figure(figsize(12,4)) plt.subplot(131) df[high_temp].hist(bins50) plt.title(最高温分布) plt.subplot(132) df[low_temp].hist(bins50) plt.title(最低温分布) plt.subplot(133) df[aqi].hist(bins50) plt.title(AQI分布) plt.tight_layout() plt.show()发现异常值后处理方法包括盖帽法Capping将超出合理范围的值替换为边界值。例如设定最高温上限为42度下限为-2度。df[high_temp] df[high_temp].clip(lower-2, upper42)视为缺失值处理将明显错误的值如温度999替换为NaN然后按缺失值方法处理。结合上下文修正如果某天温度异常但天气描述是“晴”可以查看前后几天的温度用插值法修正。3.3 特征工程创造更有价值的输入原始数据字段有时不能直接用于模型。我们需要通过特征工程Feature Engineering创造新的、对预测目标更有帮助的特征。日期特征提取日期本身是字符串或时间戳但其中蕴含周期性信息。df[date] pd.to_datetime(df[date]) df[year] df[date].dt.year df[month] df[date].dt.month df[day_of_month] df[date].dt.day df[day_of_week] df[date].dt.dayofweek # 周一0周日6 df[is_weekend] df[day_of_week].apply(lambda x: 1 if x 5 else 0) df[season] df[month].apply(lambda m: (m%12 3)//3) # 1:春,2:夏,3:秋,4:冬温差特征df[temp_range] df[high_temp] - df[low_temp]。昼夜温差可能对空气质量有影响。滞后特征Lag Features今天的空气质量很可能和昨天、前天有关。df[aqi_lag1] df[aqi].shift(1) # 前一天的AQI df[aqi_lag2] df[aqi].shift(2) # 前两天的AQI分类数据编码天气状况晴、多云、雨、风向北风、东南风是文本需要转为数值。标签编码Label Encoding适用于有大小顺序的类别如空气质量等级优良轻度污染...。from sklearn.preprocessing import LabelEncoder独热编码One-Hot Encoding适用于无序类别如天气状况。pd.get_dummies(df, columns[weather_day, wind_dir_day])。注意这可能造成特征维度爆炸对于类别很多的字段要谨慎。特征工程是提升模型性能的关键但也最容易导致过拟合。创造的特征一定要有业务或物理意义并且需要在训练集上完成拟合如计算平均值、编码映射再应用到测试集避免数据泄露。4. 探索性数据分析用可视化洞察重庆天气与空气质量在把数据喂给模型之前我们必须先“看”懂它。探索性数据分析EDA能帮助我们理解数据分布、发现规律、验证假设并为后续的模型选择提供依据。4.1 空气质量的时间序列趋势首先我们最关心的是AQI随时间的变化。plt.figure(figsize(15,5)) plt.plot(df[date], df[aqi], linewidth0.5, alpha0.7) plt.axhline(y100, colorr, linestyle--, alpha0.5, labelAQI100 (良/轻度污染边界)) plt.xlabel(日期) plt.ylabel(AQI) plt.title(重庆日度AQI时间序列趋势) plt.legend() plt.grid(True, alpha0.3) plt.show()从图中你可以直观地看到重庆的空气质量是否存在明显的季节性规律例如冬季是否因扩散条件差而AQI更高是否有长期改善或恶化的趋势极端污染事件发生在什么时候为了更清晰地观察季节性我们可以按月份聚合monthly_avg df.groupby(month)[aqi].mean() plt.figure(figsize(10,4)) monthly_avg.plot(kindbar, colorskyblue) plt.axhline(y100, colorr, linestyle--, alpha0.5) plt.xlabel(月份) plt.ylabel(月均AQI) plt.title(重庆月均AQI分布多年平均) plt.xticks(rotation0) plt.grid(axisy, alpha0.3) plt.show()4.2 气象要素与空气质量的相关性分析空气质量受多种气象因素影响。我们可以计算相关系数矩阵并用热图可视化。# 选择数值型特征 numeric_features [high_temp, low_temp, temp_range, pm25, aqi, month, day_of_week] corr_matrix df[numeric_features].corr() import seaborn as sns plt.figure(figsize(10,8)) sns.heatmap(corr_matrix, annotTrue, fmt.2f, cmapcoolwarm, center0, squareTrue) plt.title(气象要素与AQI相关性热图) plt.tight_layout() plt.show()重点关注AQI这一列。你可能会发现pm25与aqi高度正相关这是显然的PM2.5是AQI的主要组分。temp_range温差可能与aqi呈负相关较大的昼夜温差有时意味着大气垂直对流较强有利于污染物扩散。month与aqi的相关性印证了之前的季节性观察。注意相关性不等于因果关系。热图展示的是线性相关关系。更深层的影响可能需要通过更复杂的统计模型或领域知识来解释。4.3 不同天气状况下的空气质量对比天气状况晴、雨、多云等对空气质量有直接影响。我们可以用箱型图来观察不同天气下AQI的分布差异。# 假设我们已经对 weather_day 进行了归类合并了相似类别如‘小雨’、‘中雨’合并为‘雨’ plt.figure(figsize(12,6)) order df.groupby(weather_day)[aqi].median().sort_values().index # 按中位数排序 sns.boxplot(xweather_day, yaqi, datadf, orderorder) plt.axhline(y100, colorr, linestyle--, alpha0.5) plt.xlabel(白天天气状况) plt.ylabel(AQI) plt.title(不同天气状况下AQI分布对比) plt.xticks(rotation45) plt.tight_layout() plt.show()箱型图可以告诉我们雨天是否普遍对应更低的AQI多云和晴天的空气质量中位数有何差异不同天气状况下AQI的波动范围箱子的高度有多大这些直观的观察能为后续的建模提供特征选择的方向例如是否将“天气状况”作为一个重要的分类特征引入模型。5. 机器学习模型初探预测空气质量等级经过充分的EDA我们对数据有了感性认识。现在可以尝试构建一个简单的机器学习模型来预测重庆的空气质量等级例如二分类“良及以下” vs “轻度污染及以上”或多分类优、良、轻度污染等。5.1 问题定义与数据准备我们将问题定义为监督学习中的分类问题。目标变量Label空气质量等级aqi_level需要从文本如“优”、“良”转换为数值标签如012...。特征变量Features选择与目标可能相关的特征如月份、最高温、最低温、温差、前一天AQI、天气状况编码后、风向编码后等。首先准备特征X和标签y并划分训练集和测试集。务必注意时间序列数据不能随机划分如果随机打乱模型可能会用“未来”的数据来学习预测“过去”造成数据泄露得到过于乐观的评估结果。正确的做法是按时间顺序划分例如用前80%的数据做训练后20%做测试。from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler, LabelEncoder # 1. 准备特征和标签 feature_cols [month, high_temp, low_temp, temp_range, aqi_lag1, weather_day_encoded, wind_dir_encoded] # 假设已创建了编码后的特征列 ‘weather_day_encoded’ 和 ‘wind_dir_encoded’ X df[feature_cols].dropna() # 删除因创建滞后特征而产生的初始行NA y df.loc[X.index, aqi_level_encoded] # 对应的标签也已编码 # 2. 按时间顺序划分假设数据已按日期排序 split_idx int(len(X) * 0.8) X_train, X_test X.iloc[:split_idx], X.iloc[split_idx:] y_train, y_test y.iloc[:split_idx], y.iloc[split_idx:] # 3. 特征标准化对基于距离的模型如SVM、KNN很重要 scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) # 只在训练集上拟合scaler X_test_scaled scaler.transform(X_test) # 用训练集的参数转换测试集5.2 模型选择与训练对于入门级的分类问题我们可以尝试几种经典算法比较它们的性能。from sklearn.linear_model import LogisticRegression from sklearn.tree import DecisionTreeClassifier from sklearn.ensemble import RandomForestClassifier from sklearn.svm import SVC from sklearn.metrics import accuracy_score, classification_report, confusion_matrix models { Logistic Regression: LogisticRegression(max_iter1000, random_state42), Decision Tree: DecisionTreeClassifier(random_state42), Random Forest: RandomForestClassifier(n_estimators100, random_state42), SVM: SVC(kernelrbf, random_state42) } results {} for name, model in models.items(): model.fit(X_train_scaled, y_train) y_pred model.predict(X_test_scaled) acc accuracy_score(y_test, y_pred) results[name] acc print(f{name} 准确率: {acc:.4f}) # 打印更详细的评估报告 print(classification_report(y_test, y_pred, target_names[优,良,轻度污染])) # 假设是三类 print(-*50)为什么选择这几个模型逻辑回归基线模型简单、可解释性强适合线性可分问题。决策树非线性能捕捉特征交互但容易过拟合。随机森林决策树的集成通过“投票”降低过拟合风险通常表现稳健是很好的基准模型。支持向量机SVM在高维空间寻找最优分类边界对特征缩放敏感所以我们做了标准化。5.3 模型评估与特征重要性分析准确率只是一个宏观指标。对于类别不平衡的数据集比如“优”和“良”的天数远多于“污染”天数我们需要看更细化的指标精确率Precision、召回率Recall和F1-score。classification_report已经提供了这些。对于随机森林这类模型我们还可以查看特征重要性这能帮助我们理解模型是如何做决策的并可能指导我们进行特征筛选。# 以随机森林为例 rf_model models[Random Forest] importances rf_model.feature_importances_ feature_names X_train.columns importance_df pd.DataFrame({feature: feature_names, importance: importances}).sort_values(importance, ascendingFalse) plt.figure(figsize(10,6)) plt.barh(importance_df[feature], importance_df[importance]) plt.xlabel(特征重要性) plt.title(随机森林模型特征重要性排序) plt.gca().invert_yaxis() # 最重要的在顶部 plt.tight_layout() plt.show()你可能会发现aqi_lag1前一天的AQI是最重要的特征这符合空气污染具有连续性的常识。month月份和temp_range温差也可能排名靠前。如果某些特征重要性几乎为0可以考虑在后续迭代中移除它们简化模型。5.4 常见陷阱与调优思路过拟合Overfitting模型在训练集上表现完美在测试集上却很差。决策树尤其容易过拟合。对策使用随机森林对决策树进行剪枝设置max_depth,min_samples_split等参数增加训练数据量。类别不平衡Class Imbalance如果“污染”天数很少模型可能倾向于总是预测“非污染”导致对污染天的召回率极低。对策使用class_weightbalanced参数如果模型支持对少数类进行过采样如SMOTE算法或使用更适合的评估指标如AUC-ROC。数据泄露Data Leakage这是我们反复强调的。除了时间划分错误另一个常见泄露是在整个数据集上做特征缩放或编码后再划分。务必确保所有预处理步骤fit只使用训练集数据。超参数调优模型的默认参数不一定最优。可以使用GridSearchCV或RandomizedSearchCV进行网格搜索或随机搜索寻找最佳参数组合。但要注意交叉验证也要按时间序列的方式进行如TimeSeriesSplit不能打乱数据。这个机器学习环节的目的不在于构建一个预测精度极高的复杂模型而在于完整地走通一个数据分析流程理解每个步骤的意义和潜在问题。对于天气预测这种受复杂系统影响的任务简单的模型往往能提供一个不错的基线并帮助我们量化不同因素对空气质量的影响程度。6. 项目总结与扩展思考回顾这个“重庆天气质量分析”项目它从一个简单的数据抓取需求开始逐步深入到数据清洗、探索分析并最终尝试用机器学习模型去理解和预测空气质量。整个过程更像是一个标准的数据科学微型工作流的演练。几个关键的实操心得爬虫的稳健性高于效率对于学习或小规模项目爬虫代码的健壮性完善的错误处理、日志记录、友好延迟比极限速度重要得多。一次成功的、完整的数据抓取胜过多次因被封禁而中断的快速抓取。EDA是模型的指路明灯跳过EDA直接建模是危险的。可视化图表和相关性分析不仅能帮你发现数据问题更能形成对问题的直觉指导特征工程和模型选择。例如通过时间序列图发现季节性你就会想到加入“月份”特征。理解评估指标背后的意义准确率90%听起来很高但如果你的数据中90%都是“良”一个总是预测“良”的傻瓜模型也能达到90%准确率。因此必须结合混淆矩阵、精确率、召回率等指标综合判断模型在每一类上的表现。从简单模型开始逻辑回归或随机森林作为基线模型非常合适。它们训练快可解释性强。在基线模型表现不佳时再去尝试更复杂的模型如梯度提升树、神经网络才有意义。永远记住“没有免费的午餐”定理复杂模型不一定更好。这个项目还可以如何扩展更丰富的特征引入更多气象数据如湿度、气压、降水量、风速等。甚至可以尝试加入节假日信息、前几天的污染累积效应等。更复杂的模型尝试使用LSTM长短期记忆网络等时序模型来捕捉空气质量的长周期依赖关系。预测目标变化不预测等级而是回归预测具体的AQI数值。或者预测未来多天的空气质量多步预测。部署为简单应用使用Flask或Streamlit搭建一个简单的Web应用输入日期输出该日空气质量的预测结果和可视化图表。最后这个项目所有的源代码、清洗后的数据集、详细的文档说明包括环境配置、步骤解读、遇到的问题及解决方案都应该妥善整理和归档。这不仅是为了作业提交更是为了你日后回顾、复用或向他人展示时能快速理解当时的思路。一个好的数据项目其价值一半在分析结果另一半则在可复现、可理解的工程实现过程中。本文还有配套的精品资源点击获取