Python:如何从地球大数据科学服务中心批量下载VPM-GPP?
01 说明
1.1 网站和GPP数据集的基本信息
中国科学院地球大数据科学数据中心-网址:https://data.casearth.cn
本博客下载VPM-GPP的网址:https://data.casearth.cn/dataset/5c19a5660600cf2a3c557ad3
2000-2016年全球0.05°基于VPM模型的GPP数据集-产品信息:
1.2 API说明
网站所给API如下:
本博客主要基于通过ID获取文件列表
和单文件下载
进行VPM-GPP的批量下载,对于元数据信息没有进行获取,但代码可以作为参考。
1.2.1 通过ID获取文件列表
这个Url
是不完整的,是相对路径,结合前面的网站地址https://data.casearth.cn
和此处的/api/dataset/getAllFileListBySdoId?sdoId=5c19a5660600cf2a3c557ad3
拼接得到的网址https://data.casearth.cn/api/dataset/getAllFileListBySdoId?sdoId=5c19a5660600cf2a3c557ad3
(通过浏览器即可打开)就是一个文件列表。
结果如下:
但是这里我们不直接下载,我们通过Python
直接读取成json
文件对这个文件列表进一步处理.(如果想要直接下载直接复制放到文本文件里改个后缀的事情)。
1.2.2 单文件下载
对于单文件下载,这里需要使用到前面获取得到的文件列表的信息补全这里的Url
:/api/file/downloadOneFile?fileId=文件Id&username=用户名
。
例如取文件列表data
中一个文件:{"size":18485479,"file_name":"GPP.VPM.2000321.v20.CMG.tif","id":2148752}
,需要将id
即2148752
替换上面Url
中的文件Id
。
此外还有一个关键点,将用户名
替换为登录中国科学院地球大数据科学数据中心后(https://data.casearth.cn
)的用户名,注意不是登录时输入的用户名账号,而是登录之后显示的用户名(如下)。
补全之后的Url和之前的网站网址拼接即可得到该文件的下载链接(所以网站为什么索性不直接给一个含下载txt文件让我们自己做,当然现在也还好只是对于不熟悉下载的同学不是很友好)。
最终就是,按照上述的拼接就可以得到指定文件的下载链接例如:https://data.casearth.cn/api/file/downloadOneFile?fileId=2148752&username=何泽煌
,现在代码的目标就是一直重复上述操作执行下载。
02 代码说明
完整代码:
# @Author : ChaoQiezi
# @Time : 2025/7/17 上午11:09
# @Email : chaoqiezi.one@qq.com
# @Wechat : GIS茄子
# @FileName: download_VPM_GPP"""
This script is used to 批量下载VPM-GPP
"""import requests
import os
import time# 网站根地址
base_url = "https://data.casearth.cn"
# id_url, 网站中<API:通过ID获取文件列表>所显示的Url
id_url = "/api/dataset/getAllFileListBySdoId?sdoId=5c19a5660600cf2a3c557ad3"
# 用户名, 登录后网站显示的名字就是你的用户名, 一般不是登录时的输入账户名
user_name = "何泽煌"
# 输出文件夹
out_dir = r"I:\DataHub\VPM_GPP"def get_files_info(base_url, id_url):"""调用API获取所有文件的信息列表:param base_url: 根网址:param id_url: 网站中<API:通过ID获取文件列表>所显示的Url:return: 以字典形式返回{file_name: file_id, ···}""""""调用API获取所有文件的信息列表 (文件名 -> 文件ID 的映射)"""# 文件列表的urlfile_info_url = f"{base_url}{id_url}"# 获取文件列表的基本信息response = requests.get(file_info_url, timeout=30, verify=False) # timeout=30表示超时30s没有链接上报错, verify是因为该网站SSL证书过期了这里不验证response.raise_for_status() # 查询请求状态,若请求状态异常说明无法下载直接弹出报错files_info = response.json()['data'] # [{'size': xxx, 'file_name': xxx, 'id': xxx}, {}, {}, ···]# 获取文件名和idfiles_info = {file['file_name']: file['id'] for file in files_info}print(f"成功获取 {len(files_info)} 个文件的信息。")return files_infodef download_file(base_url, filename, file_id, username, save_dir):"""下载单个文件:param base_url: 根网址:param filename: 文件名(非自定义的输出, 基于api读取):param file_id: 文件id:param username: 用户名(登录后网站显示的名字就是你的用户名, 一般非登录的账户名):param save_dir: 输出文件夹路径:return: 成功返回True, 下载失败返回False""""""下载单个文件"""api_url = f"{base_url}/api/file/downloadOneFile?fileId={file_id}&username={username}"out_path = os.path.join(save_dir, filename)# 如果文件已经存在,则跳过if os.path.exists(out_path):print(f"文件存在: 文件名-{filename}")return Trueprint(f"正在下载: 文件名-{filename}, ID-{file_id})")try: # 可能由于网络等问题下载容易断开,这里try一下with requests.get(api_url, timeout=60, verify=False) as r:r.raise_for_status() # 查询请求状态,若请求状态异常说明无法下载报错with open(out_path, 'wb') as f:# 下方代码表示分块写入为防止下载文件过大而内存爆满, 如果执行这种流式传输,get方法需要设置stream=True# for chunk in r.iter_content(chunk_size=8192):# f.write(chunk)# 一次性写入(确保单个文件大小小于当前可用内存)f.write(r.content)time.sleep(0.01) # 友好请求,在每次下载后暂停一小段时间,防止因请求过快被服务器屏蔽return Trueexcept Exception as e:print(f"下载失败: 文件名-{filename}, 失败原因-{e}")time.sleep(0.05)return Falseif __name__ == "__main__":# 获取文件基本信息files_info = get_files_info(base_url, id_url)# 遍历下载success_count = 0for file_name, file_id in files_info.items():success_count += download_file(base_url, file_name, file_id, user_name, out_dir)print("\n下载完成: 成功-{}, 失败-{}".format(success_count, len(files_info) - success_count))
2.1 get_files_info
函数说明
简而言之,按照之前的下载链接的拼接方式,我们最关键的是需要所有文件的id
,但是由于输出文件时需要指定输出的文件名称,所以这里我还将file_name
提取出来与id
一一对应。因此这个函数的作用时将从读取的json
文件中将file_name
和id
以字典dict
形式返回。形如{文件名_1: 文件的id_1, 文件名_2: 文件的id_2···}
2.2 download_file
函数说明
这个函数就是将之前的拼接方式以代码形式呈现得到当前所要下载文件的下载链接,接着通过request
库进行进行即可。这里没有具体的说明是因为代码中有较为详细的注释供参考,且函数的参数和使用并不复杂,这里不展开说明,自行研究即可。