☰
Sentinel离线数据批量下载方案:sentinelsat唤醒与IDM多线程加速
2026/10/15 8:28:39 网站建设 项目流程

1. 这套方案到底在解决什么问题

哥白尼哨兵系列卫星数据(Sentinel)是目前公开遥感数据里更新最勤、覆盖最广的一批,尤其是 Sentinel-1 和 Sentinel-2,做地表变化检测、农作物长势监测、水体提取、城市扩张分析的人几乎都绕不开它。但真正下过数据的人都知道,最让人抓狂的不是处理算法,而是下载这一步。官方提供的下载渠道里,有一部分数据处于 offline 状态,也就是长期归档、不放在在线缓存里,你点下载它不会立刻给你,而是需要先提交一个“唤醒”请求,等后台把数据从磁带库搬回在线存储,通常要等几十分钟到几个小时不等,然后你才能拿到真正的下载链接。

sentinelsat这个 Python 包就是官方 API 的一个封装,它把搜索、查询、触发离线数据唤醒、获取下载链接这些动作都做成了函数调用,用起来比手点网页舒服得多。但它自带的下载函数是单线程的,速度慢、断点续传能力弱,遇到大文件(Sentinel-1 的 SLC 产品动辄 4~8 GB)经常下到一半就崩。而 IDM(Internet Download Manager)是 Windows 上老牌的多线程下载工具,支持分段并发、断点续传、失败自动重试,把这两者结合起来,就能实现“用 sentinelsat 负责搜索和唤醒,用 IDM 负责高速下载”的分工。

这套流程适合谁?适合需要批量获取历史存档 Sentinel 数据的研究生、遥感工程师、做时序分析的从业者,尤其是那些需要一次性拉几十上百景、又不想守着电脑一景一景点的人。下面我把整套思路、关键细节、踩过的坑全部拆开讲清楚。

2. 整体设计思路与方案选型

2.1 为什么是 sentinelsat 而不是纯网页操作

纯网页操作的问题在于:第一,搜索条件复杂时(比如按云量、按轨道号、按时间窗口、按相对轨道号组合筛选),网页端每次都要重新点一遍;第二,offline 数据的唤醒状态没法批量管理,你得一个个点进去看它是否已经 online;第三,拿到下载链接后还要手动复制到下载工具里,几十景就是几十次重复劳动。

sentinelsat把这些都 API 化了。它的核心对象是SentinelAPI,初始化时传入账号密码,然后query()方法接受一系列过滤参数,返回一个字典,key 是产品 UUID,value 是产品元信息。元信息里包含title、size、online(是否在线)、uuid等字段。最关键的是download()和download_all()这两个方法,以及trigger_offline_retrieval()这个专门用来唤醒离线数据的接口。

注意:sentinelsat 的download()内部其实也是走 HTTP 流式下载,它支持断点续传(通过continue参数),但并发能力有限,单连接速度受服务器限速影响明显。这就是为什么我们要把下载环节交给 IDM。

2.2 为什么用 IDM 而不是 aria2 或 wget

aria2 和 wget 当然也能多线程,但 IDM 在 Windows 桌面环境下的优势是:图形界面直观、支持剪贴板监控(复制链接自动弹出下载框)、分段策略成熟、对不稳定网络的容错做得好。更重要的是,IDM 可以直接导入一个包含多条 URL 的文本文件,批量排队下载,这对我们“先批量拿到链接、再统一丢给下载器”的流程非常契合。

如果你在 Linux 服务器上跑,aria2 是更自然的选择,但本文以 Windows + IDM 为主线,因为这是提问者标题里明确点出的组合。Linux 用户可以把 IDM 替换成 aria2c,逻辑完全一样。

2.3 整体流程拆解

整套流程分四步:

  1. 搜索:用 sentinelsat 按条件查询产品,拿到 UUID 列表和元信息。
  2. 唤醒:筛选出online=False的产品,调用trigger_offline_retrieval()提交唤醒请求。
  3. 轮询:定期检查这些产品是否已经变为 online,拿到最终下载链接。
  4. 下载:把链接写入文本文件,导入 IDM 批量下载。

这个分工的核心逻辑是:sentinelsat 负责“跟官方服务器打交道”,IDM 负责“跟文件传输打交道”。两者通过一个中间文件(URL 列表)解耦,互不干扰。

