Python自动化批量下载ECWMF和GFS最新预报数据脚本

一、白嫖EC和GFS预报数据

EC的openData部分公开了一部分预报数据,作为普通用户只能访问这些免费预报数据,具体位置在这
在这里插入图片描述
在这里插入图片描述
可以发现,由于是Open Data,我们只能获得临近四天的预报结果,虽然时间较短,但是我们可以通过每天都执行脚本储存在本地,日积月累就可以存储完整的EC预报数据


同样的,NCEP的GFS数据也是只提供近几天的数据,因此也是需要一直爬取才能获取连续的预报数据

在这里插入图片描述
注意:该网站的gfs数据是包含41层数据,如果想要选择变量和高度层的话请访问这里
在这里插入图片描述

1.1 脚本核心思路

  • 本质上是使用Python的request库通过拼接好的url发送get请求,然后每天执行一次,就可以爬取最新的数据保存下来
  • 我们点进去下载目录中点击一个文件下载,按F12调试中可以从“网络”中抓包下载时候请求的url
    在这里插入图片描述
  • 详细看一下这个url,可以发现是由几个变量拼接起来的,包括:预报时长(forecast_hour), 起报时间(start_hour),基本url(base_url),分辨率(resolution)当然这几个名字是我自己取的,跟后面代码中的相对应
    在这里插入图片描述
  • 这样只需要我们在脚本中指定好这几个参数,之后按一定规律拼接url,并发送get请求,就可以相应下载

1.2 脚本编写中的面向对象思路(模板方法设计模式)

  • 在编写过程中发现两种数据下载的逻辑完全相同,只有使用的base_url和文件名拼接规则不同
  • 将核心功能和公共功能封装为基类
  • 两个继承了基类的具体的实现类只需要根据不同的数据下载需求将filename和url的拼接规则函数重写即可
  • 启示:在python中用子类不实现该方法就会NotImplementedError的方式来实现接口,将不同的逻辑用接口的形式让子类实现,实现多态
  • 后来想起来好像无意中运用了一个之前见过的设计模式,模板方法设计模式,基类规定好算法骨架,部分会随着实现类变化的部分的实现方式延迟到子类中实现

1.3 基类ForecastDownloader类

ForecastDownloader作为基类,其核心方法是start和download,是控制整个下载流程的函数,同时有两个抽象方法需要子类实现,这样做的目的是面对不同的下载需求,其实只有一小部分在变化,将拼接方法延迟到子类中实现就好了

import requests
import os
from datetime import datetime, timedelta
from urllib.parse import urljoin
import sys
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '../utils')))
from utils.logger import Loggerclass ForecastDownloader:"""下载天气预报数据的基类。这个类定义了天气预报数据下载器的基础功能,包括文件的下载和保存。子类应该实现 `construct_filename` 和 `construct_download_url` 方法来提供具体的文件名构造和下载 URL 构造。属性:forecast_hour (list): 预报小时列表,例如 [0, 6, 12, 18]。start_hour (str): 预报开始小时,例如 '00'。save_dir (str): 文件保存目录。base_url (str): 基础下载 URL。previous_days (int): 需要下载的前几天数据。log_savepath (str): 日志记录器路径。方法:start(): 开始下载数据,遍历指定的日期范围。download(time): 根据时间下载数据。construct_filename(time, hour): 构造文件名。子类需实现。construct_download_url(time, filename): 构造下载 URL。子类需实现。"""def __init__(self, forecast_hour, start_hour, save_dir, base_url, previous_days, log_savepath):self.save_dir = save_dirself.forecast_hour = forecast_hourself.start_hour = start_hourself.base_url = base_urlself.previous_days = previous_daysself.logger = Logger.setup_logger(log_savepath)if not os.path.exists(save_dir):os.makedirs(save_dir)def start(self):"""开始下载数据,遍历指定的日期范围。这个方法会从当前日期向过去的日期循环,并调用 `download` 方法来下载数据。"""today = datetime.now().date()for i in range(self.previous_days):date = today - timedelta(days=i)self.logger.info(str(date))time_str = f'{date.year}{date.month:02}{date.day:02}'self.download(time_str)def download(self, time):"""根据时间下载数据。这个方法会下载指定时间的数据,并保存到指定目录。参数:time (str): 数据的时间字符串,例如 '20240721'。"""for hour in self.forecast_hour:filename = self.construct_filename(time, hour)if not os.path.exists(os.path.join(self.save_dir, time, filename)):url = self.construct_download_url(time, filename)try:response = requests.get(url, verify=False)if response.status_code == 200:self.logger.info(f"Downloading {url}")day_dir = os.path.join(self.save_dir, time)if not os.path.exists(day_dir):os.makedirs(day_dir)save_path = os.path.join(self.save_dir, time, filename)with open(save_path, 'wb') as f:f.write(response.content)self.logger.info(f"Finished downloading {url}")else:self.logger.error(f"Failed to download {url}: Status code {response.status_code}")except Exception as e:self.logger.error(f"Error downloading {url}: {e}")else:self.logger.info('data exists')def construct_filename(self, time, hour):"""构造文件名。这个方法应该在子类中实现,用于构造特定格式的文件名。参数:time (str): 数据的时间字符串。hour (int): 预报小时。返回:str: 构造出的文件名。抛出:NotImplementedError: 子类必须实现此方法。"""raise NotImplementedError("This method should be overridden by subclasses")def construct_download_url(self, time, filename):"""构造下载 URL。这个方法应该在子类中实现,用于构造特定格式的下载 URL。参数:time (str): 数据的时间字符串。filename (str): 文件名。返回:str: 构造出的下载 URL。抛出:NotImplementedError: 子类必须实现此方法。"""raise NotImplementedError("This method should be overridden by subclasses")

