Python:如何从地球大数据科学服务中心批量下载VPM-GPP?
<h1 id="01-说明">01 说明</h1><h2 id="11-网站和gpp数据集的基本信息">1.1 网站和GPP数据集的基本信息</h2>
<p>中国科学院地球大数据科学数据中心-网址:<code>https://data.casearth.cn</code><br>
本博客下载VPM-GPP的网址:<code>https://data.casearth.cn/dataset/5c19a5660600cf2a3c557ad3</code></p>
<p>2000-2016年全球0.05°基于VPM模型的GPP数据集-产品信息:</p>
<p><img src="https://img2024.cnblogs.com/other/3638406/202507/3638406-20250731185835571-2060665191.png"></p>
<h2 id="12-api说明">1.2 API说明</h2>
<p>网站所给API如下:</p>
<p><img src="https://img2024.cnblogs.com/other/3638406/202507/3638406-20250731185835940-755160413.png"></p>
<p>本博客主要基于<code>通过ID获取文件列表</code>和<code>单文件下载</code>进行VPM-GPP的批量下载,对于元数据信息没有进行获取,但代码可以作为参考。</p>
<h3 id="121-通过id获取文件列表">1.2.1 通过ID获取文件列表</h3>
<p>这个<code>Url</code>是不完整的,是相对路径,结合前面的网站地址<code>https://data.casearth.cn</code>和此处的<code>/api/dataset/getAllFileListBySdoId?sdoId=5c19a5660600cf2a3c557ad3</code>拼接得到的网址<code>https://data.casearth.cn/api/dataset/getAllFileListBySdoId?sdoId=5c19a5660600cf2a3c557ad3</code>(通过浏览器即可打开)就是一个文件列表。</p>
<p>结果如下:</p>
<p><img src="https://img2024.cnblogs.com/other/3638406/202507/3638406-20250731185836493-1043622395.png"></p>
<p>但是这里我们不直接下载,我们通过<code>Python</code>直接读取成<code>json</code>文件对这个文件列表进一步处理.(如果想要直接下载直接复制放到文本文件里改个后缀的事情)。</p>
<h3 id="122-单文件下载">1.2.2 单文件下载</h3>
<p>对于单文件下载,这里需要使用到前面获取得到的文件列表的信息补全这里的<code>Url</code>:<code>/api/file/downloadOneFile?fileId=文件Id&username=用户名</code>。</p>
<p>例如取文件列表<code>data</code>中一个文件:<code>{"size":18485479,"file_name":"GPP.VPM.2000321.v20.CMG.tif","id":2148752}</code>,需要将<code>id</code>即<code>2148752</code>替换上面<code>Url</code>中的<code>文件Id</code>。</p>
<p>此外还有一个关键点,将<code>用户名</code>替换为登录中国科学院地球大数据科学数据中心后(<code>https://data.casearth.cn</code>)的用户名,<strong>注意</strong>不是登录时输入的用户名账号,而是登录之后显示的用户名(如下)。</p>
<p><img src="https://img2024.cnblogs.com/other/3638406/202507/3638406-20250731185837174-313313709.png"><br>
补全之后的Url和之前的网站网址拼接即可得到该文件的下载链接(所以网站为什么索性不直接给一个含下载txt文件让我们自己做,当然现在也还好只是对于不熟悉下载的同学不是很友好)。</p>
<p>最终就是,按照上述的拼接就可以得到指定文件的下载链接例如:<code>https://data.casearth.cn/api/file/downloadOneFile?fileId=2148752&username=何泽煌</code>,现在代码的目标就是一直重复上述操作执行下载。</p>
<h1 id="02-代码说明">02 代码说明</h1>
<p>完整代码:</p>
<pre><code class="language-python"># @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 的映射)
"""
# 文件列表的url
file_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}, {}, {}, ···]
# 获取文件名和id
files_info = {file['file_name']: file['id'] for file in files_info}
print(f"成功获取 {len(files_info)} 个文件的信息。")
return files_info
def 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 True
print(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 True
except Exception as e:
print(f"下载失败: 文件名-{filename}, 失败原因-{e}")
time.sleep(0.05)
return False
if __name__ == "__main__":
# 获取文件基本信息
files_info = get_files_info(base_url, id_url)
# 遍历下载
success_count = 0
for 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))
</code></pre>
<h2 id="21-get_files_info函数说明">2.1 <code>get_files_info</code>函数说明</h2>
<p>简而言之,按照之前的下载链接的拼接方式,我们最关键的是需要所有文件的<code>id</code>,但是由于输出文件时需要指定输出的文件名称,所以这里我还将<code>file_name</code>提取出来与<code>id</code>一一对应。因此这个函数的作用时将从读取的<code>json</code>文件中将<code>file_name</code>和<code>id</code>以字典<code>dict</code>形式返回。形如<code>{文件名_1: 文件的id_1, 文件名_2: 文件的id_2···}</code></p>
<h2 id="22-download_file函数说明">2.2 <code>download_file</code>函数说明</h2>
<p>这个函数就是将之前的拼接方式以代码形式呈现得到当前所要下载文件的下载链接,接着通过<code>request</code>库进行进行即可。这里没有具体的说明是因为代码中有较为详细的注释供参考,且函数的参数和使用并不复杂,这里不展开说明,自行研究即可。</p>
<blockquote>
<p>本文由博客一文多发平台 OpenWrite 发布!</p>
</blockquote><br><br>
来源:https://www.cnblogs.com/ChaoQiezi/p/19015742
頁:
[1]