1. 项目概述与目标
最近在做一个有趣的数据分析项目:爬取泉州2022年全年的天气数据并进行可视化分析。这个项目不仅能帮助我们了解当地的气候特点,还能锻炼Python爬虫、数据处理和可视化的综合能力。
通过这个项目,我们将完成以下完整流程:
- 从天气历史数据网站抓取原始数据
- 清洗和整理成结构化格式
- 存储到CSV文件和SQLite数据库
- 生成词云展示天气类型分布
- 制作动态图表分析月度天气变化
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术选型与工具准备
2.1 主要技术栈
选择Python作为开发语言主要基于以下考虑:
- 丰富的第三方库支持网络请求和数据处理
- 简洁的语法适合快速开发
- 强大的可视化能力
核心工具包:
python复制# 网络请求与解析
import requests # 发送HTTP请求
from lxml import etree # HTML解析
# 数据处理
import pandas as pd # 数据分析
import csv # CSV文件操作
import sqlite3 # 轻量级数据库
# 可视化
from wordcloud import WordCloud # 词云生成
import matplotlib.pyplot as plt # 基础绘图
from pyecharts.charts import * # 交互式可视化
2.2 开发环境配置
建议使用Python 3.8+版本,主要库的安装命令:
bash复制pip install requests lxml pandas pyecharts wordcloud matplotlib
提示:如果安装pyecharts时遇到问题,可以尝试指定版本:
pip install pyecharts==1.9.1
3. 数据爬取实现
3.1 目标网站分析
数据源选择历史天气网站(https://lishi.tianqi.com),其优势在于:
- 提供完整的历史天气记录
- 页面结构清晰规整
- 无需登录即可访问
通过浏览器开发者工具分析页面结构,发现数据存储在<ul class="thrui">下的<li>标签中,每个<li>对应一天的天气数据。
3.2 爬虫核心代码实现
python复制def get_weather(url):
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
}
resp = requests.get(url, headers=headers)
resp_html = etree.HTML(resp.text)
weather_data = []
day_items = resp_html.xpath("//ul[@class='thrui']/li")
for item in day_items:
date = item.xpath("./div[1]/text()")[0].split(' ')[0]
high_temp = item.xpath("./div[2]/text()")[0].replace('℃', '')
low_temp = item.xpath("./div[3]/text()")[0].replace('℃', '')
weather = item.xpath("./div[4]/text()")[0]
weather_data.append({
'date': date,
'high': high_temp,
'low': low_temp,
'weather': weather
})
return weather_data
3.3 全量数据抓取策略
为获取全年数据,需要遍历12个月份的页面:
python复制base_url = 'https://lishi.tianqi.com/quanzhou/'
all_weather = []
for month in range(1, 13):
month_str = f'2022{month:02d}' # 格式化为202201,202202等
url = f'{base_url}{month_str}.html'
monthly_data = get_weather(url)
all_weather.extend(monthly_data)
注意事项:
- 添加适当的延时(如time.sleep(1))避免请求过于频繁
- 建议使用try-except处理可能的网络异常
- 可考虑将爬取的数据先保存为临时文件,防止中断后重新爬取
4. 数据存储方案
4.1 CSV文件存储
python复制with open('weather.csv', 'w', newline='', encoding='utf-8') as f:
writer = csv.DictWriter(f, fieldnames=['date', 'high', 'low', 'weather'])
writer.writeheader()
writer.writerows(all_weather)
4.2 SQLite数据库存储
python复制def init_db():
conn = sqlite3.connect('weather.db')
cursor = conn.cursor()
cursor.execute('''
CREATE TABLE IF NOT EXISTS weather (
date TEXT PRIMARY KEY,
high REAL,
low REAL,
weather TEXT
)
''')
conn.commit()
conn.close()
def save_to_db(data):
conn = sqlite3.connect('weather.db')
cursor = conn.cursor()
for record in data:
cursor.execute('''
INSERT OR REPLACE INTO weather VALUES (?, ?, ?, ?)
''', (record['date'], record['high'], record['low'], record['weather']))
conn.commit()
conn.close()
5. 数据分析与可视化
5.1 数据预处理
python复制df = pd.read_csv('weather.csv')
df['date'] = pd.to_datetime(df['date'])
df['month'] = df['date'].dt.month
weather_counts = df.groupby(['month', 'weather']).size().reset_index(name='counts')
5.2 词云生成
python复制text = ' '.join(df['weather'].tolist())
wordcloud = WordCloud(
font_path='msyh.ttc',
width=800,
height=400,
background_color='white'
).generate(text)
plt.figure(figsize=(12, 6))
plt.imshow(wordcloud, interpolation='bilinear')
plt.axis('off')
plt.show()
5.3 动态可视化图表
5.3.1 月度天气分布柱状图
python复制timeline = Timeline()
timeline.add_schema(play_interval=800)
for month in range(1, 13):
month_data = weather_counts[weather_counts['month'] == month]
month_data = month_data.sort_values('counts', ascending=True)
bar = (
Bar()
.add_xaxis(month_data['weather'].tolist())
.add_yaxis('天数', month_data['counts'].tolist())
.reversal_axis()
.set_global_opts(
title_opts=opts.TitleOpts(title=f'泉州2022年{month}月天气分布')
)
)
timeline.add(bar, f"{month}月")
timeline.render('monthly_weather_bar.html')
5.3.2 温度变化趋势图
python复制df['high'] = pd.to_numeric(df['high'])
df['low'] = pd.to_numeric(df['low'])
monthly_temp = df.groupby('month').agg({
'high': 'mean',
'low': 'mean'
}).reset_index()
line = (
Line()
.add_xaxis(monthly_temp['month'].tolist())
.add_yaxis('最高气温', monthly_temp['high'].round(1).tolist())
.add_yaxis('最低气温', monthly_temp['low'].round(1).tolist())
.set_global_opts(
title_opts=opts.TitleOpts(title='泉州2022年月均气温变化'),
yaxis_opts=opts.AxisOpts(name='温度(℃)'),
xaxis_opts=opts.AxisOpts(name='月份')
)
)
line.render('temperature_trend.html')
6. 项目优化与扩展
6.1 爬虫优化建议
- 增量爬取:记录已爬取的日期,避免重复爬取
- 代理设置:对于大规模爬取,建议使用代理IP池
- 异常处理:完善网络异常和解析失败的处理逻辑
python复制def safe_get(url, max_retry=3):
for _ in range(max_retry):
try:
resp = requests.get(url, headers=headers, timeout=10)
resp.raise_for_status()
return resp
except Exception as e:
print(f"请求失败: {e}")
time.sleep(2)
return None
6.2 数据分析扩展
- 天气趋势预测:使用时间序列分析预测未来天气
- 极端天气分析:识别高温/低温异常日期
- 天气与节假日关联:分析节假日期间的天气特点
python复制# 示例:找出温度异常日
df['temp_diff'] = df['high'] - df['low']
hot_days = df[df['high'] > 35] # 高温日
cold_days = df[df['low'] < 5] # 低温日
large_diff_days = df[df['temp_diff'] > 10] # 昼夜温差大
6.3 可视化增强
- 地理可视化:结合地图展示不同区域天气
- 交互式仪表盘:使用Dash或PyWebIO构建完整仪表盘
- 移动端适配:优化可视化结果在移动设备的显示
python复制# 示例:使用Pyecharts的地图组件
from pyecharts.charts import Map
province_weather = {
'福建': 20,
'广东': 25,
# 其他省份数据...
}
map_chart = (
Map()
.add("平均温度", list(province_weather.items()), "china")
.set_global_opts(
title_opts=opts.TitleOpts(title="南方省份平均温度"),
visualmap_opts=opts.VisualMapOpts(min_=15, max_=30)
)
)
map_chart.render('china_weather.html')
7. 经验总结与避坑指南
在实际开发过程中,我总结了以下几点经验:
-
反爬应对:
- 这个网站对频繁请求比较敏感,建议控制请求频率
- 使用真实浏览器的User-Agent更不容易被屏蔽
- 重要项目建议使用Scrapy框架,自带重试和限速机制
-
数据清洗要点:
- 原始数据中的温度带有"℃"符号,需要去除才能转为数值
- 日期格式需要统一处理,避免后续分析出错
- 注意处理缺失值,特别是极端天气可能缺少某些数据
-
可视化优化技巧:
- 中文显示问题:确保系统中存在中文字体
- 图表配色:使用更符合天气主题的色系
- 交互设计:添加tooltip提示框提升用户体验
-
性能优化:
- 大数据量时,Pandas操作比纯Python列表处理更高效
- 可视化渲染大量数据点时,考虑采样或聚合展示
- 将中间结果缓存到文件,避免重复计算
一个典型的踩坑案例:最初没有处理温度字段中的"℃"符号,导致后续数值计算全部出错。解决方法是在数据清洗阶段就统一处理:
python复制# 错误做法:直接转换为数值
df['high'] = pd.to_numeric(df['high']) # 会报错
# 正确做法:先去除单位
df['high'] = df['high'].str.replace('℃', '').astype(float)
这个项目完整展示了从数据获取到分析可视化的全流程,技术栈覆盖了Python爬虫、数据处理和可视化的核心技能。根据我的实践体验,Pyecharts的Timeline组件特别适合展示时间序列数据的变化,而Pandas的groupby功能则是数据分析的利器。
