
本文详解如何正确抓取 arcgis 共享页面中通过 api 动态生成的 .zip 下载链接,并实现稳定批量下载,解决 beautifulsoup 无法解析 javascript 渲染内容的常见问题。
ArcGIS Online 页面(如 arcgis.com/home/item.html)通常不直接在 HTML 源码中硬编码下载链接,而是通过前端 JavaScript 调用 REST API 异步加载数据(例如要素服务、附件或元数据中的 URL 字段)。因此,使用 requests + BeautifulSoup 直接解析初始 HTML 将无法获取真实 ZIP 链接——这正是你遇到“打印 links 为空”和“无文件下载”的根本原因:目标链接根本不存在于静态 HTML 中,而是在后续 AJAX 请求返回的 JSON 数据里。
要成功下载,必须逆向分析页面的数据加载逻辑。以你提供的链接为例,其 ZIP 文件实际存储在 Azure Blob(如 dasc.blob.core.windows.net)中,URL 保存在 ArcGIS Feature Service 的属性字段(如 "ContoursURL")中。正确路径是:
- 提取 item ID → 2. 调用 ArcGIS Sharing API 获取组织 ID → 3. 构造 FeatureServer 查询 URL → 4. 请求 JSON 数据并提取 ContoursURL 字段 → 5. 过滤并下载 .zip 文件
以下是可直接运行的增强版下载脚本,已整合错误处理、进度提示、目录自动创建与文件名安全处理:
import os
import re
import requests
from urllib.parse import urlparse, unquote
def extract_item_id(url):
"""从 ArcGIS item URL 中安全提取 ID"""
match = re.search(r"id=([a-f0-9]{32})", url)
if not match:
raise ValueError("Invalid ArcGIS item URL: missing or malformed 'id' parameter")
return match.group(1)
def download_zip_files(base_url, download_dir="downloads", max_files=100):
os.makedirs(download_dir, exist_ok=True)
# Step 1: Get item ID
item_id = extract_item_id(base_url)
print(f"✅ Extracted item ID: {item_id}")
# Step 2: Get orgId via Sharing REST API
api_url = f"https://www.arcgis.com/sharing/rest/content/items/{item_id}?f=json"
try:
resp = requests.get(api_url, timeout=10)
resp.raise_for_status()
data = resp.json()
org_id = data["orgId"]
print(f"✅ Retrieved orgId: {org_id}")
except Exception as e:
raise RuntimeError(f"Failed to fetch orgId: {e}")
# Step 3: Query Feature Server for ZIP URLs
feature_url = f"https://services1.arcgis.com/{org_id}/arcgis/rest/services/Statewide_Contours/FeatureServer/0/query"
params = {
"f": "json",
"where": "1=1",
"returnGeometry": "false",
"outFields": "ContoursURL",
"resultRecordCount": str(max_files),
"orderByFields": "FID ASC"
}
try:
resp = requests.get(feature_url, params=params, timeout=15)
resp.raise_for_status()
data = resp.json()
if "features" not in data or not data["features"]:
print("⚠️ No features found. Check service URL or permissions.")
return
zip_urls = []
for feat in data["features"]:
url = feat["attributes"].get("ContoursURL")
if url and url.lower().endswith(".zip"):
zip_urls.append(url)
print(f"? Found {len(zip_urls)} ZIP URLs")
# Step 4: Download each ZIP
for i, url in enumerate(zip_urls, 1):
try:
# Safe filename from URL (handle encoded chars & paths)
parsed = urlparse(url)
filename = os.path.basename(unquote(parsed.path))
filepath = os.path.join(download_dir, filename)
print(f"[{i}/{len(zip_urls)}] Downloading {filename}...")
with requests.get(url, stream=True, timeout=60) as r:
r.raise_for_status()
with open(filepath, "wb") as f:
for chunk in r.iter_content(chunk_size=8192):
f.write(chunk)
print(f"✅ Saved to {filepath}")
except Exception as e:
print(f"❌ Failed to download {url}: {e}")
except Exception as e:
raise RuntimeError(f"Failed to query feature service: {e}")
# 使用示例(替换为你的真实 URL)
if __name__ == "__main__":
ARC_URL = "https://www.arcgis.com/home/item.html?id=a5248eb6412648ec8cbd46838adb86e9#data"
download_zip_files(ARC_URL, download_dir="ks_contours_zips")? 关键注意事项:
- 不要修改 download_dir 为含空格路径(如 "C: My Drive"):Windows 路径中空格需转义或使用原始字符串,更推荐使用绝对路径如 r"C:\Users\Name\Downloads\zips" 或相对路径 "./downloads"。
- 网络权限与 CORS 无关:该方案绕过浏览器限制,直接调用公开 REST API,但需确保目标服务未启用 IP 白名单或令牌认证(本例为公开数据,无需 token)。
- 稳定性增强:脚本加入 timeout、raise_for_status() 和 stream=True,避免大文件内存溢出及请求挂起。
- 扩展性提示:若字段名非 "ContoursURL",请先 print(data['features'][0]['attributes']) 查看实际键名;若服务层级不同(如 FeatureServer/1),需调整 URL 路径。
通过理解 ArcGIS 的数据分发机制并转向 API 交互式抓取,你将彻底摆脱“BeautifulSoup 找不到链接”的困境,实现健壮、可维护的批量下载流程。










