当前位置:首页 > 技术 > 正文内容

基于Python与Vue3的多维度天气数据可视化平台

访客 技术 2026年5月22日 3

系统架构与核心目标

本项目构建了一个面向历史气象数据的分析与展示平台,采用前后端分离设计,融合爬虫采集、数据清洗、数据库建模、机器学习预测及前端动态可视化能力。系统支持多城市数据接入,实现从原始网页抓取到智能趋势推演的完整闭环。

关键技术选型

  • 后端框架:Flask 3.0 提供轻量级API服务,配合 SQLAlchemy 实现对象关系映射
  • 前端技术栈:Vue3 + Vite 构建响应式界面,使用 Element Plus 组件库增强交互体验
  • 数据可视化:ECharts 5 实现复杂图表渲染,支持时间序列、热力图与词云等展示形式
  • 数据处理:Pandas 和 NumPy 完成数据清洗与特征工程,提升分析准确性
  • 机器学习:Scikit-learn 提供线性回归与随机森林模型,用于未来气温走势预估

模块化目录结构

weather_analytics/
├── backend/                  # 后端服务
│   ├── app/
│   │   ├── api/              # REST接口分组
│   │   │   ├── auth.py       # 用户认证逻辑
│   │   │   ├── report.py     # 数据报表接口
│   │   │   ├── query.py      # 查询服务
│   │   │   ├── analysis.py   # 统计分析
│   │   │   ├── predict.py    # 预测接口
│   │   │   └── wordcloud.py  # 文本可视化
│   │   ├── models.py         # ORM实体定义
│   │   ├── cache.py          # Redis缓存配置
│   │   └── ml_engine.py      # 模型加载与推理
│   ├── config.py             # 环境配置文件
│   ├── requirements.txt      # Python依赖清单
│   └── run.py                # 应用启动脚本
│
├── frontend/                 # 前端应用
│   └── weather-dashboard/
│       ├── src/
│       │   ├── api/          # 请求封装
│       │   ├── views/        # 页面组件
│       │   │   ├── Overview.vue
│       │   │   ├── HistoryQuery.vue
│       │   │   ├── TempTrend.vue
│       │   │   ├── AirQuality.vue
│       │   │   ├── CityCompare.vue
│       │   │   ├── MapView.vue
│       │   │   ├── CloudText.vue
│       │   │   └── Forecast.vue
│       │   ├── router/       # 路由管理
│       │   ├── store/        # Pinia状态管理
│       │   └── utils/        # 工具函数
│       ├── package.json
│       └── vite.config.js
│
├── crawler/                  # 数据采集模块
│   ├── scripts/              # 爬虫主程序
│   ├── cleaners/             # 清洗逻辑
│   ├── loader.py             # 数据入库脚本
│   ├── updater.py            # 增量更新工具
│   └── city_codes.json       # 城市编码配置
│
└── docs/                     # 项目文档

数据仓库设计:星型模型

为支持多维分析,数据库采用星型结构设计,包含一个事实表和多个维度表:

  • 事实表:fact_weather 存储每日气象记录
  • 维度表:dim_date(时间)、dim_city(城市)、dim_weather_type(天气类型)

通过外键关联,实现高效的时间切片、区域对比与主题聚合查询。

数据采集流程

利用 Selenium 模拟浏览器行为,访问目标网站并提取结构化数据:

# crawler/scripts/weather_scraper.py
from selenium import webdriver
from bs4 import BeautifulSoup

def fetch_daily_data(city_code, target_year, target_month):
    url = f"https://weather-data.example.com/{city_code}/{target_year}-{target_month}"
    
    options = webdriver.ChromeOptions()
    options.add_argument("--headless")
    driver = webdriver.Chrome(options=options)
    driver.get(url)
    
    soup = BeautifulSoup(driver.page_source, 'html.parser')
    records = []
    
    for row in soup.select('table tbody tr'):
        cols = row.find_all('td')
        if len(cols) >= 6:
            records.append({
                'date': cols[0].get_text(strip=True),
                'high': cols[1].get_text(strip=True),
                'low': cols[2].get_text(strip=True),
                'condition': cols[3].get_text(strip=True),
                'wind_dir': cols[4].get_text(strip=True),
                'wind_level': cols[5].get_text(strip=True)
            })
    
    driver.quit()
    return records

