ERA5再分析资料下载
本教程使用Docker容器化Python环境与Python脚本进行下载,同时使用了Cron 服务进行定时下载,脚本及镜像Dockerfile都已提供,只需要简单的步骤和环境即可实现定时从Copernicus Climate Data官网下载再分析资料数据。(你也可以直接在服务器上安装python3环境及cds客户端执行脚本,这里使用Docker Python容器环境执行是一样的,使用Docker主要好处就是不影响宿主机环境)
服务器环境要求
- 服务器需要已安装Docker
- 服务器需要已安装 Cron 服务(大部分CentOS/RHEL 系统都会自带)
- 需要自己注册CDS账号并获取对应的url和key,同时记得在官网同意他的下载协议!否则脚本下载时会报错提示的,即时遇到报错提示了也可以访问报错提示中的地址去同意,问题不大。 (地址:https://cds.climate.copernicus.eu/how-to-api)
构建镜像
- Dockerfile
# Python 语言容器镜像
FROM python:3.10-slim-buster
# 设置工作目录为 /workspace
WORKDIR /workspace
# 创建 data 文件夹用于存储数据
RUN mkdir -p /workspace/data \
&& chmod -R 777 /workspace/data
# 更新 pip
RUN python -m pip install --upgrade pip
# 安装 cdsapi 客户端
RUN pip install cdsapi
# 创建日志文件目录
RUN mkdir -p /workspace/logs \
&& chmod -R 777 /workspace/logs # 确保容器有写入权限
# 容器启动时不启动 cron,而是通过宿主机的 cron 来执行
CMD ["tail", "-f", "/dev/null"]
- Copernicus Climate 官网建议使用Python 3 以上版本
- 因为Docker 镜像官网国内已经用不了了或者各种网络导致的问题,这里也为你贴心给你已经打包好的镜像了。( 百度网盘地址:网盘链接)
#加载 Docker 镜像
docker load -i era5-python-image.tar
配置文件
- 新建编辑CDS客户端配置文件,注意your_api_key_here是自己账号的api key
- 这里直接在宿主机上编辑CDS所需的配置文件,然后映射到容器中,方便直接修改和配置
mkdir -p /workspace
echo "url : https://cds.climate.copernicus.eu/api" > /workspace/.cdsapirc
echo "key : your_api_key_here" >> /workspace/.cdsapirc
启动python容器
- 将配置文件、脚本文件及下载目录映射至容器
docker run -d --name era5-python-container -v /home/python/.cdsapirc:/root/.cdsapirc -v /home/python/workspace:/workspace era5-python-image
配置cron定时执行脚本文件
- 这里没有直接使用容器执行脚本,选择容器bash方式执行脚本,方便后台执行时记录控制台输出日志到download_log
# 打开宿主机的 cron 配置文件进行编辑配置
crontab -e
# 添加一行 执行再分析资料下载定时任务 每天凌晨三点执行一次
0 3 * * * docker exec -d era5-python-container bash -c "python /workspace/era5.py >> /workspace/logs/download_log.log 2>&1"
# 查看 Cron 执行日志
tail -f /var/log/syslog # Debian/Ubuntu 系统
tail -f /var/log/cron # CentOS/RHEL 系统
# 启动 cron 服务
sudo service cron start # 对于 Ubuntu/Debian
sudo systemctl start crond # 对于 CentOS/RHEL
# cron 服务开机自动启动
sudo systemctl enable cron # 对于 Ubuntu/Debian
sudo systemctl enable crond # 对于 CentOS/RHEL
- 添加定时任务之前可以手动测试执行一次脚本
docker exec -it era5-python-container python /workspace/era5.py >> /workspace/logs/era5.log 2>&1
Python下载脚本代码
- 脚本很简单,就不放文件了
- 脚本下载的数据可以自己根据自己的需求调整层级、变量、时间等等
- 注意 Copernicus Climate官网提供的数据最新时间是七天前
import cdsapi
import datetime
import pathlib
# 创建 cdsapi 客户端
c = cdsapi.Client()
def get_latest_date_from_var_directory(var_directory: pathlib.Path):
"""
获取指定变量目录中最新 .nc 文件的日期,并返回日期。
"""
latest_date = None
# 获取当前变量目录下的所有 .nc 文件
nc_files = list(var_directory.glob('*.nc'))
if not nc_files:
return None # 如果当前目录下没有 .nc 文件,返回 None
# 获取文件的日期部分,文件名格式假设为 'era5.variable.YYYYMMDD.nc'
for file in nc_files:
try:
date_str = file.stem.split('.')[-1] # 获取文件名中的日期部分
file_date = datetime.datetime.strptime(date_str, '%Y%m%d')
if latest_date is None or file_date > latest_date:
latest_date = file_date
except ValueError:
continue
return latest_date # 返回最新的日期
def download_era5_data(variable, date, savepath):
"""
下载 ERA5 数据并保存为 .nc 文件
"""
try:
c.retrieve(
'reanalysis-era5-pressure-levels',
{
'product_type': 'reanalysis',
'grid': '0.25/0.25',
'area': [70, 40, 0, 140], # latitudes and longitudes
'format': 'netcdf',
'variable': variable,
'pressure_level': ['1', '50', '100', '125', '150', '175', '200', '225', '250', '300', '350', '400',
'450', '500', '550', '600', '650', '700', '750', '775', '800', '825', '850', '875',
'900', '925', '950', '975', '1000'],
'year': date.strftime('%Y'),
'month': date.strftime('%m'),
'day': date.strftime('%d'),
'time': ['00:00', '01:00', '02:00', '03:00', '04:00', '05:00', '06:00', '07:00', '08:00', '09:00',
'10:00', '11:00', '12:00', '13:00', '14:00', '15:00', '16:00', '17:00', '18:00', '19:00',
'20:00', '21:00', '22:00', '23:00']
},
savepath
)
print(f"Downloaded {variable} for {date.strftime('%Y-%m-%d')}")
except Exception as e:
print(f"Error downloading {variable} for {date.strftime('%Y-%m-%d')}: {str(e)}")
def main():
# 设置保存路径
savebase = pathlib.Path('/workspace/data')
savebase.mkdir(parents=True, exist_ok=True)
# 获取当前日期的七天前
seven_days_ago = datetime.datetime.today() - datetime.timedelta(days=7)
var_list = [
'divergence', 'fraction_of_cloud_cover', 'geopotential', 'ozone_mass_mixing_ratio',
'potential_vorticity', 'relative_humidity', 'specific_cloud_ice_water_content',
'specific_cloud_liquid_water_content', 'specific_humidity', 'specific_rain_water_content',
'specific_snow_water_content', 'temperature', 'u_component_of_wind', 'v_component_of_wind',
'vertical_velocity', 'vorticity'
]
# 遍历变量列表
for var in var_list:
# 获取每个变量对应的文件夹路径
var_directory = savebase / var
var_directory.mkdir(parents=True, exist_ok=True)
# 获取当前变量文件夹中的最新文件日期
latest_date = get_latest_date_from_var_directory(var_directory)
if latest_date is None:
# 如果没有找到文件,默认从起始日期开始
pre_begin = datetime.datetime.strptime('20241101', '%Y%m%d')
else:
# 比较当前日期的七天前与最新文件日期
if latest_date.date() == seven_days_ago.date():
print(f"{var} 的最新文件已经下载完毕,无需重复下载。")
continue # 如果最新文件的日期等于当前时间七天前,跳过该变量的下载
pre_begin = latest_date + datetime.timedelta(days=1) # 从最新文件的下一天开始
# 截止日期为当前日期的七天之前
end = seven_days_ago
# 循环下载数据,直到截止日期
current_date = pre_begin
while current_date <= end:
download_era5_data(var, current_date, var_directory / f"era5.{var}.{current_date.strftime('%Y%m%d')}.nc")
current_date += datetime.timedelta(days=1)
if __name__ == "__main__":
main()