3. 核心细节解析与实操要点

3.1 sentinelsat 的安装与账号准备

安装很简单:

pip install sentinelsat

但有几个细节要注意。sentinelsat 依赖geopandas和pandas,如果你用的是较新的 Python 版本(3.10+),建议先建虚拟环境,避免和系统里的科学计算包冲突。我实测下来,用 conda 建一个干净环境最稳:

conda create -n sentinel python=3.9 conda activate sentinel pip install sentinelsat

账号方面,你需要一个哥白尼数据空间(Copernicus Data Space Ecosystem)的账号。注意,早期很多人用的是 SciHub 账号,但 SciHub 已经退役,现在统一走 Copernicus Data Space。sentinelsat 较新版本(1.1.0+)已经支持新的端点,初始化时不需要额外指定 URL,默认就是新端点。如果你用的是老版本,可能会连不上,建议直接升级到最新版。

from sentinelsat import SentinelAPI, read_geojson, geojson_to_wkt api = SentinelAPI('你的用户名', '你的密码')

提示:不要把账号密码硬编码在脚本里然后传到公开仓库。用环境变量或者单独的配置文件读取,这是基本的安全习惯。

3.2 查询条件的构造技巧

query()方法的参数很多,常用的有:

  • platformname:'Sentinel-1'或'Sentinel-2'
  • producttype:S2 常用'S2MSI1C'(L1C)或'S2MSI2A'(L2A),S1 常用'GRD'或'SLC'
  • date:格式('20230101', '20230301'),注意是字符串元组
  • cloudcover:S2 专用,格式(0, 20)表示云量 0~20%
  • area:用 WKT 或 geojson 描述的空间范围
  • relativeorbitnumber:相对轨道号,做时序分析时非常有用

一个典型的查询:

products = api.query( area=footprint, date=('20230101', '20230601'), platformname='Sentinel-2', producttype='S2MSI2A', cloudcover=(0, 15) )

这里有个坑:area参数如果传的是一个很大的多边形,查询会返回大量结果,而且服务器端可能超时。我的经验是,尽量用相对轨道号 + 小范围 AOI 组合,把结果控制在几百景以内。如果确实要查大范围,分批次按月份或按轨道号查,别一次性拉。

3.3 判断 online 状态与触发唤醒

查询返回的products是一个字典,每个产品的元信息里有一个online字段。但要注意,这个字段在查询结果里可能不是实时准确的,有时候显示 online 但实际下载时才发现是 offline。更可靠的做法是调用api.is_online(uuid)逐个确认,或者直接尝试获取下载链接,看返回状态。

唤醒离线数据的接口是:

api.trigger_offline_retrieval(uuid)

这个调用会向服务器提交一个唤醒请求。关键点来了:唤醒请求不是无限次提交的,官方对每个账号有配额限制。如果你一次性对几百景提交唤醒,可能会触发限流,导致部分请求失败。我的做法是分批提交,每批 20~30 景,中间 sleep 几秒。

import time offline_uuids = [uuid for uuid, info in products.items() if not info['online']] for i in range(0, len(offline_uuids), 25): batch = offline_uuids[i:i+25] for uuid in batch: try: api.trigger_offline_retrieval(uuid) except Exception as e: print(f'{uuid} 唤醒失败: {e}') time.sleep(5)

注意:唤醒请求提交后,数据不会立刻变 online。官方文档说通常几十分钟到几小时,但实测下来,热门区域的数据可能几分钟就好了,冷门区域或者老数据可能要等半天甚至更久。所以轮询是必须的。

3.4 轮询状态与获取下载链接

轮询的逻辑是:每隔一段时间检查一次之前提交唤醒的产品,看online是否变为 True。一旦变为 True,就可以调用api.get_product_odata(uuid)拿到下载链接,或者直接用api.download(uuid)下载。

但我们要的是链接,不是直接下载。sentinelsat 提供了一个方法可以拿到产品的下载 URL:

odata = api.get_product_odata(uuid) download_url = odata['Download']['value']

不过这个 URL 是带 token 的,有时效性。所以拿到链接后要尽快丢给 IDM 开始下载,别攒着。我的做法是:轮询到 online 后,立刻把链接追加写入一个urls.txt文件,IDM 那边设置成监控这个文件或者定时导入。

轮询脚本可以这样写:

