Python自动下载影像?API接口怎么调?
很多 GIS 同学第一次做“Python自动下载影像?API接口怎么调?”时,最容易卡在三个地方:不知道去哪里申请接口、不清楚请求参数怎么写、下载回来的影像不知道是否能直接用于后续制图或分析。本文以常见的遥感影像与地图瓦片下载流程为例,讲清楚 Python 调用影像 API 的基本思路、可复用代码、常见错误和检查清单。

引言:Python自动下载影像适合解决什么问题
在日常 GIS 工作中,影像下载经常不是一次性的。比如你可能需要批量下载多个行政区的底图瓦片、按经纬度范围获取遥感影像、定期更新 WebGIS 背景影像,或者为机器学习样本准备一批影像切片。
如果手动在网页上一张张下载,不仅效率低,还容易出现范围不一致、分辨率不一致、文件命名混乱等问题。使用 Python 自动下载影像,可以把“请求接口、保存文件、记录日志、失败重试”做成稳定流程,适合批量和可复现的数据生产。
本文不绑定某一个商业平台,而是讲通用的 API 调用方法。你可以把示例思路套用到天地图、高德、Mapbox、ArcGIS REST、Sentinel Hub、NASA、地方政务影像服务或自建影像服务中。
背景:影像 API 接口一般有哪些类型
在写代码之前,先要判断你面对的是什么类型的影像接口。不同接口的参数差异很大,但底层逻辑基本相似:客户端通过 URL 或请求体告诉服务器“我要哪个范围、哪个图层、哪个级别、什么格式”,服务器返回图片或影像数据。
1. 地图瓦片 API
地图瓦片 API 最常见于 WebGIS 底图服务。它通常按照缩放级别 z、行列号 x/y 下载一张张小图片,例如 PNG 或 JPG。
典型 URL 形式如下:
https://example.com/tiles/{z}/{x}/{y}.png?key=你的API_KEY
这种接口适合下载底图瓦片、路网底图、影像底图切片。优点是速度快、容易并发;缺点是下载后通常是瓦片图片,不一定带完整地理参考信息,需要额外计算范围或拼接。
2. WMS 影像服务
WMS 是 Web Map Service,即网络地图服务。它通常通过 GetMap 请求返回指定范围的地图图片。
典型参数包括:
- LAYERS:图层名称。
- BBOX:请求范围。
- CRS 或 SRS:坐标参考系统。
- WIDTH / HEIGHT:输出图片宽高。
- FORMAT:返回格式,如 image/png 或 image/jpeg。
WMS 适合获取指定范围的地图快照,但很多 WMS 返回的是图片,不一定适合作为原始遥感数据进行严肃分析。
3. WCS 或遥感数据 API
WCS 是 Web Coverage Service,即网络覆盖服务,通常用于返回带空间参考和像元值的栅格数据。部分遥感平台也提供类似能力,可以按时间、云量、波段、范围下载 GeoTIFF。
如果你的目标是做 NDVI、地表温度、分类、变化检测等空间分析,应优先寻找能返回 GeoTIFF 或原始波段数据的接口,而不是只下载普通 JPG 图片。
原理:Python 调影像 API 的核心逻辑
Python自动下载影像的核心并不复杂,通常可以拆成五步:
- 准备 API Key、Token 或账号认证信息。
- 确定请求范围,例如经纬度矩形、行政区边界或瓦片行列号。
- 按照接口文档构造请求 URL 或 JSON 请求体。
- 使用 Python 发送 HTTP 请求并保存返回内容。
- 检查文件是否完整、坐标系是否正确、影像范围是否符合预期。
很多下载失败并不是 Python 代码问题,而是 API 参数、坐标系、权限、额度或服务限制问题。写代码时不要只看“有没有文件生成”,还要验证文件是否真的是你要的影像。
步骤:用 Python requests 调用影像 API 下载图片
下面先用最通用的 requests 示例说明接口怎么调。假设某个影像 API 支持通过 URL 返回一张图片,你只需要替换成自己平台的真实地址和参数。
步骤 1:安装必要库
pip install requests
如果后续要处理 GeoTIFF,可以再安装 rasterio;如果要读取矢量范围,可以安装 geopandas。
pip install rasterio geopandas
步骤 2:准备 API Key 和请求参数
不要把 API Key 直接写死在公开代码仓库中。更推荐使用环境变量或本地配置文件。下面为了演示清楚,先写成变量形式。
API_KEY = "替换为你的API_KEY"
params = {
"bbox": "116.30,39.85,116.45,39.95",
"crs": "EPSG:4326",
"width": 1024,
"height": 1024,
"format": "image/png",
"key": API_KEY
}
这里的 bbox 表示请求范围,常见顺序是 minx,miny,maxx,maxy,也就是最小经度、最小纬度、最大经度、最大纬度。但不同平台可能有差异,必须以官方文档为准。
步骤 3:发送请求并保存影像
import requests
from pathlib import Path
url = "https://example.com/api/image"
API_KEY = "替换为你的API_KEY"
params = {
"bbox": "116.30,39.85,116.45,39.95",
"crs": "EPSG:4326",
"width": 1024,
"height": 1024,
"format": "image/png",
"key": API_KEY
}
out_dir = Path("download_images")
out_dir.mkdir(exist_ok=True)
out_file = out_dir / "beijing_demo.png"
response = requests.get(url, params=params, timeout=60)
print("请求地址:", response.url)
print("状态码:", response.status_code)
print("返回类型:", response.headers.get("Content-Type"))
if response.status_code == 200 and "image" in response.headers.get("Content-Type", ""):
out_file.write_bytes(response.content)
print("下载完成:", out_file)
else:
print("下载失败:")
print(response.text[:1000])
这段代码的重点不是下载本身,而是保留了三个排错信息:请求地址、状态码、返回类型。很多接口请求失败时不会返回图片,而是返回 JSON 或 HTML 错误信息。如果不检查 Content-Type,你可能会把错误页面保存成 .png 文件。
步骤 4:给请求增加重试机制
批量下载影像时,网络波动、接口限流、临时 502 都很常见。建议加入简单重试机制。
import time
import requests
from pathlib import Path
def download_image(url, params, out_file, max_retry=3):
for i in range(max_retry):
try:
r = requests.get(url, params=params, timeout=60)
content_type = r.headers.get("Content-Type", "")
if r.status_code == 200 and "image" in content_type:
Path(out_file).write_bytes(r.content)
return True
print(f"第 {i + 1} 次失败,状态码:{r.status_code},类型:{content_type}")
print(r.text[:300])
except requests.RequestException as e:
print(f"第 {i + 1} 次请求异常:{e}")
time.sleep(2)
return False
实际项目中还应记录失败 URL,方便后续补下载。不要无限重试,否则可能触发平台风控或浪费大量时间。
步骤:按瓦片行列号批量下载影像
如果你使用的是瓦片 API,通常需要根据经纬度和缩放级别计算瓦片 x、y。WebGIS 常用的瓦片坐标系是 Web Mercator,对应 EPSG:3857。
经纬度转瓦片行列号
import math
def lonlat_to_tile(lon, lat, zoom):
lat_rad = math.radians(lat)
n = 2 ** zoom
x = int((lon + 180.0) / 360.0 * n)
y = int((1.0 - math.log(math.tan(lat_rad) + 1 / math.cos(lat_rad)) / math.pi) / 2.0 * n)
return x, y
计算范围内的瓦片并下载
import requests
from pathlib import Path
import math
import time
def lonlat_to_tile(lon, lat, zoom):
lat_rad = math.radians(lat)
n = 2 ** zoom
x = int((lon + 180.0) / 360.0 * n)
y = int((1.0 - math.log(math.tan(lat_rad) + 1 / math.cos(lat_rad)) / math.pi) / 2.0 * n)
return x, y
def download_tile(tile_url_template, z, x, y, out_path, api_key=None):
url = tile_url_template.format(z=z, x=x, y=y, key=api_key or "")
r = requests.get(url, timeout=30)
if r.status_code == 200 and "image" in r.headers.get("Content-Type", ""):
Path(out_path).write_bytes(r.content)
return True
print("失败:", r.status_code, url)
return False
bbox = {
"min_lon": 116.30,
"min_lat": 39.85,
"max_lon": 116.45,
"max_lat": 39.95
}
zoom = 15
api_key = "替换为你的API_KEY"
tile_url_template = "https://example.com/tiles/{z}/{x}/{y}.png?key={key}"
x_min, y_max = lonlat_to_tile(bbox["min_lon"], bbox["min_lat"], zoom)
x_max, y_min = lonlat_to_tile(bbox["max_lon"], bbox["max_lat"], zoom)
out_dir = Path(f"tiles_z{zoom}")
out_dir.mkdir(exist_ok=True)
for x in range(x_min, x_max + 1):
for y in range(y_min, y_max + 1):
out_file = out_dir / f"{zoom}_{x}_{y}.png"
if out_file.exists():
continue
ok = download_tile(tile_url_template, zoom, x, y, out_file, api_key)
print(z if False else zoom, x, y, ok)
time.sleep(0.1)
上面代码适合理解瓦片下载逻辑。实际生产中要注意平台服务条款,不要在没有授权的情况下大规模抓取在线底图。很多商业地图服务明确限制离线缓存和批量下载。
步骤:调用 ArcGIS REST Export Map 接口下载指定范围影像
很多单位内部或政府平台发布的是 ArcGIS Server 服务。它常见的影像或地图服务接口可以通过 export 请求获取图片。
典型请求地址类似:
https://server.example.com/arcgis/rest/services/xxx/MapServer/export
Python 示例:
import requests
from pathlib import Path
export_url = "https://server.example.com/arcgis/rest/services/xxx/MapServer/export"
params = {
"f": "image",
"bbox": "116.30,39.85,116.45,39.95",
"bboxSR": "4326",
"imageSR": "4326",
"size": "1200,1200",
"format": "png32",
"transparent": "false"
}
r = requests.get(export_url, params=params, timeout=60)
print(r.url)
print(r.status_code)
print(r.headers.get("Content-Type"))
if r.status_code == 200 and "image" in r.headers.get("Content-Type", ""):
Path("arcgis_export.png").write_bytes(r.content)
else:
print(r.text[:1000])
如果服务需要 Token,需要先调用登录或 token 接口获取 token,再把 token 作为参数带入 export 请求。不同机构配置不一样,应以该服务的 REST 页面说明为准。
步骤:下载 GeoTIFF 后如何验证是否可用
如果 API 返回的是 GeoTIFF,建议下载后用 rasterio 检查坐标系、范围、像元大小和波段数量。
import rasterio
tif_path = "download.tif"
with rasterio.open(tif_path) as src:
print("坐标系:", src.crs)
print("范围:", src.bounds)
print("宽高:", src.width, src.height)
print("波段数:", src.count)
print("像元大小:", src.res)
print("数据类型:", src.dtypes)
如果 src.crs 为空,说明影像可能没有正确的空间参考。这样的文件直接加载到 QGIS 或 ArcGIS Pro 中,可能无法和矢量边界、道路、行政区正确叠加。
常见坑:Python自动下载影像为什么经常失败
1. API Key 没有权限或额度用完
很多影像 API 分为免费、试用、商业和内部权限。能打开网页地图,不代表你有批量下载接口权限。遇到 401、403、429 状态码时,优先检查权限、额度和调用频率。
- 401:通常是未认证或认证信息错误。
- 403:通常是没有访问权限。
- 429:通常是请求过快或超出额度。
- 500 / 502 / 503:可能是服务端异常,也可能是请求范围或参数过大。
2. bbox 顺序写错
最常见的 bbox 顺序是 minx,miny,maxx,maxy,但有些接口文档会用 west,south,east,north,有些平台在特定坐标系下还会出现经纬顺序差异。下载结果为空白或偏到别处时,第一步就应检查 bbox。
3. 坐标系不匹配
你传入的是 EPSG:4326 经纬度,但服务可能要求 EPSG:3857 米制坐标;或者你传入了投影坐标,却告诉接口是 4326。坐标系错了,下载影像会出现位置偏移、范围异常、空白图等问题。
4. 请求尺寸过大
很多 WMS、ArcGIS REST 或影像 API 都限制单次请求的 WIDTH、HEIGHT 或像素总数。比如你请求一个很大的范围,同时要求 10000 x 10000 像素,接口可能直接失败。更稳妥的做法是切块下载。
5. 把地图截图当成可分析遥感数据
PNG、JPG 瓦片适合显示,不一定适合遥感分析。它们可能经过拉伸、压缩、重采样、标注叠加,像元值不再代表真实反射率或原始波段值。如果要做定量分析,应下载 GeoTIFF 或平台提供的原始产品。
方法比较:不同影像下载接口该怎么选
| 接口类型 | 适合场景 | 优点 | 限制 |
|---|---|---|---|
| 瓦片 API | WebGIS 底图、影像切片、快速浏览 | 速度快,结构简单,易于缓存 | 通常不带完整地理参考,不适合严肃遥感分析 |
| WMS GetMap | 按范围获取地图图片、专题图输出 | 参数标准化,很多 GIS 服务器支持 | 多为渲染图片,像元值可能不可用于分析 |
| ArcGIS REST Export | 单位内部 ArcGIS Server 服务下载 | 接口页面清晰,参数容易调试 | 常受权限、Token、最大图片尺寸限制 |
| WCS / 遥感数据 API | 下载 GeoTIFF、波段数据、分析级影像 | 更适合空间分析和遥感计算 | 参数复杂,通常需要理解产品、时间、波段和投影 |
| STAC API | 检索和下载遥感影像目录 | 适合按时间、范围、云量筛选影像资产 | 需要再根据 asset 链接下载具体文件 |
如果只是做底图展示,瓦片 API 或 WMS 足够。如果要做 NDVI、分类、变化检测,优先选择 WCS、STAC 或遥感平台提供的 GeoTIFF 下载接口。
检查清单:写 Python 影像下载脚本前先确认这些项
- 接口是否允许批量下载或离线缓存。
- 是否已经申请 API Key、Token 或账号权限。
- 请求范围 bbox 的坐标系和顺序是否与文档一致。
- 返回格式是 PNG、JPG,还是 GeoTIFF。
- 单次请求最大宽高、最大范围、最大像素数是多少。
- 是否需要设置 User-Agent、Referer 或其他请求头。
- 是否有频率限制,是否需要 sleep 或队列控制。
- 下载失败时是否记录状态码、URL 和错误内容。
- 下载后是否检查文件大小、坐标系、范围和波段。
- 是否把 API Key 放在安全位置,避免提交到公开仓库。
FAQ:Python自动下载影像 API接口常见问题
Q1:Python自动下载影像一定要用 API Key 吗?
不一定。公开 WMS、部分开放数据平台可能不需要 API Key。但商业地图、遥感平台、单位内网服务通常需要 Key、Token、Cookie 或账号认证。是否需要认证取决于服务方配置。
Q2:为什么浏览器能打开,Python requests 下载却失败?
常见原因包括接口需要登录态、请求头校验、Referer 限制、Token 过期、跨域配置不同,或者浏览器访问的是网页而不是实际影像接口。建议先在浏览器开发者工具中查看真实请求 URL,再用 Python 复现。
Q3:下载影像后加载到 QGIS 位置不对怎么办?
先检查影像是否带坐标系。如果是普通 PNG 或 JPG,通常没有地理参考,需要世界文件或瓦片范围信息。如果是 GeoTIFF,用 rasterio 或 QGIS 图层属性检查 CRS、范围和像元大小。位置偏移时重点检查 EPSG:4326 与 EPSG:3857 是否混用。
Q4:Python 调影像 API 下载很慢,怎么优化?
可以从四方面优化:合理切块、控制并发、跳过已下载文件、失败重试。并发不要盲目开太大,否则可能触发限流。对于瓦片下载,建议先小范围测试,再扩大范围。
Q5:可以把在线地图瓦片全部下载下来离线使用吗?
技术上可以批量请求瓦片,但是否允许取决于服务条款和授权范围。很多商业底图禁止未经授权的大规模缓存、复制和离线分发。正式项目中应使用合规的数据源或购买授权。
Q6:影像 API 返回 JSON,不返回图片是什么原因?
通常是请求参数错误、认证失败、额度不足或服务端返回错误信息。代码中要检查 Content-Type。如果返回 application/json,应打印 JSON 内容,根据错误码和 message 排查,而不是直接保存成图片。
结论:先调通一个小范围,再做批量自动下载
Python自动下载影像的关键不是写一个复杂脚本,而是先把 API 接口参数、坐标系、权限和返回格式弄清楚。推荐的工作方式是:先用一个很小的 bbox 调通请求,再检查输出文件,确认能在 QGIS 或 ArcGIS Pro 中正确加载,最后再扩展到批量下载。
如果你的目标是 WebGIS 展示,优先理解瓦片 API、WMS 和 ArcGIS REST Export;如果你的目标是遥感分析,优先寻找 GeoTIFF、WCS、STAC 或专业遥感平台接口。把接口类型选对,后面的 Python 自动化才会稳定可靠。