Python爬虫实战:抓取天气数据并生成可视化图表(完整可复制代码)

日常出行、数据分析、项目开发中,天气数据是高频需求——但直接调用第三方天气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天预报页面(如北京:
http://www.weather.com.cn/weather/101010100.shtml
);爬虫抓取:用requests获取页面HTML,BeautifulSoup提取日期、天气类型、最高温、最低温、湿度等数据;数据清洗:将温度字符串(如“25℃”)转为数字、标准化日期格式、处理无数据场景;可视化生成:用matplotlib绘制3类图表,调整样式(颜色/字体/图例),保存为高清图片;扩展灵活:通过修改城市代码,支持抓取全国任意城市天气数据。

三、全流程实操:抓取天气数据+生成可视化图表

以“抓取北京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 切换目标城市

中国天气网的城市代码可通过以下方式查询:

打开中国天气网(
http://www.weather.com.cn/
);搜索目标城市(如“上海”),进入城市天气页面;复制URL中的数字部分(如上海URL:
http://www.weather.com.cn/weather/101020100.shtml
,城市代码为
101020100
);将
CITY_URL
中的城市代码替换为目标城市代码即可。

常见城市代码参考:

北京:101010100上海:101020100广州:101280101深圳:101280601杭州:101210101

3.3.2 调整图表样式

图表大小:修改
figsize=(15, 12)
(宽15英寸,高12英寸);图片清晰度:修改
dpi=300
(数值越大越清晰,文件也越大);颜色:替换
colors
列表中的色值(支持十六进制、RGB格式);图表标题:修改
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个关键技巧

中文字体适配:代码中已配置
plt.rcParams['font.sans-serif']
,Windows用
SimHei
(黑体),Mac用
Arial Unicode MS
,Linux用
WenQuanYi Zen Hei
,避免中文乱码;颜色搭配:选择互补色/邻近色(如红色+青色、蓝色+橙色),避免过于刺眼的颜色,可通过「在线色板工具」(如Coolors)挑选颜色;细节优化:添加网格(
grid=True
)增强可读性,给图表添加数值标签,旋转x轴日期避免重叠,调整坐标轴范围让数据更突出。

4.2 数据处理优化(避免图表异常)

缺失值处理:部分日期可能无最高温(如阴天),用
groupby
按天气类型填充平均值,避免折线图断裂;格式统一:用正则表达式提取温度/湿度的数字部分,避免“25℃”“30%”等字符串无法用于绘图;异常值过滤:可添加代码过滤极端值(如温度超过50℃或低于-40℃),避免图表比例失衡:


# 过滤极端温度
df = df[(df["最高温_数值"] <= 50) & (df["最高温_数值"] >= -40)]
df = df[(df["最低温_数值"] <= 50) & (df["最低温_数值"] >= -40)]

五、避坑指南(6个实测踩过的坑)

坑1:中文乱码

症状:图表中中文显示为方块/问号;解决:根据操作系统修改
plt.rcParams['font.sans-serif']
中的字体,确保系统已安装对应字体(如Windows默认有SimHei,Mac默认有Arial Unicode MS)。

坑2:数据抓取失败(403 Forbidden)

症状:终端提示“抓取天气数据失败:403 Client Error”;解决:① 确保
fake-useragent
库已安装(自动生成随机UA);② 增加请求延迟(在
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 库名
(如
pip install fake-useragent
),确保安装命令与Python环境一致(若有多个Python版本,用
pip3
替代
pip
)。

六、进阶扩展方向

抓取多城市数据对比:创建城市代码列表,循环抓取多个城市数据,生成“多城市温度对比图”;添加实时天气数据:扩展爬虫,提取实时温度、风力、空气质量(AQI),新增“实时天气状态卡片”图表;定时生成日报:用
schedule
库设置每日固定时间抓取数据并生成图表,自动发送到邮箱/钉钉;保存数据到Excel:添加
df.to_excel("天气数据.xlsx", index=False, encoding="utf-8-sig")
,将清洗后的数据存入Excel,便于后续分析;交互式可视化:用
plotly
替代matplotlib,生成可缩放、hover显示详情的交互式图表,支持网页展示。

总结

本文通过“爬虫抓取→数据清洗→可视化生成”的完整流程,实现了天气数据的免费获取和直观展示。核心优势在于代码极简可复制、图表美观实用、无API限制,无论是零基础用户日常使用,还是开发者集成到项目中,都能快速落地。

关键在于:爬虫部分通过简单的CSS选择器提取数据,无需复杂解析;数据清洗确保了可视化的准确性;图表部分通过matplotlib的子图布局和美化技巧,让最终效果专业且易用。

如果在实操中遇到“城市代码查询”“图表样式调整”“数据抓取失败”等问题,欢迎随时交流,可针对性给出解决方案!

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
谍不谍不谍谍不的头像 - 鹿快
评论 抢沙发

请登录后发表评论

    暂无评论内容