㊗️本期内容已收录至专栏《Python爬虫实战》,持续完善知识体系与项目实战,建议先订阅收藏,后续查阅更方便~
㊙️本期爬虫难度指数:⭐⭐
🉐福利: 一次订阅后,专栏内的所有文章可永久免费看,持续更新中,保底1000+(篇)硬核实战内容。

全文目录:
-
-
- [🌟 开篇语](#🌟 开篇语)
- [0️⃣ 前言(Preface)](#0️⃣ 前言(Preface))
- [1️⃣ 摘要(Abstract)](#1️⃣ 摘要(Abstract))
- [2️⃣ 背景与需求(Why)](#2️⃣ 背景与需求(Why))
- [3️⃣ 合规与注意事项(必写) 🛑](#3️⃣ 合规与注意事项(必写) 🛑)
- [4️⃣ 技术选型与整体流程(What/How) 🛠️](#4️⃣ 技术选型与整体流程(What/How) 🛠️)
- [5️⃣ 环境准备与依赖安装(可复现) 💻](#5️⃣ 环境准备与依赖安装(可复现) 💻)
- [6️⃣ 核心实现:多源采集层(Fetcher) 📡](#6️⃣ 核心实现:多源采集层(Fetcher) 📡)
- [7️⃣ 核心实现:数据融合与清洗(Merger) 🌪️](#7️⃣ 核心实现:数据融合与清洗(Merger) 🌪️)
- [8️⃣ 可视化与相关性分析(Analyzer & Viz) 📊](#8️⃣ 可视化与相关性分析(Analyzer & Viz) 📊)
- [9️⃣ 运行方式与结果展示 🚀](#9️⃣ 运行方式与结果展示 🚀)
- [🔟 常见问题与排错(Debug 指南) 🐞](#🔟 常见问题与排错(Debug 指南) 🐞)
- [1️⃣1️⃣ 进阶优化(加分项) ⚡](#1️⃣1️⃣ 进阶优化(加分项) ⚡)
- [1️⃣2️⃣ 总结与延伸阅读 📚](#1️⃣2️⃣ 总结与延伸阅读 📚)
- [🌟 文末](#🌟 文末)
-
- [✅ 专栏持续更新中|建议收藏 + 订阅](#✅ 专栏持续更新中|建议收藏 + 订阅)
- [✅ 互动征集](#✅ 互动征集)
- [✅ 免责声明](#✅ 免责声明)
-
🌟 开篇语
哈喽,各位小伙伴们你们好呀~我是【喵手】。
运营社区: C站 / 掘金 / 腾讯云 / 阿里云 / 华为云 / 51CTO
欢迎大家常来逛逛,一起学习,一起进步~🌟
我长期专注 Python 爬虫工程化实战 ,主理专栏 《Python爬虫实战》:从采集策略 到反爬对抗 ,从数据清洗 到分布式调度 ,持续输出可复用的方法论与可落地案例。内容主打一个"能跑、能用、能扩展 ",让数据价值真正做到------抓得到、洗得净、用得上。
📌 专栏食用指南(建议收藏)
- ✅ 入门基础:环境搭建 / 请求与解析 / 数据落库
- ✅ 进阶提升:登录鉴权 / 动态渲染 / 反爬对抗
- ✅ 工程实战:异步并发 / 分布式调度 / 监控与容错
- ✅ 项目落地:数据治理 / 可视化分析 / 场景化应用
📣 专栏推广时间 :如果你想系统学爬虫,而不是碎片化东拼西凑,欢迎订阅专栏👉《Python爬虫实战》👈,一次订阅后,专栏内的所有文章可永久免费阅读,持续更新中。
💕订阅后更新会优先推送,按目录学习更高效💯~
0️⃣ 前言(Preface)
雾霾天出门要戴口罩吗?开窗通风会不会吸入更多灰尘?
今天我们要构建一个"环境监测站"。我们将同时调用两个 API 接口,将天气数据与空气质量数据进行时间轴对齐 ,清洗合并后,利用数据可视化技术,揭示天气变化对空气质量的深层影响。
读完这篇,你能获得:
- 多源数据融合:学会如何将两个不同 API 的数据(Weather + Air Quality)按时间戳合并(Merge)。
- 科学归因:通过散点图和相关系数,量化分析"风速"与"PM2.5"的关系。
- 自动化报表:生成一张包含双轴趋势图的专业环境分析报告。
1️⃣ 摘要(Abstract)
本文利用 requests 库并行请求 Open-Meteo 的气象与空气质量接口。我们获取指定城市(如北京)过去 7 天的小时级数据。利用 Pandas 的 merge 功能将异构数据整合成一张宽表。最后,使用 Seaborn 绘制"风速-污染物回归分析图",并生成包含 AQI 趋势的时间序列图表。
2️⃣ 背景与需求(Why)
为什么要爬?
- 健康防护:通过分析历史数据,预测在特定的天气条件下(如低压、无风),空气质量是否会恶化。
- 科学研究:验证"静稳天气"是否是雾霾的元凶。
目标清单:
-
目标源:Open-Meteo Weather API & Air Quality API.
-
目标字段:
Time(时间)Temperature(气温)Wind_Speed(风速 - 关键因子)PM2_5(细颗粒物浓度)US_AQI(美国标准空气质量指数)
3️⃣ 合规与注意事项(必写) 🛑
- API 配额:Open-Meteo 免费版限制每天 10,000 次调用,对于个人项目绰绰有余。请勿进行高频并发轰炸。
- 数据准确性:API 数据通常来自卫星反演和地面监测站插值,可能与你家门口的实测值有细微偏差。
- 坐标系:使用 WGS84 坐标(GPS标准)。
4️⃣ 技术选型与整体流程(What/How) 🛠️
技术栈:
- 采集 :
requests。 - 清洗 :
pandas(核心是pd.merge)。 - 分析 :
seaborn(画回归图非常强)。
流程图:
Fetch Weather + Fetch AirQuality → Pandas Merge (按小时对齐) → Correlation Analysis (相关性) → Visualization (双轴图/回归图)
5️⃣ 环境准备与依赖安装(可复现) 💻
Python 3.8+。
项目结构:
text
env_monitor/
├── env_spider.py # 主程序
├── data/
│ └── bj_env_data.csv
└── charts/
├── aqi_trend.png
└── wind_vs_pm25.png
依赖安装:
bash
pip install requests pandas seaborn matplotlib
6️⃣ 核心实现:多源采集层(Fetcher) 📡
我们需要写两个函数,分别请求两个不同的 URL,但参数(经纬度)必须一致。
python
import requests
import pandas as pd
class EnvFetcher:
def __init__(self, lat, lon):
self.lat = lat
self.lon = lon
# 1. 天气 API
self.weather_url = "https://api.open-meteo.com/v1/forecast"
# 2. 空气质量 API
self.air_url = "https://air-quality-api.open-meteo.com/v1/air-quality"
def fetch_weather(self):
"""获取天气数据 (气温、湿度、风速)"""
params = {
"latitude": self.lat,
"longitude": self.lon,
"hourly": "temperature_2m,relative_humidity_2m,wind_speed_10m",
"past_days": 7, # 抓过去7天的数据用于分析
"forecast_days": 1
}
try:
resp = requests.get(self.weather_url, params=params)
data = resp.json()
# 提取小时级数据
hourly = data.get('hourly', {})
df = pd.DataFrame({
'time': hourly.get('time'),
'temperature': hourly.get('temperature_2m'),
'humidity': hourly.get('relative_humidity_2m'),
'wind_speed': hourly.get('wind_speed_10m')
})
return df
except Exception as e:
print(f"❌ Weather Fetch Failed: {e}")
return pd.DataFrame()
def fetch_air_quality(self):
"""获取空气质量数据 (PM2.5, AQI)"""
params = {
"latitude": self.lat,
"longitude": self.lon,
"hourly": "pm2_5,us_aqi",
"past_days": 7,
"forecast_days": 1
}
try:
resp = requests.get(self.air_url, params=params)
data = resp.json()
hourly = data.get('hourly', {})
df = pd.DataFrame({
'time': hourly.get('time'),
'pm25': hourly.get('pm2_5'),
'aqi': hourly.get('us_aqi')
})
return df
except Exception as e:
print(f"❌ AirQuality Fetch Failed: {e}")
return pd.DataFrame()
7️⃣ 核心实现:数据融合与清洗(Merger) 🌪️
这是最关键的一步。两个 API 返回的时间序列长度可能不完全一致,我们要用 time 字段作为 Key 进行合并。
python
class DataProcessor:
def merge_data(self, df_weather, df_air):
if df_weather.empty or df_air.empty:
return pd.DataFrame()
# 1. 确保时间列格式一致
df_weather['time'] = pd.to_datetime(df_weather['time'])
df_air['time'] = pd.to_datetime(df_air['time'])
# 2. 合并 (Inner Join)
# 只有两个表都有的时间点才保留,确保数据对齐
df_merged = pd.merge(df_weather, df_air, on='time', how='inner')
# 3. 简单的特征工程
# 标记是否污染 (AQI > 100 为不健康)
df_merged['is_polluted'] = df_merged['aqi'] > 100
return df_merged
8️⃣ 可视化与相关性分析(Analyzer & Viz) 📊
我们要画两张图:
- AQI 趋势图:看看这一周空气好不好。
- 相关性分析图 :重点看 风速 vs PM2.5。理论上,风越大,污染物越容易扩散(负相关)。
python
import matplotlib.pyplot as plt
import seaborn as sns
import os
class EnvVisualizer:
def plot_trend(self, df, city_name):
"""绘制双轴图:左轴温度,右轴 AQI"""
plt.style.use('bmh')
fig, ax1 = plt.subplots(figsize=(12, 6))
# 左轴:温度 (曲线)
color = 'tab:red'
ax1.set_xlabel('Date Time')
ax1.set_ylabel('Temperature (°C)', color=color)
ax1.plot(df['time'], df['temperature'], color=color, alpha=0.6, label='Temp')
ax1.tick_params(axis='y', labelcolor=color)
# 右轴:AQI (柱状/填充)
ax2 = ax1.twinx()
color = 'tab:grey'
ax2.set_ylabel('US AQI', color=color)
# 填充颜色表示污染程度
ax2.fill_between(df['time'], df['aqi'], color='black', alpha=0.3, label='AQI')
ax2.tick_params(axis='y', labelcolor=color)
plt.title(f'Env Analysis: Weather vs Air Quality ({city_name})')
fig.tight_layout()
filename = 'charts/aqi_trend.png'
plt.savefig(filename)
print(f"✅ Chart saved: {filename}")
def plot_correlation(self, df):
"""绘制散点回归图:风速 vs PM2.5"""
plt.figure(figsize=(8, 6))
# 使用 seaborn 绘制带有回归线的散点图
sns.regplot(x='wind_speed', y='pm25', data=df,
scatter_kws={'alpha':0.5}, line_kws={'color':'red'})
plt.title('Correlation: Wind Speed vs PM2.5 Concentration')
plt.xlabel('Wind Speed (10m) [km/h]')
plt.ylabel('PM2.5 [μg/m³]')
plt.grid(True, linestyle='--')
filename = 'charts/wind_vs_pm25.png'
plt.savefig(filename)
print(f"✅ Chart saved: {filename}")
9️⃣ 运行方式与结果展示 🚀
我们以 北京 (Beijing) 为例(Lat: 39.9, Lon: 116.4)。
python
import os
if __name__ == "__main__":
# 创建目录
os.makedirs("data", exist_ok=True)
os.makedirs("charts", exist_ok=True)
print("🌍 Starting Environmental Monitor (Beijing)...")
# 1. 采集
# 北京坐标
fetcher = EnvFetcher(lat=39.9042, lon=116.4074)
print(" ⬇️ Fetching Weather Data...")
df_weather = fetcher.fetch_weather()
print(" ⬇️ Fetching Air Quality Data...")
df_air = fetcher.fetch_air_quality()
# 2. 清洗与融合
processor = DataProcessor()
df_final = processor.merge_data(df_weather, df_air)
if not df_final.empty:
# 保存数据
df_final.to_csv("data/bj_env_data.csv", index=False)
print(f"✅ Data Merged & Saved: {len(df_final)} records.")
# 3. 分析与展示
viz = EnvVisualizer()
# 生成趋势图
viz.plot_trend(df_final, "Beijing")
# 生成相关性分析图
viz.plot_correlation(df_final)
# 简单统计
avg_aqi = df_final['aqi'].mean()
max_pm25 = df_final['pm25'].max()
print(f"\n📊 Weekly Report:")
print(f" - Average AQI: {avg_aqi:.1f}")
print(f" - Max PM2.5: {max_pm25} μg/m³")
# 计算风速和PM2.5的相关系数
corr = df_final['wind_speed'].corr(df_final['pm25'])
print(f" - Correlation (Wind vs PM2.5): {corr:.2f}")
if corr < -0.3:
print(" 👉 结论: 风速与污染物呈显著负相关 (风来了,霾散了!)")
else:
print(" 👉 结论: 相关性不明显 (可能存在其他污染源或地形影响)")
else:
print("❌ Data Merge Failed.")
示例运行结果 (控制台):
text
🌍 Starting Environmental Monitor (Beijing)...
⬇️ Fetching Weather Data...
⬇️ Fetching Air Quality Data...
✅ Data Merged & Saved: 192 records.
✅ Chart saved: charts/aqi_trend.png
✅ Chart saved: charts/wind_vs_pm25.png
📊 Weekly Report:
- Average AQI: 85.4
- Max PM2.5: 120.5 μg/m³
- Correlation (Wind vs PM2.5): -0.45
👉 结论: 风速与污染物呈显著负相关 (风来了,霾散了!)
图表解读:
aqi_trend.png:你会看到一条波动的灰色 AQI 曲线。如果某几天温度骤降(冷锋过境)且伴随大风,通常 AQI 会迅速下降(变好)。wind_vs_pm25.png:你会看到一个向右下倾斜的回归线。这意味着风速越大,PM2.5 越低。这是环境科学中经典的物理扩散现象。
🔟 常见问题与排错(Debug 指南) 🐞
-
API 返回 400 Bad Request:
- 原因:经纬度写反了,或者参数拼写错误。
- 解法 :检查
params里的latitude和longitude。
-
Merge 后数据为空:
- 原因:两个 API 返回的时间格式不一致(有时是 UTC,有时是 Local Time)。
- 解法 :Open-Meteo 默认返回 ISO8601 格式,Pandas 能自动解析。如果不行,尝试在 API 参数里加
timezone=auto。
-
相关性为正?
- 原因:这在沙尘暴天气(Sandstorm)中很常见!大风反而把沙尘卷起来了,导致 PM10/PM2.5 飙升。数据不会骗人,这正是分析的乐趣所在。
1️⃣1️⃣ 进阶优化(加分项) ⚡
-
多城市对比:
- 同时抓取"北京"和"上海"的数据,画在同一张图上,对比谁的空气更好。
-
自动预警邮件:
- 写个逻辑:
if forecast_aqi > 150: send_email("明天重度污染,记得戴口罩!")。
- 写个逻辑:
-
长期数据库:
- 使用
SQLite存储每天的数据。累积一年后,你可以分析"季节性特征"(比如冬季是否比夏季更脏)。
- 使用
1️⃣2️⃣ 总结与延伸阅读 📚
今天我们不仅写了爬虫,还客串了一把环境科学家。
复盘: 我们利用 requests 并行获取了不同维度的环境数据,通过 pandas 实现了时间轴对齐,最后用数据证实了"风"对"霾"的驱散作用。
下一步:
可以尝试接入 OpenStreetMap,在地图上用不同颜色的圆点标记全国各地的空气质量,做一个真正的"全境污染监控大屏"!
🌟 文末
好啦~以上就是本期的全部内容啦!如果你在实践过程中遇到任何疑问,欢迎在评论区留言交流,我看到都会尽量回复~咱们下期见!
小伙伴们在批阅的过程中,如果觉得文章不错,欢迎点赞、收藏、关注哦~
三连就是对我写作道路上最好的鼓励与支持! ❤️🔥
✅ 专栏持续更新中|建议收藏 + 订阅
墙裂推荐订阅专栏 👉 《Python爬虫实战》,本专栏秉承着以"入门 → 进阶 → 工程化 → 项目落地"的路线持续更新,争取让每一期内容都做到:
✅ 讲得清楚(原理)|✅ 跑得起来(代码)|✅ 用得上(场景)|✅ 扛得住(工程化)
📣 想系统提升的小伙伴 :强烈建议先订阅专栏 《Python爬虫实战》,再按目录大纲顺序学习,效率十倍上升~

✅ 互动征集
想让我把【某站点/某反爬/某验证码/某分布式方案】等写成某期实战?
评论区留言告诉我你的需求,我会优先安排实现(更新)哒~
⭐️ 若喜欢我,就请关注我叭~(更新不迷路)
⭐️ 若对你有用,就请点赞支持一下叭~(给我一点点动力)
⭐️ 若有疑问,就请评论留言告诉我叭~(我会补坑 & 更新迭代)
✅ 免责声明
本文爬虫思路、相关技术和代码仅用于学习参考,对阅读本文后的进行爬虫行为的用户本作者不承担任何法律责任。
使用或者参考本项目即表示您已阅读并同意以下条款:
- 合法使用: 不得将本项目用于任何违法、违规或侵犯他人权益的行为,包括但不限于网络攻击、诈骗、绕过身份验证、未经授权的数据抓取等。
- 风险自负: 任何因使用本项目而产生的法律责任、技术风险或经济损失,由使用者自行承担,项目作者不承担任何形式的责任。
- 禁止滥用: 不得将本项目用于违法牟利、黑产活动或其他不当商业用途。
- 使用或者参考本项目即视为同意上述条款,即 "谁使用,谁负责" 。如不同意,请立即停止使用并删除本项目。!!!
