Python爬虫实战:环境监测实战 - 天气与空气质量的联合分析!

㊗️本期内容已收录至专栏《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 接口,将天气数据与空气质量数据进行时间轴对齐 ,清洗合并后,利用数据可视化技术,揭示天气变化对空气质量的深层影响。
读完这篇,你能获得:

  1. 多源数据融合:学会如何将两个不同 API 的数据(Weather + Air Quality)按时间戳合并(Merge)。
  2. 科学归因:通过散点图和相关系数,量化分析"风速"与"PM2.5"的关系。
  3. 自动化报表:生成一张包含双轴趋势图的专业环境分析报告。

1️⃣ 摘要(Abstract)

本文利用 requests 库并行请求 Open-Meteo 的气象与空气质量接口。我们获取指定城市(如北京)过去 7 天的小时级数据。利用 Pandasmerge 功能将异构数据整合成一张宽表。最后,使用 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 AirQualityPandas 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) 📊

我们要画两张图:

  1. AQI 趋势图:看看这一周空气好不好。
  2. 相关性分析图 :重点看 风速 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 里的 latitudelongitude
  • 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爬虫实战》,再按目录大纲顺序学习,效率十倍上升~

✅ 互动征集

想让我把【某站点/某反爬/某验证码/某分布式方案】等写成某期实战?

评论区留言告诉我你的需求,我会优先安排实现(更新)哒~


⭐️ 若喜欢我,就请关注我叭~(更新不迷路)

⭐️ 若对你有用,就请点赞支持一下叭~(给我一点点动力)

⭐️ 若有疑问,就请评论留言告诉我叭~(我会补坑 & 更新迭代)


✅ 免责声明

本文爬虫思路、相关技术和代码仅用于学习参考,对阅读本文后的进行爬虫行为的用户本作者不承担任何法律责任。

使用或者参考本项目即表示您已阅读并同意以下条款:

  • 合法使用: 不得将本项目用于任何违法、违规或侵犯他人权益的行为,包括但不限于网络攻击、诈骗、绕过身份验证、未经授权的数据抓取等。
  • 风险自负: 任何因使用本项目而产生的法律责任、技术风险或经济损失,由使用者自行承担,项目作者不承担任何形式的责任。
  • 禁止滥用: 不得将本项目用于违法牟利、黑产活动或其他不当商业用途。
  • 使用或者参考本项目即视为同意上述条款,即 "谁使用,谁负责" 。如不同意,请立即停止使用并删除本项目。!!!
相关推荐
喵手1 小时前
Python爬虫实战:鸣枪起跑!深度抓取全国马拉松赛事报名情报!
爬虫·python·爬虫实战·马拉松·零基础python爬虫教学·采集马拉松赛事报名数据·马拉松数据采集
小钻风33661 小时前
Java函数式编程-lambda表达式
java·开发语言·python
wefly20171 小时前
告别繁琐配置!m3u8live.cn让 M3U8 链接验证变得如此简单
开发语言·前端·python·django·flask·开发工具
伊珞_712 小时前
【雨云图】雨云图简介+简单数据python画图代码
开发语言·python
飞Link2 小时前
深度解析 NT-Xent:对比学习中的标准化温度交叉熵损失
python·算法·数据挖掘·回归
飞Link2 小时前
深度解析 InfoNCE:对比学习背后的“核心功臣”
python·学习·数据挖掘·回归
怪侠_岭南一只猿2 小时前
爬虫工程师学习路径 · 阶段四:反爬虫对抗(完整学习文档)
css·爬虫·python·学习·html
CodeLinghu2 小时前
我写了一个OpenClaw一健部署工具,引发了3w人围观
人工智能·python·语言模型·llm
搬砖者(视觉算法工程师)2 小时前
通俗易懂的 Transformer 入门文章(第一部分):功能概述
人工智能·python