import time pending = set(offline_uuids) while pending: for uuid in list(pending): odata = api.get_product_odata(uuid) if odata['Online']: url = odata['Download']['value'] with open('urls.txt', 'a') as f: f.write(url + '\n') pending.remove(uuid) print(f'{uuid} 已就绪,链接已写入') if pending: print(f'还剩 {len(pending)} 景等待中,60秒后重试') time.sleep(60)

这里有个细节:get_product_odata返回的字段名可能是Online也可能是online,取决于 sentinelsat 版本。建议先打印一次看看实际结构,别照抄。

4. 实操过程与核心环节实现

4.1 完整脚本的搭建

把上面的片段串起来,形成一个完整的脚本。我习惯分成三个文件:config.py放账号和查询参数,search_and_trigger.py负责搜索和唤醒,poll_and_export.py负责轮询和导出链接。这样分工的好处是,搜索和唤醒可以跑一次就停,轮询可以挂着长期跑,互不影响。

config.py:

USERNAME = 'your_username' PASSWORD = 'your_password' AOI_GEOJSON = 'aoi.geojson' DATE_RANGE = ('20230101', '20230601') PLATFORM = 'Sentinel-2' PRODUCT_TYPE = 'S2MSI2A' CLOUD_COVER = (0, 15)

search_and_trigger.py:

from sentinelsat import SentinelAPI, read_geojson, geojson_to_wkt import config import time api = SentinelAPI(config.USERNAME, config.PASSWORD) footprint = geojson_to_wkt(read_geojson(config.AOI_GEOJSON)) products = api.query( area=footprint, date=config.DATE_RANGE, platformname=config.PLATFORM, producttype=config.PRODUCT_TYPE, cloudcover=config.CLOUD_COVER ) print(f'共查询到 {len(products)} 景') offline = [u for u, i in products.items() if not i['online']] print(f'其中 {len(offline)} 景为离线状态,开始唤醒') for i in range(0, len(offline), 25): for uuid in offline[i:i+25]: try: api.trigger_offline_retrieval(uuid) except Exception as e: print(f'{uuid}: {e}') time.sleep(5) with open('pending_uuids.txt', 'w') as f: for uuid in offline: f.write(uuid + '\n')

poll_and_export.py:

from sentinelsat import SentinelAPI import config import time api = SentinelAPI(config.USERNAME, config.PASSWORD) with open('pending_uuids.txt') as f: pending = set(line.strip() for line in f if line.strip()) while pending: for uuid in list(pending): try: odata = api.get_product_odata(uuid) if odata.get('Online') or odata.get('online'): url = odata['Download']['value'] with open('urls.txt', 'a') as f: f.write(url + '\n') pending.remove(uuid) print(f'就绪: {uuid}') except Exception as e: print(f'检查 {uuid} 出错: {e}') if pending: print(f'剩余 {len(pending)} 景,等待中...') time.sleep(120)

4.2 IDM 的批量导入配置

拿到urls.txt后,打开 IDM,选择“任务”->“从文件导入”,选中这个文本文件,IDM 会自动把所有链接加入下载队列。这里有几个设置要调:

  • 连接数:默认是 8,可以调到 16,但不要太高,官方服务器对单 IP 的并发连接有限制,调太高反而容易被限速。
  • 重试次数:设成 5~10 次,网络抖动时自动重试。
  • 保存路径:按产品类型分文件夹,比如S2MSI2A/、S1GRD/,方便后续处理。
  • UA 设置:IDM 默认的 User-Agent 有时会被服务器拒绝,可以在“选项”->“下载”里自定义 UA,模仿浏览器。

提示:IDM 导入的链接如果带 token,token 过期后下载会失败。所以导入后尽快开始下载,别让队列挂太久。如果发现大量 403 错误,说明 token 过期了,需要重新跑轮询脚本生成新链接。

4.3 参数计算与选择过程

这里重点说两个参数的选择逻辑。

并发连接数:官方服务器对单个账号的并发下载数没有明确文档,但实测下来,同时下载 3~5 个文件、每个文件 8~16 连接是比较稳的。如果你一次性把 100 个链接全丢进去让 IDM 同时下,大概率会被限速甚至临时封禁。我的做法是:IDM 队列里同时活跃的任务不超过 5 个,其余排队。

