1. 项目概述:爬取与分析全年天气数据的价值
爬取全年天气数据并进行可视化分析是一个典型的Python爬虫与数据分析结合项目。这个项目不仅能帮助初学者掌握requests、BeautifulSoup等爬虫基础库的使用,还能通过pandas进行数据清洗和分析,最后用matplotlib或seaborn完成可视化呈现。对于气象爱好者、数据分析师或需要天气数据支撑业务决策的从业者来说,这类项目具有很高的实用价值。
我曾在多个数据分析项目中处理过天气数据,发现完整年度的数据能揭示很多季节性规律和异常天气事件。比如通过分析某地全年温度变化,可以找出最适合户外活动的时间段;或者通过降水量分析,为农业种植提供参考。这些数据在气象站官网上通常都有记录,但需要爬虫技术才能高效获取。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术选型与工具准备
2.1 核心工具链选择
对于天气数据爬取与分析项目,我推荐以下Python工具组合:
-
爬虫部分:
requests:用于发送HTTP请求获取网页内容BeautifulSoup或lxml:解析HTML页面结构selenium(可选):应对动态加载的页面
-
数据处理:
pandas:数据清洗、转换和分析numpy:数值计算支持
-
可视化:
matplotlib:基础绘图库seaborn:基于matplotlib的高级可视化库plotly(可选):交互式可视化
提示:如果目标网站有反爬机制,可以考虑使用
fake-useragent库随机生成请求头,或者设置合理的请求间隔时间。
2.2 开发环境配置
建议使用Python 3.8+版本,并通过虚拟环境管理依赖:
bash复制python -m venv weather-env
source weather-env/bin/activate # Linux/Mac
weather-env\Scripts\activate # Windows
pip install requests beautifulsoup4 pandas matplotlib seaborn
对于IDE选择,VS Code或PyCharm都是不错的选择。我个人偏好VS Code,因为它轻量且对Jupyter Notebook支持良好,方便在开发过程中随时检查数据。
3. 目标网站分析与数据爬取
3.1 选择合适的天气数据源
国内常用的天气数据来源包括:
- 中国天气网(www.weather.com.cn)
- 中央气象台(www.nmc.cn)
- 世界天气信息服务网(worldweather.wmo.int)
以中国天气网为例,我们可以通过以下步骤获取城市全年历史天气数据:
- 进入历史天气页面(如石家庄:www.weather.com.cn/weather40d/101090101.shtml)
- 分析页面结构和数据加载方式
- 确定需要爬取的数据字段(日期、最高/最低温度、天气状况、风力等)
3.2 爬虫代码实现
下面是一个基础爬虫实现框架:
python复制import requests
from bs4 import BeautifulSoup
import pandas as pd
import time
from datetime import datetime
def get_weather_data(city_code, year):
base_url = f"http://www.weather.com.cn/weather40d/{city_code}.shtml"
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
}
try:
response = requests.get(base_url, headers=headers)
response.raise_for_status()
response.encoding = 'utf-8'
soup = BeautifulSoup(response.text, 'html.parser')
# 解析数据 - 这里需要根据实际页面结构调整
weather_data = []
days = soup.select('.tqtongji1 ul li')
for day in days:
date = day.select_one('.date').get_text()
temp = day.select_one('.temp').get_text()
weather = day.select_one('.weather').get_text()
wind = day.select_one('.wind').get_text()
weather_data.append({
'日期': date,
'温度': temp,
'天气': weather,
'风力': wind
})
return pd.DataFrame(weather_data)
except Exception as e:
print(f"获取数据失败: {e}")
return None
# 示例:爬取石家庄2023年天气数据
df = get_weather_data('101090101', 2023)
if df is not None:
df.to_csv('shijiazhuang_weather_2023.csv', index=False, encoding='utf-8-sig')
注意:实际爬取时需要根据目标网站的具体HTML结构调整CSS选择器。有些网站可能会定期更新页面结构,所以爬虫代码也需要相应调整。
3.3 反爬虫策略应对
在爬取天气数据时,可能会遇到以下反爬措施:
-
请求频率限制:解决方法是在请求间添加随机延迟
python复制import random time.sleep(random.uniform(1, 3)) # 随机等待1-3秒 -
User-Agent检测:使用
fake-useragent库轮换User-Agentpython复制from fake_useragent import UserAgent ua = UserAgent() headers = {'User-Agent': ua.random} -
IP封禁:可以考虑使用代理IP池,但对于天气数据爬取通常不需要
4. 数据清洗与预处理
4.1 常见数据问题处理
爬取到的原始数据通常需要经过以下处理:
-
日期格式标准化:
python复制df['日期'] = pd.to_datetime(df['日期'], format='%m月%d日').apply( lambda x: x.replace(year=2023)) # 假设是2023年数据 -
温度数据分离:
python复制df[['最高温度', '最低温度']] = df['温度'].str.extract('(-?\d+)℃/(-?\d+)℃') df['最高温度'] = df['最高温度'].astype(int) df['最低温度'] = df['最低温度'].astype(int) -
天气状况分类:
python复制weather_mapping = { '晴': '晴天', '多云': '多云', '阴': '阴天', '小雨': '雨天', '中雨': '雨天', # 其他天气状况映射... } df['天气类型'] = df['天气'].map(weather_mapping).fillna('其他')
4.2 数据质量检查
在分析前应该进行基本的数据质量检查:
python复制# 检查缺失值
print(df.isnull().sum())
# 检查异常值
print(df.describe())
# 检查日期连续性
date_range = pd.date_range(start=df['日期'].min(), end=df['日期'].max())
missing_dates = date_range.difference(df['日期'])
print(f"缺失的日期数量: {len(missing_dates)}")
5. 数据分析与可视化
5.1 基础统计分析
首先我们可以计算一些基本的统计指标:
python复制# 计算月平均温度
df['月份'] = df['日期'].dt.month
monthly_avg = df.groupby('月份').agg({
'最高温度': 'mean',
'最低温度': 'mean'
}).round(1)
# 天气类型分布
weather_dist = df['天气类型'].value_counts(normalize=True) * 100
5.2 温度变化趋势可视化
使用matplotlib绘制全年温度变化折线图:
python复制import matplotlib.pyplot as plt
import seaborn as sns
plt.figure(figsize=(12, 6))
sns.lineplot(data=df, x='日期', y='最高温度', label='最高温度')
sns.lineplot(data=df, x='日期', y='最低温度', label='最低温度')
plt.title('2023年石家庄市每日温度变化')
plt.xlabel('日期')
plt.ylabel('温度(℃)')
plt.grid(True)
plt.legend()
plt.tight_layout()
plt.savefig('temperature_trend.png', dpi=300)
plt.show()
5.3 天气类型分布可视化
使用seaborn绘制天气类型分布饼图:
python复制plt.figure(figsize=(8, 8))
weather_counts = df['天气类型'].value_counts()
plt.pie(weather_counts, labels=weather_counts.index, autopct='%1.1f%%',
startangle=90, colors=sns.color_palette('pastel'))
plt.title('2023年石家庄市天气类型分布')
plt.savefig('weather_distribution.png', dpi=300)
plt.show()
5.4 高级可视化:热力图
我们可以创建温度热力图来直观展示全年的温度变化:
python复制# 创建日期-月份的热力矩阵
heatmap_data = df.pivot_table(index='日期', values='最高温度', aggfunc='mean')
heatmap_data['day'] = heatmap_data.index.day
heatmap_data['month'] = heatmap_data.index.month
heatmap_matrix = heatmap_data.pivot('day', 'month', '最高温度')
plt.figure(figsize=(12, 8))
sns.heatmap(heatmap_matrix, cmap='coolwarm', annot=True, fmt='.0f',
linewidths=.5, cbar_kws={'label': '温度(℃)'})
plt.title('2023年石家庄市每日最高温度热力图')
plt.xlabel('月份')
plt.ylabel('日期')
plt.tight_layout()
plt.savefig('temperature_heatmap.png', dpi=300)
plt.show()
6. 深入分析与洞察挖掘
6.1 季节划分与特征分析
根据温度数据,我们可以科学划分季节并分析特征:
python复制# 定义季节划分函数
def get_season(month):
if month in [3, 4, 5]:
return '春季'
elif month in [6, 7, 8]:
return '夏季'
elif month in [9, 10, 11]:
return '秋季'
else:
return '冬季'
df['季节'] = df['月份'].apply(get_season)
# 季节温度分析
seasonal_stats = df.groupby('季节').agg({
'最高温度': ['mean', 'max', 'min'],
'最低温度': ['mean', 'max', 'min']
})
print(seasonal_stats)
6.2 极端天气事件分析
识别并分析极端天气事件:
python复制# 找出极端高温日
heat_wave = df[df['最高温度'] > df['最高温度'].quantile(0.95)]
print(f"极端高温日数量: {len(heat_wave)}")
print(heat_wave[['日期', '最高温度', '天气']])
# 找出极端低温日
cold_wave = df[df['最低温度'] < df['最低温度'].quantile(0.05)]
print(f"极端低温日数量: {len(cold_wave)}")
print(cold_wave[['日期', '最低温度', '天气']])
6.3 天气变化趋势分析
使用移动平均法分析温度变化趋势:
python复制df['7天最高温度移动平均'] = df['最高温度'].rolling(window=7).mean()
df['30天最高温度移动平均'] = df['最高温度'].rolling(window=30).mean()
plt.figure(figsize=(12, 6))
plt.plot(df['日期'], df['最高温度'], alpha=0.3, label='每日最高温度')
plt.plot(df['日期'], df['7天最高温度移动平均'], label='7天移动平均')
plt.plot(df['日期'], df['30天最高温度移动平均'], label='30天移动平均')
plt.title('2023年石家庄市最高温度变化趋势')
plt.xlabel('日期')
plt.ylabel('温度(℃)')
plt.legend()
plt.grid(True)
plt.tight_layout()
plt.savefig('temperature_trend_analysis.png', dpi=300)
plt.show()
7. 项目优化与扩展
7.1 爬虫优化建议
-
增量爬取:记录已爬取的日期,避免重复爬取
python复制import os if os.path.exists('shijiazhuang_weather_2023.csv'): existing_data = pd.read_csv('shijiazhuang_weather_2023.csv') existing_dates = set(pd.to_datetime(existing_data['日期'])) else: existing_dates = set() -
异常处理增强:添加重试机制
python复制from tenacity import retry, stop_after_attempt, wait_exponential @retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=4, max=10)) def safe_request(url, headers): response = requests.get(url, headers=headers) response.raise_for_status() return response
7.2 分析扩展方向
-
多城市对比分析:爬取多个城市数据,比较气候差异
python复制cities = { '北京': '101010100', '上海': '101020100', '广州': '101280101' } all_data = pd.concat([get_weather_data(code, 2023).assign(城市=name) for name, code in cities.items()]) -
气象指标相关性分析:研究温度、湿度、风力等指标间的关系
python复制sns.pairplot(df[['最高温度', '最低温度', '风力等级']]) plt.savefig('weather_correlation.png', dpi=300) -
预测模型构建:使用历史数据训练简单的温度预测模型
7.3 可视化增强技巧
-
交互式可视化:使用plotly创建可交互图表
python复制import plotly.express as px fig = px.line(df, x='日期', y=['最高温度', '最低温度'], title='2023年石家庄市温度变化') fig.show() -
动态仪表盘:使用Dash或Streamlit创建完整的数据仪表盘
-
地理空间可视化:结合城市位置数据在地图上展示天气信息
8. 常见问题与解决方案
8.1 爬虫相关问题
问题1:爬取的数据不全或格式不一致
解决方案:
- 检查CSS选择器是否准确匹配目标元素
- 添加更完善的异常处理逻辑
- 考虑使用XPath作为备用选择方案
问题2:网站返回403禁止访问
解决方案:
- 检查请求头是否完整(特别是User-Agent和Referer)
- 添加请求延迟
- 考虑使用会话(Session)保持cookies
8.2 数据处理问题
问题1:日期转换错误
解决方案:
python复制# 更健壮的日期解析函数
def parse_date(date_str, year):
try:
return pd.to_datetime(f"{year}-{date_str.replace('月','-').replace('日','')}")
except:
return pd.NaT
问题2:温度数据包含异常值
解决方案:
python复制# 温度范围合理性检查
df = df[(df['最高温度'] >= -30) & (df['最高温度'] <= 50)]
df = df[(df['最低温度'] >= -40) & (df['最低温度'] <= 40)]
8.3 可视化问题
问题1:图表标签重叠
解决方案:
python复制plt.xticks(rotation=45) # 旋转x轴标签
plt.tight_layout() # 自动调整布局
问题2:图表保存后分辨率低
解决方案:
python复制plt.savefig('output.png', dpi=300, bbox_inches='tight') # 提高DPI并调整边界
9. 完整项目结构建议
对于想要完整实现这个项目的开发者,我建议采用以下项目结构:
code复制weather-analysis/
├── data/ # 存放原始数据和清洗后的数据
│ ├── raw/ # 爬取的原始数据
│ └── processed/ # 清洗后的数据
├── notebooks/ # Jupyter笔记本用于探索性分析
├── scripts/
│ ├── crawler.py # 爬虫脚本
│ ├── cleaner.py # 数据清洗脚本
│ └── analyzer.py # 分析脚本
├── visualization/ # 生成的图表
├── config.py # 配置文件(如城市代码、请求头等)
├── requirements.txt # 依赖列表
└── README.md # 项目说明
这种结构便于维护和扩展,特别是当需要爬取多个城市或多年数据时。
