日常出行、数据分析、项目开发中,天气数据是高频需求——但直接调用第三方天气API往往需要密钥、有调用次数限制,手动查询又无法批量获取历史/预报数据。用Python爬虫抓取公开天气网站数据,再通过可视化图表直观呈现,既能免费获取无限制数据,又能按需定制展示效果。
本文将以「中国天气网」为例,全程实现“7天预报数据抓取→实时天气提取→数据清洗→多类型可视化图表生成”,技术栈极简(requests+BeautifulSoup+pandas+matplotlib),步骤详细到每一行代码,零基础也能30分钟完成,最后还会分享图表美化技巧和避坑指南。
一、核心亮点(没时间看全文可直接拿)
无限制数据:抓取公开天气网站数据,无需API密钥,支持批量获取多城市、多日数据;可视化全覆盖:生成温度趋势折线图、天气类型占比饼图、每日温湿度柱状图3类高频图表;代码极简可复制:核心代码仅80行,含详细注释,复制粘贴即可运行,无需复杂配置;数据清洗完整:处理字符串转数字、缺失值填充、日期标准化等问题,确保图表准确性;灵活扩展:支持切换城市、调整图表样式、保存高清图片,适配不同使用场景(报告/项目/日常)。
二、技术方案:为什么是“requests+BeautifulSoup+matplotlib”?
2.1 技术栈选型(针对性解决需求)
| 工具/库 | 核心作用 | 选型理由 |
|---|---|---|
| requests | 发送HTTP请求,获取天气网页内容 | 语法简单,上手快,支持Headers配置(反爬) |
| BeautifulSoup | 解析HTML页面,提取目标天气数据 | 无需懂复杂XPath,API直观,快速定位标签 |
| pandas | 数据清洗、整理(如日期/温度格式化) | 一键处理数据格式,与matplotlib无缝适配 |
| matplotlib | 生成可视化图表(折线图/饼图/柱状图) | Python主流可视化库,定制性强,支持保存高清图 |
| fake-useragent | 生成随机User-Agent,应对反爬 | 自动更新UA池,避免手动维护 |
2.2 核心逻辑
确定目标URL:选择中国天气网某城市的7天预报页面(如北京:);爬虫抓取:用requests获取页面HTML,BeautifulSoup提取日期、天气类型、最高温、最低温、湿度等数据;数据清洗:将温度字符串(如“25℃”)转为数字、标准化日期格式、处理无数据场景;可视化生成:用matplotlib绘制3类图表,调整样式(颜色/字体/图例),保存为高清图片;扩展灵活:通过修改城市代码,支持抓取全国任意城市天气数据。
http://www.weather.com.cn/weather/101010100.shtml
三、全流程实操:抓取天气数据+生成可视化图表
以“抓取北京7天预报数据”为例,从环境搭建到图表生成全程落地。
3.1 第一步:环境搭建(5分钟搞定)
推荐Python 3.9+,直接复制命令安装依赖,无需额外配置:
# 核心依赖:请求页面+解析数据+数据处理+可视化
pip install requests==2.31.0 beautifulsoup4==4.12.3 pandas==2.2.2 matplotlib==3.8.4 fake-useragent==1.5.1
3.2 第二步:核心代码(复制粘贴即可运行)
创建文件,复制以下代码,无需修改即可运行(默认抓取北京天气):
weather_crawler_visual.py
import requests
from bs4 import BeautifulSoup
import pandas as pd
import matplotlib.pyplot as plt
from fake_useragent import UserAgent
import re
# -------------------------- 1. 配置参数(可按需修改)--------------------------
# 城市天气URL(中国天气网,替换城市代码即可切换城市,城市代码查询见下文)
CITY_URL = "http://www.weather.com.cn/weather/101010100.shtml" # 北京(101010100)
# 图表保存路径(默认当前目录,支持绝对路径如"D:/weather_chart.png")
CHART_SAVE_PATH = "天气可视化图表"
# 设置中文字体(解决中文乱码问题,Windows/Linux/Mac适配)
plt.rcParams['font.sans-serif'] = ['SimHei', 'DejaVu Sans'] # Windows用SimHei,Mac用Arial Unicode MS
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示异常
# -------------------------- 2. 爬虫抓取天气数据 --------------------------
def crawl_weather_data(url):
"""抓取7天天气预报数据"""
# 反爬配置:随机User-Agent
ua = UserAgent()
headers = {
"User-Agent": ua.random,
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Referer": "http://www.weather.com.cn/"
}
try:
# 发送请求获取页面
response = requests.get(url, headers=headers, timeout=10)
response.raise_for_status() # 状态码非200则抛出异常
response.encoding = "utf-8" # 设置编码,避免中文乱码
# 解析HTML页面
soup = BeautifulSoup(response.text, "html.parser")
weather_list = []
# 定位7天天气数据的容器(通过类名匹配,无需复杂XPath)
forecast_items = soup.select(".t .clearfix") # 每个li对应一天的天气
for item in forecast_items[:7]: # 只取前7天数据
# 提取日期(如“周三(11-05)”)
date = item.select_one(".time").get_text(strip=True) if item.select_one(".time") else "未知日期"
# 提取天气类型(如“晴”“多云”)
weather_type = item.select_one(".wea").get_text(strip=True) if item.select_one(".wea") else "未知"
# 提取最高温(如“25℃”,部分日期可能无最高温,用None填充)
high_temp = item.select_one(".tem .high").get_text(strip=True) if item.select_one(".tem .high") else None
# 提取最低温(如“15℃”)
low_temp = item.select_one(".tem .low").get_text(strip=True) if item.select_one(".tem .low") else "0℃"
# 提取湿度(如“30%”)
humidity = item.select_one(".humidity").get_text(strip=True) if item.select_one(".humidity") else "50%"
# 存储单天数据
weather_list.append({
"日期": date,
"天气类型": weather_type,
"最高温": high_temp,
"最低温": low_temp,
"湿度": humidity
})
return weather_list
except Exception as e:
print(f"抓取天气数据失败:{str(e)}")
return None
# -------------------------- 3. 数据清洗(确保可视化准确性)--------------------------
def clean_weather_data(raw_data):
"""清洗原始数据:字符串转数字、处理缺失值"""
if not raw_data:
return None
# 转为DataFrame,方便数据处理
df = pd.DataFrame(raw_data)
# 1. 温度清洗:提取数字(如“25℃”→25),处理缺失值
def extract_temp(temp_str):
if not temp_str:
return None
# 用正则提取数字(支持负数,如“-5℃”→-5)
match = re.search(r"-?d+", temp_str)
return int(match.group()) if match else None
df["最高温_数值"] = df["最高温"].apply(extract_temp)
df["最低温_数值"] = df["最低温"].apply(extract_temp)
# 填充缺失的最高温(用同天气类型的平均最高温填充)
df["最高温_数值"] = df["最高温_数值"].fillna(df.groupby("天气类型")["最高温_数值"].transform("mean").round())
# 2. 湿度清洗:提取数字(如“30%”→30)
df["湿度_数值"] = df["湿度"].str.extract(r"(d+)").astype(int)
# 3. 日期简化(如“周三(11-05)”→“11-05(周三)”)
df["简化日期"] = df["日期"].str.replace(r"(w+)((d+-d+))", r"2(1)", regex=True)
return df
# -------------------------- 4. 生成可视化图表(3类核心图表)--------------------------
def generate_weather_charts(clean_df):
"""生成温度趋势图、天气类型占比图、温湿度对比图"""
if clean_df is None or clean_df.empty:
print("无有效数据,无法生成图表")
return
# 创建2行2列的子图布局(容纳3个图表,预留1个空白)
fig, ((ax1, ax2), (ax3, _)) = plt.subplots(2, 2, figsize=(15, 12), dpi=100)
fig.suptitle("7天天气数据可视化分析", fontsize=16, fontweight="bold", y=0.95)
# -------------------------- 图表1:7天温度趋势折线图 --------------------------
x = range(len(clean_df)) # x轴为日期索引
ax1.plot(x, clean_df["最高温_数值"], marker="o", linewidth=2.5, color="#ff6b6b", label="最高温")
ax1.plot(x, clean_df["最低温_数值"], marker="s", linewidth=2.5, color="#4ecdc4", label="最低温")
# 美化图表1
ax1.set_title("温度趋势", fontsize=14, fontweight="bold", pad=20)
ax1.set_xlabel("日期", fontsize=12)
ax1.set_ylabel("温度(℃)", fontsize=12)
ax1.set_xticks(x)
ax1.set_xticklabels(clean_df["简化日期"], rotation=45, ha="right") # 日期旋转45度,避免重叠
ax1.legend(fontsize=11)
ax1.grid(True, alpha=0.3) # 添加网格,增强可读性
ax1.set_ylim(min(clean_df["最低温_数值"]) - 5, max(clean_df["最高温_数值"]) + 5) # 调整y轴范围
# -------------------------- 图表2:天气类型占比饼图 --------------------------
weather_count = clean_df["天气类型"].value_counts() # 统计各天气类型出现次数
colors = ["#ff9f43", "#0984e3", "#6c5ce7", "#a29bfe", "#fd79a8"] # 自定义颜色
wedges, texts, autotexts = ax2.pie(
weather_count.values, labels=weather_count.index, autopct="%1.1f%%",
colors=colors[:len(weather_count)], startangle=90, textprops={"fontsize": 11}
)
# 美化饼图文字
for autotext in autotexts:
autotext.set_color("white")
autotext.set_fontweight("bold")
ax2.set_title("天气类型占比", fontsize=14, fontweight="bold", pad=20)
# -------------------------- 图表3:每日湿度柱状图 --------------------------
bars = ax3.bar(x, clean_df["湿度_数值"], color="#74b9ff", alpha=0.7, edgecolor="#0984e3", linewidth=1.5)
# 美化图表3
ax3.set_title("每日湿度", fontsize=14, fontweight="bold", pad=20)
ax3.set_xlabel("日期", fontsize=12)
ax3.set_ylabel("湿度(%)", fontsize=12)
ax3.set_xticks(x)
ax3.set_xticklabels(clean_df["简化日期"], rotation=45, ha="right")
ax3.set_ylim(0, 100) # 湿度范围固定0-100%
# 在柱状图上添加数值标签
for i, bar in enumerate(bars):
height = bar.get_height()
ax3.text(bar.get_x() + bar.get_width()/2., height + 1,
f"{int(height)}%", ha="center", va="bottom", fontsize=10, fontweight="bold")
# -------------------------- 调整布局+保存图表 --------------------------
plt.tight_layout() # 自动调整子图间距,避免重叠
plt.savefig(f"{CHART_SAVE_PATH}.png", dpi=300, bbox_inches="tight") # 保存高清图片(300dpi)
plt.show()
print(f"图表已保存到:{CHART_SAVE_PATH}.png")
# -------------------------- 5. 主函数(串联全流程)--------------------------
if __name__ == "__main__":
print("开始抓取天气数据...")
raw_weather_data = crawl_weather_data(CITY_URL)
if raw_weather_data:
print("抓取成功,开始清洗数据...")
clean_data = clean_weather_data(raw_weather_data)
print("数据清洗完成,预览前3条数据:")
print(clean_data[["简化日期", "天气类型", "最高温_数值", "最低温_数值", "湿度_数值"]].head(3))
print("开始生成可视化图表...")
generate_weather_charts(clean_data)
print("图表生成完成!")
else:
print("数据抓取失败,请检查URL或网络连接。")
3.3 关键配置说明(按需修改)
3.3.1 切换目标城市
中国天气网的城市代码可通过以下方式查询:
打开中国天气网();搜索目标城市(如“上海”),进入城市天气页面;复制URL中的数字部分(如上海URL:
http://www.weather.com.cn/,城市代码为
http://www.weather.com.cn/weather/101020100.shtml);将
101020100中的城市代码替换为目标城市代码即可。
CITY_URL
常见城市代码参考:
北京:101010100上海:101020100广州:101280101深圳:101280601杭州:101210101
3.3.2 调整图表样式
图表大小:修改(宽15英寸,高12英寸);图片清晰度:修改
figsize=(15, 12)(数值越大越清晰,文件也越大);颜色:替换
dpi=300列表中的色值(支持十六进制、RGB格式);图表标题:修改
colors中的文字,调整
fig.suptitle改变字体大小。
fontsize
3.3.3 运行步骤
保存代码文件();打开终端,进入代码所在目录;执行命令:
weather_crawler_visual.py;运行完成后,当前目录会生成
python weather_crawler_visual.py,同时终端会输出数据预览。
天气可视化图表.png
3.4 运行效果展示
3.4.1 终端数据预览
开始抓取天气数据...
抓取成功,开始清洗数据...
数据清洗完成,预览前3条数据:
简化日期 天气类型 最高温_数值 最低温_数值 湿度_数值
0 11-05(周三) 晴 25.0 15 30
1 11-06(周四) 多云 23.0 14 35
2 11-07(周五) 阴 20.0 13 40
开始生成可视化图表...
图表已保存到:天气可视化图表.png
图表生成完成!
3.4.2 可视化图表效果
上左:7天温度趋势折线图(清晰展示最高温/最低温变化);上右:天气类型占比饼图(直观呈现晴/多云/阴等天气占比);下左:每日湿度柱状图(带数值标签,明确每日湿度情况);整体风格简洁美观,支持直接用于报告、PPT等场景。
四、核心技巧:图表美化与数据处理优化
4.1 图表美化3个关键技巧
中文字体适配:代码中已配置,Windows用
plt.rcParams['font.sans-serif'](黑体),Mac用
SimHei,Linux用
Arial Unicode MS,避免中文乱码;颜色搭配:选择互补色/邻近色(如红色+青色、蓝色+橙色),避免过于刺眼的颜色,可通过「在线色板工具」(如Coolors)挑选颜色;细节优化:添加网格(
WenQuanYi Zen Hei)增强可读性,给图表添加数值标签,旋转x轴日期避免重叠,调整坐标轴范围让数据更突出。
grid=True
4.2 数据处理优化(避免图表异常)
缺失值处理:部分日期可能无最高温(如阴天),用按天气类型填充平均值,避免折线图断裂;格式统一:用正则表达式提取温度/湿度的数字部分,避免“25℃”“30%”等字符串无法用于绘图;异常值过滤:可添加代码过滤极端值(如温度超过50℃或低于-40℃),避免图表比例失衡:
groupby
# 过滤极端温度
df = df[(df["最高温_数值"] <= 50) & (df["最高温_数值"] >= -40)]
df = df[(df["最低温_数值"] <= 50) & (df["最低温_数值"] >= -40)]
五、避坑指南(6个实测踩过的坑)
坑1:中文乱码
症状:图表中中文显示为方块/问号;解决:根据操作系统修改中的字体,确保系统已安装对应字体(如Windows默认有SimHei,Mac默认有Arial Unicode MS)。
plt.rcParams['font.sans-serif']
坑2:数据抓取失败(403 Forbidden)
症状:终端提示“抓取天气数据失败:403 Client Error”;解决:① 确保库已安装(自动生成随机UA);② 增加请求延迟(在
fake-useragent后添加
requests.get);③ 避免频繁运行爬虫。
time.sleep(1)
坑3:图表保存后部分内容被截断
症状:图表标题/坐标轴标签被切掉;解决:保存时添加参数(代码中已包含),自动调整图片边距。
bbox_inches="tight"
坑4:温度趋势图断裂
症状:部分日期无最高温,导致折线图出现断点;解决:代码中已通过填充缺失值,若仍有断点,可手动设置默认值(如
fillna)。
df["最高温_数值"] = df["最高温_数值"].fillna(20)
坑5:湿度柱状图数值异常
症状:湿度显示为0或100以上;解决:检查正则表达式是否正确提取湿度数字,可修改,添加
df["湿度_数值"] = df["湿度"].str.extract(r"(d+)").fillna(50).astype(int)处理无湿度数据的情况。
fillna(50)
坑6:运行报错“ModuleNotFoundError”
症状:提示某库未找到(如);解决:重新执行
No module named 'fake_useragent'(如
pip install 库名),确保安装命令与Python环境一致(若有多个Python版本,用
pip install fake-useragent替代
pip3)。
pip
六、进阶扩展方向
抓取多城市数据对比:创建城市代码列表,循环抓取多个城市数据,生成“多城市温度对比图”;添加实时天气数据:扩展爬虫,提取实时温度、风力、空气质量(AQI),新增“实时天气状态卡片”图表;定时生成日报:用库设置每日固定时间抓取数据并生成图表,自动发送到邮箱/钉钉;保存数据到Excel:添加
schedule,将清洗后的数据存入Excel,便于后续分析;交互式可视化:用
df.to_excel("天气数据.xlsx", index=False, encoding="utf-8-sig")替代matplotlib,生成可缩放、hover显示详情的交互式图表,支持网页展示。
plotly
总结
本文通过“爬虫抓取→数据清洗→可视化生成”的完整流程,实现了天气数据的免费获取和直观展示。核心优势在于代码极简可复制、图表美观实用、无API限制,无论是零基础用户日常使用,还是开发者集成到项目中,都能快速落地。
关键在于:爬虫部分通过简单的CSS选择器提取数据,无需复杂解析;数据清洗确保了可视化的准确性;图表部分通过matplotlib的子图布局和美化技巧,让最终效果专业且易用。
如果在实操中遇到“城市代码查询”“图表样式调整”“数据抓取失败”等问题,欢迎随时交流,可针对性给出解决方案!
















暂无评论内容