轮询间隔:太短会浪费请求配额,太长会延迟拿到链接。我的经验是,唤醒提交后的前 30 分钟,每 2 分钟查一次;30 分钟到 2 小时,每 10 分钟查一次;2 小时以上,每 30 分钟查一次。这样既能及时拿到,又不会过度请求。

def get_interval(elapsed_minutes): if elapsed_minutes < 30: return 120 elif elapsed_minutes < 120: return 600 else: return 1800

4.4 实操现场记录

我最近一次跑这套流程,是拉一批 2022 年春季的 Sentinel-2 L2A 数据,AOI 是某农业区,时间窗口 3 个月,云量 15% 以下,共查到 87 景。其中 62 景是 online,25 景是 offline。唤醒提交后,最快的 8 分钟就变 online 了,最慢的一景等了将近 4 小时。62 景在线数据直接用 sentinelsat 的download_all下了,25 景离线数据走唤醒 + IDM 流程。IDM 那边 5 个并发任务,平均每个文件 800 MB 左右,总耗时约 2 小时 40 分钟,比纯 sentinelsat 单线程快了大概 4 倍。

5. 常见问题与排查技巧实录

5.1 唤醒请求提交失败

最常见的报错是HTTP 429 Too Many Requests,说明你提交太快了。解决办法就是分批 + sleep,前面已经说了。还有一种情况是Invalid UUID,通常是因为你传的 UUID 格式不对,或者这个产品已经被删除。检查一下 UUID 是不是从products字典的 key 里直接取的,别手动拼。

5.2 轮询一直显示 offline

如果一景数据唤醒后超过 24 小时还是 offline,可能是唤醒请求没真正生效。可以登录 Copernicus Data Space 网页端,找到这个产品看看它的状态。有时候网页端会显示“正在检索中”,这种情况只能等。如果网页端显示的还是“离线”且没有检索任务,说明你的唤醒请求丢了,重新提交一次。

5.3 IDM 下载报 403

403 基本就是 token 过期。sentinelsat 拿到的下载链接里带一个token参数,有效期通常几小时。解决办法是重新跑轮询脚本,生成新链接。为了避免频繁过期,建议轮询到 online 后立刻下载,别攒。

5.4 下载速度忽快忽慢

这通常是服务器端限速,不是你的问题。可以尝试降低并发连接数,有时候连接数少反而更稳。另外,避开欧洲时间的白天(北京时间下午到晚上),那时候服务器负载高,速度会慢一些。北京时间凌晨到上午,速度明显更快。

5.5 常见问题速查表

问题现象可能原因解决办法
429 Too Many Requests唤醒提交过快分批提交,每批 25 景,间隔 5 秒
轮询一直 offline唤醒请求丢失网页端确认状态,重新提交
IDM 403token 过期重新轮询生成新链接
下载速度慢服务器限速降低并发,避开高峰时段
查询超时AOI 太大或时间跨度太长分批次查询,加轨道号过滤
脚本报 KeyError字段名版本差异打印 odata 结构确认字段名

5.6 独家避坑技巧

第一个技巧:把 UUID 和产品标题的映射存下来。轮询脚本里只处理 UUID,但下载下来的文件名是产品标题,后期对不上号很麻烦。建议在搜索阶段就存一个uuid -> title的 JSON 文件,后面处理数据时直接查。

第二个技巧:IDM 的临时文件别乱删。IDM 下载大文件时会生成.tmp文件,断点续传靠的就是它。如果你中途手动清理了临时文件,续传就失效了,得从头下。

第三个技巧:用requests先验证链接可用性。在把链接写入urls.txt之前,可以用requests.head(url)快速检查一下返回码,200 才写入,避免把失效链接丢给 IDM。

import requests def check_url(url): try: r = requests.head(url, timeout=10) return r.status_code == 200 except: return False

第四个技巧:日志要记全。每次唤醒、每次轮询、每次链接生成,都写进日志文件。出问题的时候,日志是你唯一的线索。我习惯用 Python 的logging模块,按天切分日志文件。

这套流程我前后跑了大概半年,处理过几千景数据,最大的体会是:搜索和唤醒要稳,下载要快,两者解耦是关键。sentinelsat 的 API 调用要克制,别把配额浪费在无意义的轮询上;IDM 的队列要控制,别贪多导致被封。把这两点做好,批量下载离线 Sentinel 数据就不再是件折磨人的事。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询