1.4 子类EcForecastDownloader类

  • 继承自ForecastDownloader类,只需要把baserl改一下和重写两个拼接文件名和url的方法,其他逻辑与基类完全一致,这样就轻松实现了代码复用
class EcForecastDownloader(ForecastDownloader):def __init__(self, forecast_hour, start_hour, save_dir, previous_days, log_savepath='../logs/ecLog.log'):base_url = "https://data.ecmwf.int/forecasts/"super().__init__(forecast_hour, start_hour, save_dir, base_url, previous_days, log_savepath)def construct_filename(self, time, forecast_hour):return f'{time}{self.start_hour}0000-{forecast_hour}h-oper-fc.grib2'def construct_download_url(self, time, filename):return urljoin(self.base_url, f"{time}/{self.start_hour}z/ifs/0p25/oper/{filename}")

1.5 子类NecpForecastDownloader类

class NecpForecastDownloader(ForecastDownloader):def __init__(self, forecast_hour, start_hour, save_dir, previous_days, log_savepath='../logs/ncepLog.log'):base_url = "https://nomads.ncep.noaa.gov/pub/data/nccf/com/gfs/prod/"super().__init__(forecast_hour, start_hour, save_dir, base_url, previous_days, log_savepath)def construct_filename(self, time, forecast_hour):return f'gfs.t{self.start_hour}z.pgrb2.0p25.f0{forecast_hour}'def construct_download_url(self, time, filename):return f'{self.base_url}/gfs.{time}/{self.start_hour}/atmos/{filename}'

1.6 日志类

import logging
import osclass Logger:@staticmethoddef setup_logger(log_file):if not os.path.exists('../logs'):os.makedirs('../logs')logger = logging.getLogger()logger.setLevel(logging.INFO)# 文件处理器file_handler = logging.FileHandler(log_file)file_handler.setLevel(logging.INFO)file_formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')file_handler.setFormatter(file_formatter)logger.addHandler(file_handler)# 控制台处理器console_handler = logging.StreamHandler()console_handler.setLevel(logging.INFO)console_formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')console_handler.setFormatter(console_formatter)logger.addHandler(console_handler)return logger

1.7 控制部分

将下载类和具体的使用逻辑分离,只要指定好构造函数的几个参量,之后间隔一定的小时数之后一直执行下载代码,这样就可以实现不间断更新数据

from forecast_downloader import EcForecastDownloader, NecpForecastDownloader
import time
def download_ec(interval_hour):save_dir = '../download_res/ec'D = EcForecastDownloader([24, 48, 72], '00', save_dir, 4)while True:D.start()time.sleep(interval_hour * 60 * 60)def download_ncep(interval_hour):save_dir = '../download_res/ncep'D = NecpForecastDownloader([24, 48, 72], '00', save_dir, 4)while True:D.start()time.sleep(interval_hour * 60 * 60)if __name__ == '__main__':download_ec(24)

