简介:面向气象科研人员、数据爱好者及Python技能进阶者,这份资料系统演示了如何借助Python批量获取ECMWF欧洲中期天气预报中心数据,涵盖API密钥配置、请求参数构造、JSON响应解析、文件保存、异常处理及多线程下载等关键环节,适合定时抓取大量气象产品的应用场景。资源共含5个文件,压缩包约29.53MB,py脚本为可直接运行的下载程序,doc图文教程分步骤讲解环境准备到调试运行的完整流程,rar与exe分别提供ECMWF API客户端和Python 3.6安装包,txt文件可做参数记录与说明,搭配使用即可快速上手。已有5672人学习,关注度较高。通过这份材料,读者既能直接调用脚本完成批量获取,也能深入理解请求库、进度显示、存储管理等编码细节,进而按自身需求灵活扩展下载方案。
1. 项目背景与整体思路
1.1 为什么需要批量下载ECMWF数据
做气象、气候、水文或者环境方向研究的人,对ECMWF(欧洲中期天气预报中心)这套数据体系一定不陌生。ERA5再分析资料、ERA5-Land高分辨率陆面数据、季节预报数据,这些几乎是科研场景里的"硬通货"。但真到下载环节,很多人的第一反应还是打开网页,选好变量、时间、区域,然后点提交,等它生成链接再手动下载。
这个流程在数据量小的时候还凑合,一旦涉及长序列、多变量、多区域,比如要下载1990年到2020年逐小时的降水、风速、温度,手动操作就能把人搞崩溃。且不说网页端频繁切换很麻烦,ECMWF后台的请求队列有时候要排队几个小时,一个请求失败就得重来,根本没有效率可言。所以用Python脚本批量提交请求、统一管理下载任务,就成了处理这类数据需求的基本功。
我最早接触ECMWF数据下载是好几年前,当时要下整整十年的ERA5逐小时数据,十几个变量,分区下载,一开始手动操作到第三天就彻底放弃了。后来老老实实花了一个下午把Python下载脚本跑通,从那以后遇到新数据集需求,基本都是先写脚本再干活。这也是我把这个项目整理成一篇实操记录的原因——我踩过的坑,大家可以少踩一遍。
1.2 整体技术方案选型
ECMWF提供了官方的数据服务接口CDS(Climate Data Store),用户通过注册账号获取API密钥,然后借助Python的cdsapi库提交数据请求并下载结果。这套机制本质上是"请求-处理-下载"的异步任务模式:你提交一个数据请求,ECMWF服务器把它加入队列,处理完之后给你一个下载链接,你再通过脚本拉取数据。
既然要批量下载,方案的核心就是两件事:如何高效地循环提交多个请求,以及如何在请求失败、网络中断的情况下保证任务能完整跑完。我的技术选型就是这么定的:Python + cdsapi库负责请求提交和数据下载,脚本采用按时间分段循环的策略,并加入请求状态检测和自动重试逻辑。整个过程不需要额外的存储服务,也不需要数据库,一台普通电脑挂着就行,属于典型的小成本高收益方案。
另外说句实话,市面上也有人用wget、curl直接在链接后面拼参数下载,理论上可行,但遇到需要按天循环、按变量切换的场景,代码复杂度反而上去了。Python的优势在于生态成熟、cdsapi库官方维护、报错信息相对友好,对非专业开发人员来说是最容易上手的选择。
2. 环境准备与账号配置
2.1 Python环境安装
这一步是所有人的第一道门槛。现在ECMWF的CDS API要求Python 3.6以上版本,我建议直接装3.8以上的,毕竟新版本库的兼容性更好。如果你之前没装过Python,去python官网下载安装包,安装的时候记得勾选"Add Python to PATH",这一步很多新手会漏掉,漏掉之后在命令行里输入python会提示找不到命令,后面所有操作都进行不下去。
装好之后,在命令行里验证一下:
python --version能正常输出版本号,说明安装成功。如果提示没有这个命令,大概率就是PATH的问题,重新安装并勾选添加环境变量即可。这里再补充一句,macOS和Linux用户一般系统自带Python,但版本可能偏老,建议直接用pyenv或者系统包管理器装一个新版本。
2.2 注册ECMWF账号并获取API密钥
在开始写代码前,必须先去CDS官网注册账号并拿到专属的API密钥。这个密钥是连接你本地环境和ECMWF服务器的凭证,相当于一把钥匙。
具体流程是:在CDS网站注册登录后,进入个人主页,找到"API key"一栏,里面会显示你的UID和一串密钥。这两个字符串在后面的配置文件中会用到,一定要复制保存好。注意,这个密钥是和个人账号绑定的,不要泄露给他人,否则别人可以用你的账号额度去下载数据。
注册过程中有一个容易忽略的细节:很多数据集的访问需要单独同意使用协议。比如首次下载ERA5数据时,CDS会要求你在数据集页面勾选并同意许可协议,否则即使API密钥配置正确,提交请求后也会报错,提示你还没有授权访问这个数据集。所以拿到密钥之后,建议先到目标数据集页面点一下"Download data"按钮,走一遍网页端请求流程,确认自己有权访问,再回到脚本里干活。
2.3 cdspai库的安装与密钥配置文件
环境装好后,接下来安装Python数据下载的核心库cdsapi。这个库是ECMWF官方维护的Python客户端,封装了与CDS服务器交互的所有细节。安装非常简单:
pip install cdsapi如果你的环境里同时有Python 2和Python 3,可能需要用pip3来安装。
然后需要配置密钥文件。Windows系统下,在用户目录下创建一个名为.cdsapirc的文件,Linux和macOS同理,文件名也是.cdsapirc。文件内容格式如下:
url: https://cds.climate.copernicus.eu/api key: 你的UID:你的API密钥注意key的格式是UID和密钥之间用英文冒号分隔,中间没有空格。配置完成后,可以运行一段极简单的测试代码验证配置是否生效:
import cdsapi c = cdsapi.Client() print("配置成功")如果打印出"配置成功",说明客户端实例化正常,可以进入下一步了。如果这里报错,绝大多数情况是配置文件路径不对或者key格式写错了,优先检查这两处。
3. 核心代码实现与参数详解
3.1 单次数据请求脚本拆解
拿到一个ECMWF数据集,首先要学会看它的请求结构。以最常用的ERA5数据为例,一个标准的CDS请求由数据集名称、请求参数和目标文件名三部分组成。
下面是一段完整的单次下载脚本:
import cdsapi c = cdsapi.Client() c.retrieve( "reanalysis-era5-single-levels", { "product_type": "reanalysis", "variable": ["2m_temperature", "total_precipitation"], "year": "2020", "month": "06", "day": ["01", "02", "03"], "time": ["00:00", "06:00", "12:00", "18:00"], "data_format": "netcdf", "area": [50, 110, 20, 135], }, "era5_sample.nc" )参数含义逐一说清楚:product_type指定数据产品类型,ERA5的再分析数据填reanalysis;variable指定要下载的变量,这里我选了2米气温和总降水,变量名必须在CDS数据集页面提供的列表中找,拼错一个字母就会报错;year、month、day、time控制时间范围,注意这几个字段不一定接受列表,有些数据集只支持单个值,需要先查清楚;data_format是输出格式,ERA5支持netcdf和grib两种,我一般用netcdf,因为后续用xarray处理更方便;area定义下载区域,格式是北纬、西经、南纬、东经四个值,我这里设置的是中国中东部区域。
这里特别提醒一个绝大多数人都会踩的坑:ECMWF的经纬度顺序是[北, 西, 南, 东],不是常见的[左上, 右下],更不是[经度, 纬度]。我第一次用这套接口时,按照grib格式的习惯写反向区域,下载下来的数据范围完全不对,检查了大半天才意识到是顺序问题。
3.2 循环批量下载的完整实现
单次请求跑通之后,批量下载就只是把单次请求包装到循环里的问题。核心思路是按时间切片,逐期提交请求,每次请求的数据量控制在合理范围内,避免一次提交超大数据集导致服务端排队过长甚至请求失败。
以按年循环为例:
import cdsapi c = cdsapi.Client() years = ["1990", "1991", "1992", "1993", "1994"] months = ["01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12"] variables = ["2m_temperature", "total_precipitation"] for year in years: for month in months: print(f"正在提交 {year}-{month} 的请求...") c.retrieve( "reanalysis-era5-single-levels", { "product_type": "reanalysis", "variable": variables, "year": year, "month": month, "day": [f"{d:02d}" for d in range(1, 32)], "time": ["00:00", "06:00", "12:00", "18:00"], "data_format": "netcdf", "area": [50, 110, 20, 135], }, f"era5_{year}_{month}.nc" ) print(f"{year}-{month} 提交完成")运行这段脚本,它会逐月提交请求。这里有一个关键技巧:day参数用列表生成式[f"{d:02d}" for d in range(1, 32)],可以自动生成01到31的日期列表,不需要手写一大串数字。等脚本把请求全部提交完,服务器端会依次处理每个请求,生成的文件会按era5_1990_01.nc、era5_1990_02.nc的格式保存到本地。
实际的下载逻辑也在这个循环里,c.retrieve会等待服务端处理完当前请求并完成下载,才会进入下一次循环。所以脚本只要不中断,就会一直运行到所有文件下载完成。不过这个过程中有几个需要特别注意的点,我在下一节统一讲。
3.3 请求参数中的高频问题和避坑心得
先说变量名的问题。很多新手喜欢按照文献或者别人的代码里写的变量名直接用,然后发现报错。ECMWF不同数据集允许请求的变量名是有差异的,比如2m_temperature在ERA5里是正确的,但换成ERA5-Land数据集后,变量名可能变成了2m_temperature加上特定的product_type组合。最稳妥的办法是到CDS数据集页面,点开"Download data"标签页,那里有图形化的变量选择界面,下拉框里的选项就是脚本里能用的合法变量名。
再说时间字段。有些数据集对time字段只接受固定的几个时次,比如ERA5是00:00到23:00每小时一个,但某些衍生产品只有3小时间隔或者6小时间隔,填了不存在的时次会报"Bad request for url"之类的错误。处理方式是先做一次小范围测试请求,比如只下一天的某个时次,确认无误后再铺开跑全量。
最后说目标文件名。c.retrieve的第三个参数是本地的保存文件名,完全可以按需求自由命名,但建议遵循"数据集_时间_区域"的结构化命名方式,方便后续管理。如果下载中断了,或者想补某个时间段的数据,清晰的命名能让你少很多麻烦。
4. 实操过程中的常见问题与排查技巧
4.1 请求长时间排队卡住不动
这是使用ECMWF数据下载时遇到最多的状况。明明提交了一个请求,服务器也不报错,但就是一直转圈。原因在于CDS采用异步处理队列,当某个时间段请求量很大时,你的任务可能排在几千个任务之后,等待时间难以估计。
我的处理办法是:不在循环里干等,而是把大批量任务拆成多个小任务,分批次提交。比如连续提交10个请求,每个请求只包含一个月的6个变量,不要一次性提交10年所有变量。这样做的好处是单个任务处理时间短、失败率低,即使中途断了,重跑的代价也可控。
另一个实用技巧是给脚本加上运行时长限制。对于单个请求,如果超过一定时间(比如30分钟)还没下载完成,就跳过进入下一个请求,最后统一补漏。这个可以通过在请求前记录时间戳、请求后检查文件是否生成来实现,代码逻辑很简单,但能显著提升大批量任务的可靠性。
4.2 服务器返回HTTP错误代码的处理
跑批量下载时经常会遇到HTTP 400、HTTP 429等错误。它们的意思完全不同:
- HTTP 400:请求参数有误,通常是变量名、时间格式、区域范围不合法。排查方法很简单,把请求参数和CDS网页端的图形化选择界面做比对,重点检查变量名和区域顺序。
- HTTP 429:请求频率过高,触发了限流策略。这时候最忌讳的是脚本继续快速重试,反而会加重限流。正确做法是等待一段时间(比如60秒)再继续。
- HTTP 500:服务端暂时性错误,属于ECMWF那边的问题,过几分钟重试通常能解决。
针对这些情况,我一般会在代码里加上异常捕获和退避重试逻辑:
import time import cdsapi c = cdsapi.Client() def download_with_retry(c, params, filename, max_retries=5): for attempt in range(max_retries): try: c.retrieve(params["dataset"], params["request"], filename) return True except Exception as e: wait_time = 2 ** attempt * 30 print(f"请求失败: {e}, {wait_time}秒后重试...") time.sleep(wait_time) return False这里用指数退避策略,第一次失败等30秒,第二次等60秒,依次翻倍,避免频繁请求把服务器惹毛。
4.3 磁盘空间不足与增量下载策略
ERA5逐小时、多变量的nc文件体积不小,动辄几百MB,如果下载整个中国区域几十年的数据,总存储需求会非常夸张。很多人的电脑硬盘根本装不下。这也是为什么我强烈建议在批量下载前先估算数据量,而不是无脑全量下载。
我个人的习惯是先用网页端下一个最小测试文件,比如某一天的一个变量,查看文件大小,然后乘以天数、乘以变量数,估算总需求。如果超出了可用磁盘空间,就要调整策略:要么减少区域范围,要么只下载需要的变量,要么压缩时间频率。
在这个基础上,还可以做增量下载。比如脚本已经下载了1990年到2020年的数据,新需求只需要2020年以后的数据,那就调整年份列表,只提交2020年之后的请求。这个场景非常常见,所以我在脚本里专门设计了可以灵活修改的年份、月份、变量列表参数,换需求时只改列表内容就行,不会影响其他逻辑。
4.4 文件名与断点续传的安全检查
还有一个容易被忽略但极其重要的问题:同一个文件名如果已存在,retrieve会怎么处理?不同版本行为可能不一样,有的版本会直接覆盖,有的版本会报错。为了稳妥起见,我会在下载前检查目标文件是否已存在且大小不为0,如果条件满足就跳过当前请求。
import os filename = f"era5_{year}_{month}.nc" if os.path.exists(filename) and os.path.getsize(filename) > 0: print(f"{filename} 已存在,跳过") continue这个小技巧特别适合大批量任务中断后的补跑。跑了一上午任务,中途由于网络原因断了,不用全盘重来,只需要重新运行脚本,它会把已经下载好的文件自动跳过去,只补缺失的文件。
5. 完整工程化脚本示例与扩展思路
5.1 整合后的批量下载脚本
把前面各节的关键逻辑整合成一份完整的工程化脚本,结构包括参数配置区、循环主体、异常处理、已完成文件跳过。代码结构如下:
import os import time import cdsapi # 配置区 DATASET = "reanalysis-era5-single-levels" VARIABLES = ["2m_temperature", "total_precipitation"] YEARS = ["1990", "1991", "1992"] MONTHS = [f"{m:02d}" for m in range(1, 13)] DAYS = [f"{d:02d}" for d in range(1, 32)] TIMES = ["00:00", "06:00", "12:00", "18:00"] AREA = [50, 110, 20, 135] OUTPUT_FORMAT = "netcdf" c = cdsapi.Client() def already_downloaded(filename): return os.path.exists(filename) and os.path.getsize(filename) > 0 def download_file(year, month): filename = f"era5_{year}_{month}.nc" if already_downloaded(filename): print(f"{filename} 已存在,跳过") return request = { "product_type": "reanalysis", "variable": VARIABLES, "year": year, "month": month, "day": DAYS, "time": TIMES, "data_format": OUTPUT_FORMAT, "area": AREA, } for attempt in range(5): try: print(f"提交 {year}-{month} 请求...") c.retrieve(DATASET, request, filename) print(f"{filename} 下载完成") return except Exception as e: wait = 30 * (2 ** attempt) print(f"请求异常: {e}, {wait}秒后重试") time.sleep(wait) if __name__ == "__main__": for year in YEARS: for month in MONTHS: download_file(year, month) print("全部任务处理完毕")这段脚本可以原样复制保存为era5_download.py,然后直接运行:
python era5_download.py运行时留意控制台输出,如果看到某个年份月份提示"已存在,跳过",说明断点续传在正常工作。整个脚本不需要额外依赖,逻辑也不复杂,适合直接作为项目模板复用。
5.2 多变量多区域的批量组合策略
实际项目中,往往需要下载的不止一个区域、一组变量。比如气候分析中常见的是把中国分几个大区分别处理,或者需要同时下载多个高度的风场数据。这时候我的做法是再套一层区域和变量组的循环:外层遍历区域配置,内层遍历变量组合,文件名里把区域标识也带进去。
这里需要注意的是,ECMWF服务器对单位时间内提交的请求数量有限制,如果循环提交太快,很容易触发限流。所以多组合循环时要控制节奏,每提交8到10个请求后主动停顿几秒,具体间隔可以在实际运行中调整,以不触发429错误为准。
5.3 从nc文件到分析结果的后续处理建议
数据下载完成不代表工作结束。拿到nc文件后,通常用xarray配合pandas进行后续处理。比如提取某个网格点的时序数据、计算区域平均值、把逐小时数据重采样成逐日数据,这些操作用xarray都是一行代码的事。
import xarray as xr ds = xr.open_dataset("era5_1990_01.nc") temp = ds["t2m"] - 273.15 daily_mean = temp.resample(time="1D").mean()这里做了一次开尔文到摄氏度的转换,然后重采样成日均温。xarray的resample接口和pandas几乎一样,熟悉pandas的人上手很快。这种"Python批量下载 + xarray分析"的组合,基本覆盖了从数据获取到初步分析的全过程,也是我目前在项目里最常使用的工作流。
写在最后
跑ECMWF批量下载这个项目给我最大的感受就是:官方文档写得再清楚,也不如自己动手跑一遍踩坑学得快。密码学配置、变量名合法性、区域顺序、队列等待、断点续传,每一个环节都有看似不起眼但实际操作时会卡住的地方。希望这篇记录能帮你减少一些不必要的试错时间。
最后再分享一个小技巧:第一次跑大批量任务前,一定要先用一个小范围请求做走通测试,比如只下一天的2个变量,确认文件名、数据内容和预期一致后,再放开了跑全量。这一步看着多花了十分钟,实际上可能帮你省下的是好几个小时的无效等待和排查时间。
本文还有配套的精品资源,点击获取