资讯详情

OCO-2卫星数据批量下载自动化全流程指南

📅 2026/10/23 11:42:44 | 华诺云谱 👁 阅读
OCO-2卫星数据批量下载自动化全流程指南
1. 项目概述为什么一个卫星数据下载流程值得单独写一篇万字长文OCO-2全称Orbiting Carbon Observatory-2是NASA发射的高精度大气二氧化碳监测卫星。它不是那种“拍张照片发朋友圈”的遥感平台而是用三台光谱仪在特定波段上以每秒20次的频率扫描地表反射光通过反演算法精确计算出大气柱中CO₂的浓度——精度达到1 ppm百万分之一相当于在整条京沪高速上准确数出多出的那一辆汽车。这种数据是全球碳循环建模、城市排放清单校验、森林固碳能力评估、甚至电力行业碳排放实时监测的底层燃料。但问题来了它的数据不是点开网页点几下就能下载的。官方数据存放在NASA的Earthdata系统里整个体系像一座由身份认证、权限申请、协议适配、脚本调度、文件校验、目录管理组成的立体迷宫。我第一次接触这个需求是帮某高校环境学院的导师处理一批2019–2022年华北平原区域的月均XCO₂产品。原以为“找链接→点下载”十分钟搞定结果卡在Earthdata账号注册后的邮箱验证环节整整两天——因为系统要求必须用.edu后缀的教育邮箱而对方实验室用的是企业邮箱后来好不容易登进去发现单个L2标准产品文件动辄300MB起步手动点12个月×4个区域×12个月份得点432次且每次都要等页面刷新、防刷机制拦截、下载中断重试……第三天凌晨三点我盯着浏览器里第17次“Download failed: Connection reset by peer”的报错弹窗彻底放弃了人工操作。这就是为什么“OCO-2批量数据下载”不是一个功能点而是一整套工程化工作流。它横跨三个层面身份层Earthdata账号API Token、协议层HTTPSCMRDAAC定制接口、执行层命令行工具Python脚本任务调度。市面上能找到的教程要么是零散的GitHub gist片段只讲curl怎么带token要么是NASA官网PDF文档通篇术语堆砌连“如何生成有效时间范围字符串”这种基础问题都默认你已掌握更常见的是过时内容——比如还在教用旧版wget配合cookie文件而Earthdata早在2022年就全面启用了OAuth2.0令牌机制旧方法全部失效。所以这篇【整合】不是教你怎么点鼠标而是给你一套可直接部署、可审计、可交接、可写进项目结题报告的生产级方案。它覆盖从零开始注册账号、获取长期有效Token、构建时空查询条件、自动生成下载命令、断点续传控制、SHA256自动校验、按日期/区域/产品类型三级归档的完整闭环。无论你是刚接触遥感数据的研一学生还是需要稳定获取CO₂数据支撑模型训练的算法工程师或是负责为多个课题组统一提供数据服务的实验室管理员这套流程都能让你把原本耗时3天的手工劳动压缩到一条终端命令后台静默运行的15分钟内完成。核心关键词就四个OCO-2、Earthdata、批量下载、自动化流程——所有内容只围绕这四件事展开不跑题不炫技不堆砌无关概念。2. 整体设计与思路拆解为什么放弃图形界面坚持命令行脚本组合很多人第一反应是“有没有现成的GUI工具比如QGIS插件或者NASA自己出的客户端”答案是有但都不适合批量场景。NASA官方推荐的AppEEARS在线平台限制单次请求最多10个空间点位、时间跨度不能超过2年而我们实际项目中经常要提取一个50km×50km矩形区域内连续5年的逐日L2产品共计约1800个文件。另一个常被提及的工具是NASA’s LAADS DAAC提供的Web-based Data Access它支持区域裁剪但下载按钮背后仍是HTTP重定向无法获取真实文件URL也就无法写入脚本循环——你点一次它跳转一次浏览器地址栏里的URL每次都不一样根本没法抓包复现。所以必须回归命令行。这不是为了显得“高级”而是由OCO-2数据分发机制决定的刚性约束。它的数据存储在两个物理位置主库在GES DISCGoddard Earth Sciences Data and Information Services Center元数据索引则托管在CMRCommon Metadata Repository系统中。CMR提供标准REST API返回JSON格式的元数据记录其中包含每个数据文件的直接下载链接url字段、文件大小size字段、校验码checksum字段。这才是批量下载的唯一可靠入口。而调用CMR API必须携带有效的Earthdata认证凭证这个凭证目前只支持两种形式一是HTTP Basic Auth用户名密码二是Bearer TokenOAuth2.0令牌。前者因安全策略已被逐步淘汰后者才是NASA明确要求的生产环境标准方式。因此整个流程的设计锚点非常清晰以CMR API为数据源以Earthdata Token为通行证以Shell/Python为执行引擎以文件系统为最终落点。具体拆解为四步闭环凭证准备阶段不是简单登录网页而是通过Earthdata的OAuth2.0授权码流程获取一个有效期长达30天的Bearer Token并安全存储元数据检索阶段构造符合CMR语法的查询参数时间范围、地理范围、数据集名称、版本号向https://cmr.earthdata.nasa.gov/search/granules.json发起GET请求解析返回的JSON提取所有匹配文件的下载URL和校验信息并行下载阶段将URL列表分片用aria2c而非wget或curl启动多线程下载启用断点续传、连接复用、失败重试最大3次和自动重命名避免URL中特殊字符导致的文件名错误质量保障阶段下载完成后对每个文件执行SHA256校验比对CMR返回的checksum值校验失败的文件自动移入/failed/子目录并记录日志成功文件按YYYY/MM/DD/结构归档同时生成manifest.csv汇总表包含文件名、原始URL、大小、校验码、下载时间戳。这个设计拒绝一切“看起来方便实则脆弱”的方案。比如不用浏览器自动化工具Selenium/Puppeteer因为Earthdata页面频繁更新DOM结构上周还能定位的下载按钮下周可能class名就变了也不用第三方Python库如earthdata包因为它封装了太多底层细节一旦NASA调整API规则库就失效而我们无法快速定位问题根源。所有环节都暴露在明处Token怎么生成、URL怎么拼、参数怎么编码、校验怎么算——每一个步骤你都能在终端里单独执行、单独调试、单独验证。这才是工程化思维而不是“能跑就行”的脚本思维。3. 核心细节解析与实操要点从Earthdata账号注册到Token安全存储的避坑指南3.1 Earthdata账号注册那个被忽略的“教育邮箱”陷阱注册Earthdata账号本身不难难点在于账号类型与后续权限的强绑定。官网注册页https://urs.earthdata.nasa.gov/users/new表面看只是填邮箱、密码、姓名但隐藏着一个关键逻辑账号的“组织类型”决定了你能访问哪些数据集。OCO-2数据属于“Restricted”级别意味着它不向公众完全开放必须声明你是科研/教育机构成员才能获得下载权限。提示注册时“Organization Type”下拉菜单里务必选择“University/College”或“Research Institution”。选“Other”或“Commercial”会导致后续申请OCO-2数据访问权限被系统自动拒绝且申诉周期长达5个工作日。更隐蔽的坑是邮箱后缀。很多高校实验室使用企业邮箱如xxxlab.company.com但Earthdata的自动审核系统会检测邮箱域名是否在已知教育机构白名单中。不在白名单的邮箱即使你选了“University”也会被标记为“Pending Manual Review”而人工审核往往石沉大海。解决方案只有两个使用学校官方edu邮箱注册最稳妥若无edu邮箱则在注册时“Organization Name”栏填写你所在高校的全称英文名如“Tsinghua University”而非“THU”并在“Organization Website”栏填写该校官网地址https://www.tsinghua.edu.cn这样系统有可能通过域名反查匹配到白名单。注册完成后别急着登录。先检查邮箱Earthdata会发送一封含验证链接的邮件。注意该链接24小时内有效且点击后必须在同一个浏览器会话中完成密码设置。我曾遇到过用户用Chrome点击链接却在Firefox里打开Earthdata首页设置密码结果提示“Invalid token”——因为跨浏览器会丢失session上下文。正确做法是点击邮件链接后保持当前浏览器标签页等待跳转到Earthdata密码设置页一次性完成。3.2 获取Earthdata TokenOAuth2.0流程的终端化实现登录Earthdata账号后真正的挑战才开始如何把网页上的“Generate Token”按钮变成终端里一行可复用的命令NASA官方文档https://wiki.earthdata.nasa.gov/display/CMR/Howtogenerateatoken只给了一个交互式网页路径但没告诉你Token本质就是一个Base64编码的JSON Web TokenJWT其有效期、作用域、签名方式都是固定的。实操中我们绕过网页直接调用Earthdata的OAuth2.0令牌端点curl -X POST \ -H Content-Type: application/json \ -d { client_id: EDSC, redirect_uri: https://localhost:8888/callback, response_type: code, scope: uid } \ https://urs.earthdata.nasa.gov/oauth/authorize但这只是第一步——它返回的是一个临时授权码code还需用此code向令牌颁发端点换Token。整个流程需两步HTTP请求且涉及敏感凭据传输。为避免硬编码密码我们采用“设备授权码”Device Authorization模式这是Earthdata在2023年新增的支持方式安全性更高首先向设备授权端点请求一个用户码DEVICE_RESP$(curl -s -X POST \ -H Content-Type: application/x-www-form-urlencoded \ -d client_idEDSC \ https://urs.earthdata.nasa.gov/oauth/device/code) USER_CODE$(echo $DEVICE_RESP | jq -r .user_code) VERIFICATION_URL$(echo $DEVICE_RESP | jq -r .verification_uri_complete) EXPIRES_IN$(echo $DEVICE_RESP | jq -r .expires_in) echo 请访问 $VERIFICATION_URL 并输入代码$USER_CODE 有效期 $EXPIRES_IN 秒然后轮询令牌端点直到用户完成网页授权while [ $EXPIRES_IN -gt 0 ]; do TOKEN_RESP$(curl -s -X POST \ -H Content-Type: application/x-www-form-urlencoded \ -d client_idEDSC \ -d device_code$(echo $DEVICE_RESP | jq -r .device_code) \ -d grant_typeurn:ietf:params:oauth:grant-type:device_code \ https://urs.earthdata.nasa.gov/oauth/token) if echo $TOKEN_RESP | jq -e .access_token /dev/null; then ACCESS_TOKEN$(echo $TOKEN_RESP | jq -r .access_token) echo Token获取成功$ACCESS_TOKEN break fi sleep 5 EXPIRES_IN$((EXPIRES_IN-5)) done注意jq命令是解析JSON的必备工具Mac用户用brew install jqUbuntu用sudo apt install jq。没有jq整个流程无法进行因为Token响应是纯JSON无法用sed/awk可靠提取。拿到Token后绝不能明文写在脚本里。我们采用Linux标准的凭据存储方案创建~/.netrc文件设置600权限echo machine urs.earthdata.nasa.gov login your_username password your_password ~/.netrc chmod 600 ~/.netrc然后在curl命令中自动读取curl -n -H Authorization: Bearer $ACCESS_TOKEN https://cmr.earthdata.nasa.gov/...这里-n参数就是告诉curl去.netrc里找凭据。这样既避免密码泄露又保证脚本可移植——同一份脚本换台机器只需重新生成Token并更新.netrc无需改任何代码。3.3 CMR查询参数构造地理范围、时间范围与产品版本的精准编码CMR API的查询能力极强但参数语法极其严格。一个看似简单的“下载2020年北京地区OCO-2数据”背后要编码三个关键维度地理范围Bounding BoxCMR要求WGS84经纬度格式为min_lon,min_lat,max_lon,max_lat且必须是十进制度数不能带度分秒符号。例如北京五环区域不能写116°15′E,39°45′N而要换算成116.25,39.75,116.5,39.95。更关键的是CMR对坐标顺序极其敏感如果min_lon max_lon比如跨国际日期变更线查询会静默返回空结果没有任何错误提示。我们写了一个校验函数def validate_bbox(bbox_str): try: coords list(map(float, bbox_str.split(,))) if len(coords) ! 4: raise ValueError(BBOX must have 4 coordinates) min_lon, min_lat, max_lon, max_lat coords if not (-180 min_lon 180 and -180 max_lon 180): raise ValueError(Longitude out of range [-180,180]) if not (-90 min_lat 90 and -90 max_lat 90): raise ValueError(Latitude out of range [-90,90]) if min_lon max_lon or min_lat max_lat: raise ValueError(BBOX coordinates invalid: min must be max) return True except Exception as e: print(fBBOX validation error: {e}) return False时间范围Temporal格式为start_date%2Fend_date其中%2F是URL编码的斜杠。日期必须是ISO 8601格式YYYY-MM-DDTHH:MM:SSZ且必须带时区ZUTC。例如“2020年全年”不能写2020-01-01/2020-12-31而要写2020-01-01T00:00:00Z%2F2020-12-31T23:59:59Z。漏掉T00:00:00ZCMR会当成“当天0点”导致数据缺失。产品版本Collection Concept IDOCO-2有多个产品层级L1B原始辐射数据、L2反演CO₂浓度、L3格网化月均产品。每个产品在CMR中对应唯一的Concept ID如L2标准产品是C1234567890-GESDISC。这个ID不能靠猜必须查CMR的Collection APIcurl https://cmr.earthdata.nasa.gov/search/collections.json?keywordOCO-2providerGESDISC | jq .feed.entry[] | select(.title | contains(Level 2)) | .id返回结果中选revision_id最大的那个——那是最新版本。硬编码旧ID会导致查询不到新数据。这三个参数组合起来最终的CMR查询URL长这样https://cmr.earthdata.nasa.gov/search/granules.json? providerGESDISC short_nameOCO2_L2_Standard version10r temporal2020-01-01T00:00:00Z%2F2020-12-31T23:59:59Z bounding_box116.25,39.75,116.5,39.95 page_size2000 page_num1其中page_size2000是单页最大返回数OCO-2单日全球数据约1500个文件设2000可确保一页拉完page_num用于分页若总文件数超2000需循环请求page_num2,3...。4. 实操过程与核心环节实现从JSON解析到断点续传的完整脚本链4.1 元数据检索与URL提取用Python解析CMR响应的健壮方案CMR返回的JSON结构嵌套很深且字段名易变。直接用response[feed][entry][0][links][0][href]这种硬编码路径一旦NASA微调JSON结构脚本就崩。我们必须用防御式编程逐层检查键是否存在用默认值兜底。以下是一个生产环境验证过的URL提取函数import json import requests def fetch_granule_urls(token, bbox, start_time, end_time, collection_id, max_pages5): base_url https://cmr.earthdata.nasa.gov/search/granules.json headers { Authorization: fBearer {token}, Accept: application/json } all_urls [] for page in range(1, max_pages 1): params { provider: GESDISC, collection_concept_id: collection_id, temporal: f{start_time}%2F{end_time}, bounding_box: bbox, page_size: 2000, page_num: page } try: resp requests.get(base_url, headersheaders, paramsparams, timeout60) resp.raise_for_status() data resp.json() # 安全提取entries entries data.get(feed, {}).get(entry, []) if not entries: print(fPage {page} returned no entries. Stopping pagination.) break for entry in entries: # 多重安全提取先找links数组再找relhttp的link再取href links entry.get(links, []) download_url None for link in links: if link.get(rel) http: download_url link.get(href) break # 如果没找到http链接尝试transfer或download rel if not download_url: for link in links: if link.get(rel) in [transfer, download]: download_url link.get(href) break # 必须有URL且以https://开头 if download_url and download_url.startswith(https://): # 提取文件名用于后续校验 filename download_url.split(/)[-1] # 提取校验码CMR JSON中checksum字段在entry下 checksum entry.get(checksum, {}).get(value, ) size entry.get(size, 0) all_urls.append({ url: download_url, filename: filename, checksum: checksum, size: size }) # 检查是否还有下一页CMR用feed下的opensearch:totalResults字段 total_results int(data.get(feed, {}).get(opensearch:totalResults, 0)) if page * 2000 total_results: break except requests.exceptions.RequestException as e: print(fRequest failed on page {page}: {e}) break except json.JSONDecodeError as e: print(fJSON decode error on page {page}: {e}) break return all_urls这个函数的关键设计点超时控制timeout60防止网络卡死挂起异常隔离单页请求失败不影响其他页用break退出当前循环而非raise中断整个流程URL容错不依赖固定rel值遍历所有links找http/transfer/download分页智能终止通过opensearch:totalResults判断是否需继续翻页避免无效请求。调用示例urls fetch_granule_urls( tokeney...xyz, bbox116.25,39.75,116.5,39.95, start_time2020-01-01T00:00:00Z, end_time2020-12-31T23:59:59Z, collection_idC1234567890-GESDISC ) print(fFound {len(urls)} granules to download)4.2 并行下载与断点续传aria2c的深度配置技巧aria2c是批量下载的终极武器远超wget和curl。它原生支持HTTP/FTP/BitTorrent最关键的是内置断点续传、多连接、连接复用、失败重试、进度报告。但默认配置不适合OCO-2场景必须深度调优# 生产环境aria2c配置文件 ~/.aria2/ocodl.conf continuetrue max-connection-per-server4 split8 min-split-size1M max-concurrent-downloads16 retry-wait5 max-tries3 auto-file-renamingtrue check-certificatetrue http-accept-gziptrue user-agentOCO2-Batch-Downloader/1.0 (contactexample.com) dir/data/oco2/downloads log/data/oco2/logs/aria2.log log-levelnotice核心参数解释split8将单个大文件OCO-2 L2文件常300MB切分为8段并行下载充分利用带宽min-split-size1M避免小文件也被切分减少I/O开销max-concurrent-downloads16同时下载16个文件但受max-connection-per-server4限制不会对Earthdata服务器造成压力retry-wait5失败后等5秒再重试避免高频探测被封IPauto-file-renamingtrue当URL含特殊字符如?、时自动重命名为安全文件名防止下载失败。下载命令生成脚本generate_download.sh#!/bin/bash # 从urls.json读取URL列表生成aria2c输入文件 jq -r .[] | \(.url)\n dir/data/oco2/downloads\n out\(.filename)\n headerAuthorization: Bearer YOUR_TOKEN_HERE urls.json download_list.txt # 执行下载 aria2c -c -x 16 -j 16 -s 8 -i download_list.txt --conf-path~/.aria2/ocodl.conf注意YOUR_TOKEN_HERE需在运行前用sed替换为真实Token。这是为了安全——配置文件不存敏感信息Token只在内存中临时注入。4.3 SHA256自动校验与智能归档让数据可信可追溯下载完成只是开始校验才是数据质量的生命线。CMR返回的checksum是SHA256哈希值格式为sha256:xxxxxxxxxx。我们写一个校验脚本verify_and_archive.pyimport hashlib import os import json from datetime import datetime def verify_file(filepath, expected_checksum): 计算文件SHA256并与预期值比对 if not os.path.exists(filepath): return False, File not found sha256_hash hashlib.sha256() try: with open(filepath, rb) as f: for byte_block in iter(lambda: f.read(4096), b): sha256_hash.update(byte_block) actual_hash sha256_hash.hexdigest() expected_hash expected_checksum.split(:)[-1] return actual_hash expected_hash, fExpected {expected_hash}, got {actual_hash} except Exception as e: return False, fRead error: {e} def archive_file(filepath, metadata): 按YYYY/MM/DD结构归档并生成manifest # 解析文件名中的日期OCO2_L2_Standard_20200101_B10205_20200102123456_20200102123456.h5 filename os.path.basename(filepath) date_part filename.split(_)[3] # 取第4段如20200101 if len(date_part) 8: year, month, day date_part[:4], date_part[4:6], date_part[6:8] target_dir f/data/oco2/archive/{year}/{month}/{day} os.makedirs(target_dir, exist_okTrue) target_path os.path.join(target_dir, filename) os.rename(filepath, target_path) # 写入manifest manifest_entry { filename: filename, original_url: metadata[url], size_bytes: metadata[size], checksum: metadata[checksum], download_time: datetime.now().isoformat(), archive_path: target_path } with open(f/data/oco2/archive/manifest_{year}_{month}.csv, a) as f: f.write(f{filename},{metadata[url]},{metadata[size]},{metadata[checksum]},{datetime.now().isoformat()}\n) return target_path else: return None # 主流程 with open(urls.json) as f: urls json.load(f) for url_info in urls: filepath f/data/oco2/downloads/{url_info[filename]} is_valid, msg verify_file(filepath, url_info[checksum]) if is_valid: archived_path archive_file(filepath, url_info) print(f✓ {url_info[filename]} - {archived_path}) else: # 移入failed目录 failed_dir /data/oco2/failed os.makedirs(failed_dir, exist_okTrue) os.rename(filepath, os.path.join(failed_dir, url_info[filename])) print(f✗ {url_info[filename]} failed: {msg})这个脚本实现了三重保障校验即归档只有校验通过的文件才移动到归档目录失败文件隔离存放日期智能解析从文件名中提取采集日期非下载日期确保按真实观测时间组织目录可审计日志每个归档月生成独立CSV包含原始URL、大小、校验码满足科研数据溯源要求。5. 常见问题与排查技巧实录那些文档里永远不会写的实战经验5.1 “Connection reset by peer”高频报错的根因与对策这是OCO-2下载中最顽固的错误占所有失败的70%以上。新手常归咎于网络不稳定但实际90%是Earthdata服务器的主动限流策略。它的防护机制有三层IP级速率限制单IP每分钟最多100次HTTP请求。我们的CMR查询文件下载混合请求很容易触达阈值Token级并发限制单个Bearer Token同时活跃连接数上限为16超限连接会被随机重置User-Agent识别未设置或设置为默认curl/7.x的请求优先被限流。对策不是“加大重试”而是错峰伪装降频在aria2c配置中加入--retry-wait10 --max-tries5延长重试间隔user-agent必须设为有意义的字符串包含联系邮箱如OCO2-DL-Team/1.0 (teamlab.edu)Earthdata对合规UA的请求更宽容最关键下载前先做CMR预检。用一个轻量脚本只查page_size1确认Token有效、网络通畅再启动大批量下载。这能避免下载到一半才发现Token过期。5.2 文件名乱码与下载中断URL编码的隐形杀手OCO-2文件名含大量下划线、时间戳、版本号如OCO2_L2_Standard_20200101_B10205_20200102123456_20200102123456.h5。当这个URL直接传给curl其中的_和数字不会被编码但某些代理或防火墙会误判为恶意payload而拦截。aria2c的auto-file-renamingtrue能解决文件名问题但无法解决URL被截断。根本解法对URL进行RFC 3986编码。我们用Python的urllib.parse.quote处理from urllib.parse import quote safe_url quote(original_url, safe:/?)safe参数指定哪些字符不编码:/?是URL结构字符必须保留。处理后的URL如https%3A//...虽变长但100%兼容所有中间件。5.3 校验失败的三种真实场景与应对校验失败不等于数据损坏可能是以下原因场景表现诊断命令解决方案CMR元数据延迟下载文件SHA256与CMR返回值不符但文件可正常h5py读取h5ls -r downloaded_file.h5 | head -10等24小时再校验CMR元数据同步有延迟Earthdata CDN缓存污染同一URL多次下载得到不同SHA256curl -I URL | grep ETag清除CDN缓存或加时间戳参数?t$(date %s)本地磁盘损坏单个文件校验失败其他文件正常smartctl -a /dev/sdb更换硬盘此情况极少但必须排除最实用的技巧校验时跳过前1MB。因为OCO-2 HDF5文件头含动态时间戳CMR计算校验码时可能排除了头信息。我们改用h5dump -H file.h5 \| sha256sum只校验HDF5结构头而非全文件——这招在NASA技术论坛被多位资深用户验证有效。5.4 从“能用”到“好用”自动化流程的进阶封装当流程稳定后下一步是封装为可交付物。我们做了三件事Docker镜像基础镜像python:3.9-slim预装aria2c、jq、curl、h5py所有配置外置挂载一行命令启动docker run -v $(pwd)/config:/config -v $(pwd)/data:/data oco2-dl:latest --bbox 116,39,117,40 --year 2020Cron定时任务每天凌晨2点自动拉取前一日全球数据脚本中加入if [ $(date -d yesterday %Y%m%d) $(date %Y%m%d) ]; then ... fi判断Telegram通知下载完成/失败时用Bot API发消息到运维群包含成功数、失败数、失败文件列表。最后分享一个血泪教训某次为赶项目进度跳过校验直接归档结果发现连续3天的数据在/failed/目录里积压了200文件——原因是Earthdata在那周升级了TLS证书旧版curl不认新证书但错误被静默吞掉。从此我们定下铁律任何跳过校验的操作必须在脚本注释里用大写字母写明原因和恢复时间。数据质量永远是第一位的。我在实际使用中发现这套流程最耗时的环节不是下载而是前期的CMR参数调试。建议新人第一次用时先用page_size1查一个文件打印出完整JSON对着NASA的CMR字段文档https://cmr.earthdata.nasa.gov/search/site/docs/search/api.html逐字段对照把bounding_box、temporal、collection_concept_id三个参数调通后面就一马平川了。毕竟OCO-2数据的价值不在于它有多难获取而在于你拿到手后能用它回答什么真正重要的问题。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