1.8 使用时的目录结构

  • 三个Downloader类都放在了forecasr_downloader.py中
  • 控制部分放在start_downloading.py中
  • 日志类放在utils下面
    在这里插入图片描述

1.9 挂服务器上后台运行

  • 传入服务器后,直接后台运行,只要线程不被杀,就可以一直将最新的数据自动下载
nohup python3 start_downloading.py &

二、小节

  • 上述脚本适用于一切需要定时执行拼接url来自动下载最新数据的数据爬取
  • 如果再来一个数据下载需求,只需要创建一个类,继承基类后重写拼接规则以及base url即可,代码的可拓展性和复用性较强
  • 一开始也是直接写,不断抽象为各个方法,类,类与类之间再抽象出基类,通过合理的抽象和设计模式的运用使代码优雅,复用性强WMF

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/48934.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vue3前端开发-小兔鲜项目-二级页面面包屑导航和跳转

vue3前端开发-小兔鲜项目-二级页面面包屑导航和跳转!这一次,做两件事。第一件事是把二级分类页面的跳转(也就是路由)设计一下。第二件事是把二级页面的面包屑导航设计一下。 第一件事,二级页面的跳转路由设计一下。 如…

Python爬虫(4) --爬取网页图片

文章目录 爬虫爬取图片指定url发送请求获取想要的数据数据解析定位想要内容的位置存放图片 完整代码实现总结 爬虫 Python 爬虫是一种自动化工具,用于从互联网上抓取网页数据并提取有用的信息。Python 因其简洁的语法和丰富的库支持(如 requests、Beaut…

科普文:后端性能优化的实战小结

一、背景与效果 ICBU的核心沟通场景有了10年的“积累”,核心场景的界面响应耗时被拉的越来越长,也让性能优化工作提上了日程,先说结论,经过这一波前后端齐心协力的优化努力,两个核心界面90分位的数据,FCP平…

Day05-readinessProbe探针,startupProbe探针,Pod生命周期,静态Pod,初始化容器,rc控制器的升级和回滚,rs控制器精讲

Day05-readinessProbe探针,startupProbe探针,Pod生命周期,静态Pod,初始化容器,rc控制器的升级和回滚,rs控制器精讲 0、昨日内容回顾1、readinessProbe可用性检查探针之exec案例2、可用性检查之httpGet案例3…

[数据集][目标检测]躺坐站识别检测数据集VOC+YOLO格式9488张3类别

数据集格式:Pascal VOC格式YOLO格式(不包含分割路径的txt文件,仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数):9488 标注数量(xml文件个数):9488 标注数量(txt文件个数):9488 标注…

C语言 | Leetcode C语言题解之第242题有效的字母异位词

题目&#xff1a; 题解&#xff1a; bool isAnagram(char* s, char* t) {int len_s strlen(s), len_t strlen(t);if (len_s ! len_t) {return false;}int table[26];memset(table, 0, sizeof(table));for (int i 0; i < len_s; i) {table[s[i] - a];}for (int i 0; i &…

EMQX 跨域集群:增强可扩展性,打破地域限制

跨域集群的概念 提到 EMQX&#xff0c;人们通常首先会想到它的可扩展性。尽管 EMQX 能随着硬件数量的增加几乎实现线性扩展&#xff0c;但在单个计算实例上的扩展能力终究有限&#xff1a;资源总会耗尽&#xff0c;升级成本也会急剧上升。这时&#xff0c;分布式部署就显得尤为…

JavaScript(11)——对象

对象 声明&#xff1a; let 对象名 { 属性名&#xff1a;属性值, 方法名&#xff1a;函数 } let 对象名 new Object() 对象的操作 先创建一个对象 let op {name:jvav,id:4,num:1001} 查 对象名.属性 console.log(op.name) 对象名[属性名] 改 对象名.属性 新值 op.name …

Pytorch学习笔记day4——训练mnist数据集和初步研读

该来的还是来了hhhhhhhhhh&#xff0c;基本上机器学习的初学者都躲不开这个例子。开源&#xff0c;数据质量高&#xff0c;数据尺寸整齐&#xff0c;问题简单&#xff0c;实在太适合初学者食用了。 今天把代码跑通&#xff0c;趁着周末好好的琢磨一下里面的各种细节。 代码实…

Spring MVC的高级功能——拦截器(三)拦截器的执行流程

一、单个拦截器的执行流程 如果在项目中只定义了一个拦截器&#xff0c;单个拦截器的执行流程如图所示。 二、单个拦截器的执行流程分析 从单个拦截器的执行流程图中可以看出&#xff0c;程序收到请求后&#xff0c;首先会执行拦截器中的preHandle()方法&#xff0c;如果preHa…

bug诞生记——动态库加载错乱导致程序执行异常

大纲 背景问题发生问题猜测和分析过程是不是编译了本工程中的其他代码是不是有缓存是不是编译了非本工程的文件是不是调用了其他可执行文件查看CMakefiles分析源码检查正在运行程序的动态库 解决方案 这个案例发生在我研究ROS 2的测试Demo时发生的。 整体现象是&#xff1a;修改…

聊一聊前端动画的种类,以及动画的触发方式有哪些?

引言 动画在前端开发中扮演着重要的角色。它不仅可以提升用户体验&#xff0c;还可以使界面更加生动和有趣。在这篇文章中&#xff0c;我们将深入探讨前端动画的各种实现方式&#xff0c;包括 CSS 动画、JavaScript 动画、SVG 动画等。我们还将讨论一些触发动画的方式和动画在…

【MQTT(2)】开发一个客户端,ubuntu版本

基本流程如下&#xff0c;先生成Mosquitto的库&#xff0c;然后qt调用库进行开发界面。 文章目录 0 生成库1 有界面的QT版本2 无界面版本 0 生成库 下载源码&#xff1a;https://github.com/eclipse/mosquitto.git 编译ubuntu 版本很简单&#xff0c;安装官方说明直接make&am…

rk3568 OpenHarmony4.1 Launcher定制开发—桌面壁纸替换

Launcher 作为系统人机交互的首要入口&#xff0c;提供应用图标的显示、点击启动、卸载应用&#xff0c;并提供桌面布局设置以及最近任务管理等功能。本文将介绍如何使用Deveco Studio进行单独launcher定制开发、然后编译并下载到开发板&#xff0c;以通过Launcher修改桌面背景…

记录|如何打包C#项目

参考文章&#xff1a; c#窗体应用程序怎么打包 经过检验确实有效 Step1. 生成发布文件 在Visual Studio的菜单中&#xff0c;找到“生成”->“发布” 第一次会有个向导&#xff0c;基本上一路next下来既可以 最后&#xff0c;点击完成即可以 Step2. 获得publish文件 自…

【JavaEE】AQS原理

本文将介绍AQS的简单原理。 首先有个整体认识&#xff0c;全称是 AbstractQueuedSynchronizer&#xff0c;是阻塞式锁和相关的同步器工具的框架。常用的ReentrantLock、Semaphore、CountDownLatch等都有实现它。 本文参考&#xff1a; 深入理解AbstractQueuedSynchronizer只需…

[C++]TinyWebServer

TinyWebServer 文章目录 TinyWebServer1 主体框架2 Buffer2.1 向Buffer写入数据2.2 从Buffer读取数据2.3 动态扩容2.4 从socket中读取数据2.5 具体实现 3 日志系统3.1 生产者-消费者模型3.2 数据一致3.3 代码 4 定时器4.1 调整堆中元素操作4.2 堆的操作4.2.1 增4.2.2 删4.2.3 改…

微信小程序-应用,页面和组件生命周期总结

情景1&#xff1a;小程序冷启动时候的顺序 情景2: 使用navigator&#xff08;保留并打开另一个页面&#xff09;和redirect&#xff08;关闭并打开另一个页面&#xff09;的执行顺序 情景3&#xff1a;切后台和切前台

Linux——组管理和权限管理

目录 组管理 Linux 组基本介绍 文件/目录所有者 组的创建 查看&修改文件/目录所在组 改变用户所在组 权限管理 基本介绍 rwx 文件/目录权限详解 chmod 修改文件或目录权限 chown 修改文件所有者 组管理 Linux 组基本介绍 关于第二张图中问题&#xff0c;答案…

【Qt】Qt的坐标转换(mapToGlobal)

1、QPoint QWidget::mapToGlobal(const QPoint &pos) const 将小部件坐标转换为全局坐标。mapToGlobal(QPoint(0,0))可以得到小部件左上角像素的全局坐标。2、QPoint QWidget::mapToParent(const QPoint &pos) const 将小部件坐标转换为父部件坐标。如果小部件没有父部…