从数据到洞察:如何用Python爬虫和机器学习预测空气质量趋势
·
从数据到洞察:Python爬虫与机器学习驱动的空气质量预测实战
当城市上空笼罩着灰蒙蒙的雾霾,我们是否能够提前预知明天的空气质量?本文将带你深入探索如何用Python构建从数据采集到预测分析的完整解决方案,揭示隐藏在空气质量数据中的规律与趋势。
1. 空气质量数据采集:高效爬虫设计
获取准确、及时的空气质量数据是预测分析的第一步。我们选择PM2.5.in作为数据源,这个网站提供了全国主要城市的实时空气质量指数(AQI)及各项污染物浓度数据。
1.1 爬虫核心组件设计
import requests
from bs4 import BeautifulSoup
import pandas as pd
import time
import random
class AQISpider:
def __init__(self):
self.headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
}
self.base_url = "https://www.pm25.in"
def get_city_list(self):
"""获取所有支持的城市列表"""
response = requests.get(self.base_url, headers=self.headers)
soup = BeautifulSoup(response.text, 'html.parser')
city_links = soup.select('.city_list a')
return {link.text: link['href'] for link in city_links}
关键点说明:
- 使用随机User-Agent轮换避免反爬
- 采用BeautifulSoup解析HTML结构
- 城市列表动态获取确保数据全面性
1.2 数据抓取与存储优化
def fetch_city_aqi(self, city_name, city_path):
"""获取单个城市的详细AQI数据"""
url = f"{self.base_url}{city_path}"
try:
response = requests.get(url, headers=self.headers)
soup = BeautifulSoup(response.text, 'html.parser')
# 解析表格数据
table = soup.find('table', {'id': 'detail-data'})
rows = table.find_all('tr')[1:] # 跳过表头
data = []
for row in rows:
cols = row.find_all('td')
record = {
'city': city_name,
'time': cols[0].text.strip(),
'aqi': int(cols[1].text),
'pm2_5': int(cols[3].text),
# 其他污染物字段...
}
data.append(record)
return pd.DataFrame(data)
except Exception as e:
print(f"Error fetching {city_name}: {str(e)}")
return None
存储策略对比:
| 存储方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| CSV文件 | 简单易用,无需额外服务 | 查询效率低,不适合大数据量 | 小型项目,临时存储 |
| SQLite | 轻量级,单文件数据库 | 并发性能有限 | 中小型项目,本地分析 |
| MySQL | 支持复杂查询,性能好 | 需要单独部署维护 | 生产环境,团队协作 |
| MongoDB | 灵活schema,适合非结构化数据 | 内存占用较高 | 快速迭代,文档型数据 |
提示:对于长期监测项目,建议采用MySQL+MongoDB混合存储方案,结构化数据存入MySQL,原始HTML快照存入MongoDB
2. 数据清洗与特征工程
原始数据往往包含噪声和缺失值,高质量的特征工程能显著提升模型性能。
2.1 数据清洗实战
def clean_aqi_data(df):
"""数据清洗流程"""
# 处理缺失值
df = df.dropna(subset=['aqi', 'pm2_5'])
# 去除异常值
df = df[(df['aqi'] > 0) & (df['aqi'] <= 500)]
df = df[(df['pm2_5'] >= 0) & (df['pm2_5'] <= 500)]
# 时间字段标准化
df['datetime'] = pd.to_datetime(df['time'])
df['hour'] = df['datetime'].dt.hour
df['day_of_week'] = df['datetime'].dt.dayofweek
return df
2.2 特征构建技巧
- 时间特征:小时、星期、是否为节假日
- 气象特征:温度、湿度、风速(可从气象API获取)
- 空间特征:城市经纬度、海拔高度
- 滞后特征:前3/6/12小时AQI值
- 移动统计:24小时AQI移动平均、标准差
# 创建滞后特征示例
def create_lag_features(df, lags=[3, 6, 12]):
for lag in lags:
df[f'aqi_lag_{lag}'] = df.groupby('city')['aqi'].shift(lag)
return df
# 添加移动统计量
df['aqi_rolling_24h_mean'] = df.groupby('city')['aqi'].rolling(24).mean().values
3. 时间序列预测模型构建
空气质量数据具有明显的时间依赖性,我们需要选择合适的时序模型。
3.1 模型选型对比
| 模型类型 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| ARIMA | 理论成熟,解释性强 | 需手动确定参数,线性假设 | 平稳时间序列 |
| Prophet | 自动处理季节项,鲁棒性强 | 计算资源消耗较大 | 具有明显季节规律的数据 |
| LSTM | 捕捉长期依赖,非线性建模 | 需要大量数据,调参复杂 | 复杂时序模式 |
| XGBoost | 特征重要性明确,性能优越 | 需手动构建时序特征 | 特征丰富的场景 |
3.2 LSTM模型实现
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import LSTM, Dense, Dropout
def build_lstm_model(input_shape):
model = Sequential([
LSTM(64, return_sequences=True, input_shape=input_shape),
Dropout(0.2),
LSTM(32),
Dropout(0.2),
Dense(1)
])
model.compile(optimizer='adam', loss='mse')
return model
# 数据标准化
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
scaled_data = scaler.fit_transform(df[['aqi', 'pm2_5']])
# 创建时间步长数据
def create_dataset(data, time_steps=24):
X, y = [], []
for i in range(len(data)-time_steps):
X.append(data[i:(i+time_steps)])
y.append(data[i+time_steps, 0]) # 预测aqi
return np.array(X), np.array(y)
X, y = create_dataset(scaled_data)
model = build_lstm_model((X.shape[1], X.shape[2]))
model.fit(X, y, epochs=50, batch_size=32, validation_split=0.2)
4. 预测结果可视化与分析
直观的视觉呈现有助于理解模型预测效果和数据规律。
4.1 动态预测可视化
import plotly.express as px
def plot_prediction(actual, predicted, city):
fig = px.line(title=f'{city} AQI预测对比')
fig.add_scatter(y=actual, name='实际值')
fig.add_scatter(y=predicted, name='预测值')
fig.update_layout(
xaxis_title='时间',
yaxis_title='AQI',
hovermode='x unified'
)
return fig
# 生成未来24小时预测
future_steps = 24
last_sequence = scaled_data[-time_steps:]
predictions = []
for _ in range(future_steps):
pred = model.predict(last_sequence.reshape(1, time_steps, -1))
predictions.append(pred[0,0])
last_sequence = np.roll(last_sequence, -1, axis=0)
last_sequence[-1] = pred[0]
predictions = scaler.inverse_transform(
np.column_stack([np.array(predictions), np.zeros(future_steps)])
)[:,0]
4.2 污染物相关性分析
import seaborn as sns
corr_matrix = df[['aqi', 'pm2_5', 'pm10', 'so2', 'no2', 'co', 'o3']].corr()
plt.figure(figsize=(10,8))
sns.heatmap(corr_matrix, annot=True, cmap='coolwarm', center=0)
plt.title('污染物相关性热力图')
plt.show()
典型发现:
- PM2.5与AQI相关系数达0.92,是主要影响因素
- O3与其他污染物呈负相关,夏季表现明显
- 风速与AQI呈负相关,验证了扩散作用
5. 系统优化与生产部署
将预测模型转化为可持续运行的服务需要考虑多方面因素。
5.1 自动化数据管道
from airflow import DAG
from airflow.operators.python_operator import PythonOperator
from datetime import datetime, timedelta
default_args = {
'owner': 'airflow',
'depends_on_past': False,
'start_date': datetime(2023, 1, 1),
'retries': 3,
}
dag = DAG(
'aqi_prediction_pipeline',
default_args=default_args,
schedule_interval='0 3 * * *', # 每天凌晨3点运行
)
def fetch_data():
spider = AQISpider()
df = spider.run()
df.to_parquet(f'/data/raw/{datetime.now().date()}.parquet')
fetch_task = PythonOperator(
task_id='fetch_aqi_data',
python_callable=fetch_data,
dag=dag,
)
# 添加其他任务节点...
5.2 模型性能监控指标
| 指标 | 计算公式 | 健康范围 | 说明 |
|---|---|---|---|
| 预测准确率 | 1 - MAPE | >80% | 平均绝对百分比误差 |
| 数据新鲜度 | 当前时间 - 最新数据时间 | <1小时 | 数据时效性 |
| 特征覆盖率 | 可用特征数/总特征数 | >95% | 数据完整性 |
| 预测稳定性 | 预测结果标准差 | <10 | 输出波动程度 |
在实际项目中,我们还需要考虑模型的可解释性。SHAP值分析可以帮助理解各个特征对预测结果的影响程度:
import shap
# 对XGBoost模型进行SHAP分析
explainer = shap.TreeExplainer(model)
shap_values = explainer.shap_values(X_test)
plt.figure(figsize=(10,6))
shap.summary_plot(shap_values, X_test, feature_names=feature_names)
plt.title('特征重要性分析')
plt.show()
从分析结果可以发现,历史AQI值、PM2.5浓度和小时时段是最具影响力的三个特征,这与空气质量变化的物理规律高度一致。这种可解释性分析不仅验证了模型的合理性,也为进一步优化特征工程提供了方向。
更多推荐
所有评论(0)