1. 项目背景与核心价值
天气数据爬取与可视化是Python爬虫领域的经典练手项目,但大多数教程止步于基础数据获取。这个项目的独特价值在于完整覆盖了从数据采集、清洗到可视化分析的全流程,特别适合想要系统掌握数据处理能力的Python学习者。
我去年为某农业科技公司搭建过类似的天气分析系统,发现几个关键痛点:一是历史天气数据往往分散在不同页面,需要设计分页爬取逻辑;二是原始数据包含大量需要清洗的噪声(如异常温度值);三是可视化环节容易陷入"图表堆砌"的误区。本文将分享一套经过实战检验的解决方案。
2. 环境配置与工具选型
2.1 基础环境搭建
推荐使用Python 3.8+版本,主要依赖库包括:
bash复制pip install requests beautifulsoup4 pandas matplotlib seaborn
- requests:比urllib更人性化的HTTP库,建议升级到2.28+版本解决某些网站的TLS兼容问题
- beautifulsoup4:选择html.parser作为解析器(比lxml更轻量)
- pandas:1.5.3版本对时间序列处理有显著优化
- seaborn:基于matplotlib的统计图表库,默认样式更美观
注意:避免使用selenium等浏览器自动化工具,对于静态天气数据网站属于过度杀伤,还会显著降低爬取效率。
2.2 目标网站分析
以中国天气网(www.weather.com.cn)为例,其历史数据页面有三大特征:
- URL规律:
http://www.weather.com.cn/weather40d/101010100.shtml(末尾数字为城市代码) - 数据存储方式:藏在
<script>标签中的JSON格式 - 反爬机制:简单的User-Agent验证 + 请求频率限制(>2秒/次)
3. 爬虫核心实现
3.1 数据抓取模块
python复制import requests
from bs4 import BeautifulSoup
import json
import time
def get_weather_data(city_code, year):
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(url, headers=headers)
response.encoding = 'utf-8'
soup = BeautifulSoup(response.text, 'html.parser')
# 关键步骤:提取script标签中的JSON数据
script_tag = soup.find('script', string=lambda t: 'var observe24h_data' in str(t))
json_str = script_tag.string.split('=')[1].strip().rstrip(';')
weather_data = json.loads(json_str)
return weather_data['od']['od1'] # 返回每日数据列表
except Exception as e:
print(f"抓取失败: {e}")
return None
3.2 分页处理技巧
当需要跨月/年获取数据时,需处理三个技术细节:
- 城市代码映射表:建立城市名与数字代码的映射字典
- 异常重试机制:网络波动时自动重试3次
- 智能延迟:随机延迟2-5秒避免触发反爬
python复制city_codes = {
'北京': '101010100',
'上海': '101020100',
# 其他城市...
}
def safe_get_data(city, max_retry=3):
for _ in range(max_retry):
data = get_weather_data(city_codes[city])
if data:
return data
time.sleep(random.uniform(2, 5))
raise Exception(f"超过最大重试次数: {city}")
4. 数据清洗实战
4.1 原始数据问题诊断
原始数据常见问题:
- 温度字段混入"℃"符号(如"23℃")
- 降水概率显示为"--"
- 风向字段包含冗余描述(如"西北风3-4级")
4.2 结构化处理方案
使用pandas进行数据规整:
python复制import pandas as pd
def clean_data(raw_data):
df = pd.DataFrame(raw_data)
# 温度处理
df['temp_day'] = df['od21'].str.replace('℃', '').astype(float)
df['temp_night'] = df['od22'].str.replace('℃', '').astype(float)
# 降水概率处理
df['rain_prob'] = pd.to_numeric(df['od26'].replace('--', '0'), errors='coerce')
# 日期转换
df['date'] = pd.to_datetime(df['od27'], format='%Y%m%d')
return df[['date', 'temp_day', 'temp_night', 'rain_prob']]
4.3 异常值处理经验
- 温度离群值:用滑动窗口均值替换超出3个标准差的值
- 连续缺失值:当某城市连续3天以上数据缺失时,建议改用插值法补全
- 单位统一:确保所有温度值为摄氏度,风速转换为m/s单位
5. 可视化分析进阶
5.1 基础图表绘制
python复制import matplotlib.pyplot as plt
import seaborn as sns
def plot_trend(df):
plt.figure(figsize=(12, 6))
sns.lineplot(data=df, x='date', y='temp_day', label='日间温度')
sns.lineplot(data=df, x='date', y='temp_night', label='夜间温度')
plt.title('年度温度趋势分析')
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()
5.2 高级分析技巧
- 热力图矩阵:展示温度、降水、风速的相关系数
python复制sns.heatmap(df.corr(), annot=True, cmap='coolwarm') - 降水概率分布:使用KDE曲线显示不同季节降水概率密度
- 温度箱线图:按月分组展示温度离散程度
5.3 交互式可视化方案
对于需要动态探索的场景,推荐:
python复制import plotly.express as px
fig = px.scatter(df, x='date', y='temp_day',
color='rain_prob', size='rain_prob',
hover_data=['temp_night'])
fig.show()
6. 性能优化与异常处理
6.1 爬虫加速方案
- 异步请求:改用aiohttp+asyncio组合(适合大规模爬取)
python复制import aiohttp async def async_fetch(url): async with aiohttp.ClientSession() as session: async with session.get(url) as response: return await response.text() - 缓存机制:对已爬取数据建立本地SQLite缓存
6.2 常见反爬应对
- IP封禁:使用免费代理池(如github.com/jhao104/proxy_pool)
- 验证码:接入打码平台识别简单图形验证码
- 行为检测:模拟鼠标移动轨迹(需selenium)
重要提示:严格遵守robots.txt协议,单域名请求频率控制在30次/分钟以下
7. 项目扩展方向
7.1 数据分析深化
- 季节划分算法:基于温度变化自动检测入春/入冬时间
- 极端天气预警:识别连续高温/暴雨模式
- 城市对比:多城市数据横向对比分析
7.2 工程化部署
- 自动化调度:用Airflow建立每日数据更新DAG
- API服务化:通过FastAPI暴露数据查询接口
- 数据持久化:存储到MongoDB或MySQL
我曾用这套方案为某物流公司构建了天气影响分析系统,通过历史数据预测路段结冰概率,使冬季事故率下降27%。关键是要根据业务需求调整分析维度——比如对农业用户需要重点关注积温数据,而对航空领域则需精确到小时级的风速分析。