数据清洗与转换

使用 Pandas 对原始数据进行标准化处理:

# crawler/cleaners/data_processor.py
import pandas as pd

class DataProcessor:
    def standardize(self, raw_df):
        df = raw_df.copy()
        
        # 移除空值
        df.dropna(subset=['date', 'high', 'low'], inplace=True)
        
        # 异常值过滤
        df = df[
            (df['high'].astype(float) >= -40) & 
            (df['high'].astype(float) <= 60) &
            (df['low'].astype(float) >= -40) & 
            (df['low'].astype(float) <= 60)
        ]
        
        # 类型转换与日期拆解
        df['date'] = pd.to_datetime(df['date'])
        df['high'] = pd.to_numeric(df['high'], errors='coerce')
        df['low'] = pd.to_numeric(df['low'], errors='coerce')
        df['year'] = df['date'].dt.year
        df['month'] = df['date'].dt.month
        df['day'] = df['date'].dt.day
        
        return df

后端数据接口实现

提供统一的 API 接口,支持按城市、年份筛选查询结果:

# backend/app/api/report.py
from flask import Blueprint, request, jsonify
from app.models import db, FactWeather, DimCity, DimDate

report_bp = Blueprint('report', __name__)

@report_bp.route('/trend', methods=['GET'])
def get_temperature_trend():
    city_id = request.args.get('city_id', type=int)
    year = request.args.get('year', type=int)

    query = db.session.query(
        DimDate.date,
        FactWeather.high,
        FactWeather.low,
        FactWeather.aqi
    ).join(DimDate).join(DimCity)

    if city_id:
        query = query.filter(FactWeather.city_id == city_id)
    if year:
        query = query.filter(DimDate.year == year)

    results = query.order_by(DimDate.date).all()

    return jsonify([
        {
            'date': r.date.strftime('%Y-%m-%d'),
            'high': r.high,
            'low': r.low,
            'aqi': r.aqi
        } for r in results
    ])

前端可视化实现

结合 Vue3 的响应式特性与 ECharts 的强大绘图能力,实现动态图表:


<template>
  <div ref="chartContainer" class="chart-wrapper"></div>
</template>

<script setup>
import { ref, onMounted } from 'vue'
import * as echarts from 'echarts'
import { fetchTrendData } from '../api/report'

const chartContainer = ref(null)
const dataSeries = ref([])

const renderChart = () => {
  const chart = echarts.init(chartContainer.value)
  
  const option = {
    title: { text: '温度变化趋势' },
    tooltip: { trigger: 'axis' },
    legend: { data: ['最高温', '最低温', '空气质量'] },
    xAxis: {
      type: 'category',
      data: dataSeries.value.map(d => d.date)
    },
    yAxis: [
      { type: 'value', name: '℃' },
      { type: 'value', name: 'AQI' }
    ],
    series: [
      {
        name: '最高温',
        type: 'line',
        data: dataSeries.value.map(d => d.high),
        smooth: true
      },
      {
        name: '最低温',
        type: 'line',
        data: dataSeries.value.map(d => d.low),
        smooth: true
      },
      {
        name: 'AQI',
        type: 'line',
        yAxisIndex: 1,
        data: dataSeries.value.map(d => d.aqi),
        smooth: true
      }
    ]
  }

  chart.setOption(option)
}

onMounted(async () => {
  dataSeries.value = await fetchTrendData()
  renderChart()
})
</script>

部署与运行

系统可独立部署于服务器或容器环境中:

# 后端启动
cd backend
pip install -r requirements.txt
python run.py

# 前端开发
cd frontend/weather-dashboard
npm install
npm run dev

# 数据初始化
cd crawler
python updater.py --mode full_import

功能展示

系统包含以下关键页面:

  • 首页概览:展示全国主要城市的实时气象摘要
  • 历史查询:支持按城市、时间段检索具体天气记录
  • 温度趋势图:显示长期气温波动曲线
  • 空气质量分析:结合污染指数进行环境评估
  • 城市对比面板:横向比较不同地区的气候特征
  • 词云图:基于天气描述生成关键词视觉呈现
  • 预测模块:基于历史数据输出未来7天天气预判

所有图表均支持缩放、平移与数据导出功能,满足科研与决策支持需求。

标签: PythonVue3

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。