python
复制代码
"""
烟台市历史天气数据爬取及可视化
功能:从天气网站爬取2011-2012年数据,生成Excel表格并绘制气温折线图
"""
# ==================== 库导入 ====================
import requests # 网络请求库
from bs4 import BeautifulSoup # HTML解析库
import pandas as pd # 数据处理库
import time # 时间模块(用于延迟)
import random # 随机数模块(用于随机延迟)
import matplotlib.pyplot as plt # 绘图库
import matplotlib.dates as mdates # 日期格式化模块
# ==================== 数据爬取函数 ====================
def save_a_month_data(url):
"""
获取单个月份的天气数据
:param url: 目标页面URL (格式: http://lishi.tianqi.com/yantai/YYYYMM.html)
:return: 二维列表,包含该月每天的天气数据
"""
a_month = []
headers = {
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36"
}
try:
# 发送带超时设置的GET请求(连接超时10秒,读取超时30秒)
r = requests.get(url, headers=headers, timeout=(10, 30))
r.raise_for_status() # 检查HTTP状态码,非200会抛出异常
except requests.exceptions.RequestException as e:
print(f"请求失败:{url},错误:{e}")
return a_month # 返回空列表
# 使用BeautifulSoup解析HTML
soup = BeautifulSoup(r.text, 'html.parser')
tianqi_zone = soup.find(class_='tian_three') # 定位天气数据区域
try:
tianqi_data = tianqi_zone.find(class_='thrui') # 定位数据列表
except AttributeError:
print(f"页面结构异常:{url}")
return a_month
# 提取每日数据(每个<li>标签代表一天)
tianqi_data_a_month = tianqi_data.find_all('li')
# 数据清洗处理
for tianqi_data_a_day in tianqi_data_a_month:
data = tianqi_data_a_day.text.split() # 按空白字符分割文本
# 处理异常格式:当某天有空气质量数据时字段数为7
if len(data) == 7:
data.pop(1) # 移除第二个元素(原始数据中的冗余字段)
a_month.append(data)
return a_month
# ==================== 主程序:数据爬取 ====================
all_data = [] # 存储所有月份的数据
# 注意:range的结束值不包含,2011-2012年应写range(2011, 2013)
for year in range(2011, 2013): # 修正后的年份范围
for month in range(1, 13):
month_str = f"{year}{month:02d}" # 格式化月份为两位数(如201101)
url = f"http://lishi.tianqi.com/yantai/{month_str}.html"
print(f"正在抓取:{url}")
# 调用爬取函数并合并数据
a_month_data = save_a_month_data(url)
all_data += a_month_data
# 随机延迟(1~3秒)防止触发反爬机制
time.sleep(random.uniform(1, 3))
# ==================== 数据保存 ====================
# 注意:如果桌面已存在weather_data.xlsx文件,需先关闭该文件
df = pd.DataFrame(all_data, columns=["日期", "最高温", "最低温", "天气", "风力风向", "空气质量"])
df.to_excel("C:/Users/lenovo/Desktop/weather_data.xlsx", index=False)
print("数据已保存至桌面")
# ==================== 数据可视化 ====================
# 重新读取数据(避免修改原始数据影响后续操作)
df = pd.read_excel("C:/Users/lenovo/Desktop/weather_data.xlsx")
# ---------- 数据预处理 ----------
# 1. 转换日期格式(将字符串转为datetime类型)
df['日期'] = pd.to_datetime(df['日期'])
# 2. 清洗温度数据(去除℃符号并转为浮点数)
df['最高温'] = df['最高温'].str.replace('℃', '').astype(float)
df['最低温'] = df['最低温'].str.replace('℃', '').astype(float)
# 筛选指定时间段数据
mask = (df['日期'] >= '2011-01-01') & (df['日期'] <= '2012-12-31')
df_filtered = df.loc[mask]
# ---------- 图表配置 ----------
# 设置中文字体(Windows系统使用SimHei)
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题
# 创建画布(单位:英寸,15x6英寸≈38x15厘米)
plt.figure(figsize=(15, 6))
# ---------- 绘制折线图 ----------
# 绘制最高温曲线(颜色代码:橙红色)
plt.plot(df_filtered['日期'], df_filtered['最高温'],
label='最高温', color='#FF4500', linewidth=1.2)
# 绘制最低温曲线(颜色代码:道奇蓝)
plt.plot(df_filtered['日期'], df_filtered['最低温'],
label='最低温', color='#1E90FF', linewidth=1.2)
# ---------- 图表美化 ----------
plt.title("烟台市气温变化 (2011-2012)", fontsize=14, pad=20) # pad标题间距
plt.xlabel("日期", fontsize=12, labelpad=10) # labelpad标签间距
plt.ylabel("温度 (℃)", fontsize=12, labelpad=10)
plt.grid(linestyle=':', color='gray', alpha=0.7) # 虚线网格
# 设置x轴日期格式
ax = plt.gca() # 获取当前坐标轴
ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m')) # 显示年月
ax.xaxis.set_major_locator(mdates.MonthLocator(interval=2)) # 每2个月一个主刻度
plt.xticks(rotation=45, ha='right') # 标签旋转45度,右端对齐
# 添加图例(无边框,右上角位置)
plt.legend(loc='upper right', frameon=False)
# 自动调整子图参数(避免标签被截断)
plt.tight_layout()
# ---------- 保存与显示 ----------
# 保存高清图片(dpi=300为印刷级清晰度)
plt.savefig("C:/Users/lenovo/Desktop/2011-2012_temperature.png",
dpi=300, bbox_inches='tight') # bbox_inches='tight'去除白边
plt.show() # 显示图表窗口