browser-harness 实战指南:World Bank Open Data API 抓取与数据提取

  • 浏览器控制
  • GUI 自动化
  • AI Agent
  • 人工智能
  • MCP 服务
  • AI 技能

【免费下载链接】browser-harness

Browser Harness | Self-healing harness that enables LLMs to complete any task.

项目地址: https://gitcode.com/gh_mirrors/br/browser-harness
点击查看 免费下载

世界银行开放数据(World Bank Open Data)通过 https://api.worldbank.org/v2 提供全球发展指标的免费 REST API,无需 API Key、无需登录、无需浏览器即可获取 GDP、人口、失业率等 29000+ 个指标。本文基于 browser-harness 仓库中的领域技能文档 world-bank/scraping.md,结合 helpers.py 中 http_get 的源码实现,完整讲解该 API 的响应结构、常用抓取工作流、URL 参数体系与全部踩坑点,读完即可在 browser-harness 环境中直接复制运行。

在 browser-harness 中调用 http_get

World Bank API 是纯 HTTP 接口,不需要启动浏览器。browser-harness 的领域技能文档(Domain Skills)遵循"能用普通 HTTP 请求解决就不用浏览器"的原则——src/browser_harness/SKILL.md 明确指出:公开页面、API、文档这类一次 http_get 就能读到的内容,应直接使用 HTTP 抓取,浏览器留给需要点击、输入、登录态、JS 渲染或反爬对抗的场景。

在 browser-harness 中,脚本通过 heredoc 方式交给 CLI 执行,helpers.py 中的核心原语会被自动导入进这些脚本(见 AGENTS.md 与 src/browser_harness/SKILL.md):

browser-harness <<'PY'
from helpers import http_get
import json

raw = http_get("https://api.worldbank.org/v2/country/US/indicator/NY.GDP.MKTP.CD?format=json")
print(raw[:200])
PY

http_get 定义在 src/browser_harness/helpers.py,签名与行为如下:

def http_get(url, headers=None, timeout=20.0):
    """Pure HTTP — no browser. Use for static pages / APIs. Wrap in ThreadPoolExecutor for bulk.

    When BROWSER_USE_API_KEY is set, routes through the fetch-use proxy (handles bot
    detection, residential proxies, retries). Falls back to local urllib otherwise."""

源码级要点:

  • 纯 HTTP 实现,不经过浏览器与 CDP,适用于静态页面与 API;
  • 默认 timeout=20.0 秒,可通过 timeout 参数调整;
  • 默认携带 User-Agent: Mozilla/5.0 与 Accept-Encoding: gzip,并自动解压 gzip 响应,返回解码后的文本;
  • 可传入 headers 字典覆盖或追加请求头(如自定义 User-Agent);
  • 当环境变量 BROWSER_USE_API_KEY 存在时,会尝试路由到 fetch_use 代理(处理反爬、住宅代理与重试),否则回退到本地 urllib;
  • 适合大批量抓取时配合 ThreadPoolExecutor 并发调用(文档中原生给出了并行抓取示例)。

第一步必读:响应永远是一个 2 元素 JSON 数组

World Bank API 的每个响应都是一个 2 元素 JSON 数组 [metadata, data]。索引 0 是分页元数据,索引 1 是数据数组。这是最大的坑——json.loads(raw) 得到的是 list 而不是 dict:

from helpers import http_get
import json

raw = http_get("https://api.worldbank.org/v2/country/US/indicator/NY.GDP.MKTP.CD?format=json")
d = json.loads(raw)
meta = d[0]   # {"page": 1, "pages": 2, "per_page": 50, "total": 66, ...}
rows = d[1]   # list of data records

同时,务必在 URL 末尾追加 ?format=json——API 默认返回 XML,不带该参数时 json.loads 会直接失败。

常用工作流

以下工作流的输出均为文档作者于 2026-04-18 实测确认,可直接复制运行。

单国家、单指标

from helpers import http_get
import json

raw = http_get("https://api.worldbank.org/v2/country/US/indicator/NY.GDP.MKTP.CD?format=json")
d = json.loads(raw)
meta, rows = d[0], d[1]

for r in rows:
    if r["value"] is not None:   # recent years often have null values
        print(r["date"], r["value"])
# Confirmed output (2026-04-18):
# 2024 28750956130731.2
# 2023 27292170793214.4
# 2022 25604848907611.0
# ...

注意循环中的 if r["value"] is not None 过滤:近年数据(如 2025)以及数据稀疏的国家经常返回 null 值。

最近 N 个值(mrv 参数)

mrv(most recent values)会跳过空值年份,返回最近 N 个非临时(non-provisional)数据点:

from helpers import http_get
import json

raw = http_get(
    "https://api.worldbank.org/v2/country/US/indicator/NY.GDP.MKTP.CD"
    "?format=json&mrv=5"
)
d = json.loads(raw)
for r in d[1]:
    print(r["date"], r["value"])
# Confirmed output (2026-04-18):
# 2024 28750956130731.2
# 2023 27292170793214.4
# 2022 25604848907611.0
# 2021 23315080560000.0
# 2020 21060473613000.0

多国家、日期区间

国家代码在 URL 路径中用分号分隔,日期区间用 date=YYYY:YYYY:

from helpers import http_get
import json

raw = http_get(
    "https://api.worldbank.org/v2/country/US;CN;GB/indicator/SP.POP.TOTL"
    "?format=json&date=2000:2023&per_page=100"
)
d = json.loads(raw)
meta, rows = d[0], d[1]
print(f"Total records: {meta['total']}, pages: {meta['pages']}")

for r in rows:
    print(r["country"]["value"], r["date"], r["value"])
# Confirmed: returns 8 records per page (50 default), date range honored exactly
# Countries: ['China', 'United States', 'United Kingdom']
# Dates: ['2000', '2001', ..., '2023']

所有国家、仅取最新值

mrv=1 配合 per_page=1000(API 单页上限),一次调用即可拿回全部 266 个实体的最新值:

from helpers import http_get
import json

raw = http_get(
    "https://api.worldbank.org/v2/country/all/indicator/NY.GDP.PCAP.CD"
    "?format=json&mrv=1&per_page=1000"
)
d = json.loads(raw)
meta, rows = d[0], d[1]
print(f"Countries returned: {len(rows)}")  # 266 (includes aggregates)

# Filter out regional aggregates — they have no iso2Code or have aggregate ids
countries_only = [r for r in rows if len(r["country"]["id"]) == 2]
for r in sorted(countries_only, key=lambda x: -(x["value"] or 0))[:5]:
    print(r["country"]["value"], r["date"], f"${r['value']:,.0f}")
# Confirmed output (2026-04-18):
# Luxembourg 2024 $135,605
# Norway 2024 $105,056
# ...

完整分页(抓取所有页)

通过 meta["pages"] 驱动循环,逐页追加:

from helpers import http_get
import json

def fetch_all_pages(base_url):
    """Fetch all pages of a World Bank API endpoint."""
    all_rows = []
    page = 1
    while True:
        url = f"{base_url}&page={page}" if "?" in base_url else f"{base_url}?page={page}"
        d = json.loads(http_get(url))
        meta, rows = d[0], d[1]
        all_rows.extend(rows)
        if page >= meta["pages"]:
            break
        page += 1
    return all_rows

# Example: all US GDP data (66 years, 2 pages)
rows = fetch_all_pages(
    "https://api.worldbank.org/v2/country/US/indicator/NY.GDP.MKTP.CD"
    "?format=json&per_page=50"
)
print(f"Total rows: {len(rows)}")  # 66
non_null = [(r["date"], r["value"]) for r in rows if r["value"] is not None]
print(f"Non-null: {len(non_null)}, range: {non_null[-1][0]}–{non_null[0][0]}")

指标列表(发现可用指标)

from helpers import http_get
import json

raw = http_get("https://api.worldbank.org/v2/indicator?format=json&per_page=50")
d = json.loads(raw)
meta = d[0]
print(f"Total indicators: {meta['total']}, pages: {meta['pages']}")
# Confirmed: 29,511 indicators across 591 pages

for ind in d[1][:3]:
    print(ind["id"], "-", ind["name"])
# 1.0.HCount.1.90usd - Poverty Headcount ($1.90 a day)
# ...

按主题获取指标

from helpers import http_get
import json

# Topic 3 = Economy & Growth
raw = http_get("https://api.worldbank.org/v2/topic/3/indicator?format=json&per_page=50")
d = json.loads(raw)
print(f"Economy & Growth indicators: {d[0]['total']}")  # 306

for ind in d[1][:5]:
    print(ind["id"], "-", ind["name"])

国家元数据

from helpers import http_get
import json

raw = http_get("https://api.worldbank.org/v2/country/US?format=json")
d = json.loads(raw)
c = d[1][0]
print(c["name"], c["capitalCity"], c["region"]["value"], c["incomeLevel"]["value"])
# United States  Washington D.C.  North America  High income

# Filter countries by income level
raw = http_get("https://api.worldbank.org/v2/country?format=json&incomeLevel=LIC&per_page=300")
d = json.loads(raw)
print(f"Low-income countries: {d[0]['total']}")  # 25

主题列表

from helpers import http_get
import json

raw = http_get("https://api.worldbank.org/v2/topics?format=json")
d = json.loads(raw)
for t in d[1]:
    print(t["id"], t["value"])
# 1  Agriculture & Rural Development
# 2  Aid Effectiveness
# 3  Economy & Growth
# ... (21 topics total)

多指标并行抓取(ThreadPoolExecutor)

http_get 本身是阻塞式纯网络调用,适合放进线程池做批量并发。这是 helpers.py docstring 中明确推荐的批量用法:

from helpers import http_get
from concurrent.futures import ThreadPoolExecutor
import json

INDICATORS = {
    "NY.GDP.MKTP.CD": "GDP (current US$)",
    "SP.POP.TOTL": "Population",
    "NY.GDP.PCAP.CD": "GDP per capita",
}

def fetch_indicator(ind_id):
    url = (
        f"https://api.worldbank.org/v2/country/US/indicator/{ind_id}"
        f"?format=json&mrv=5"
    )
    d = json.loads(http_get(url))
    return ind_id, d[1]

with ThreadPoolExecutor(max_workers=3) as ex:
    results = dict(ex.map(lambda i: fetch_indicator(i), INDICATORS))

for ind_id, rows in results.items():
    latest = next((r for r in rows if r["value"] is not None), None)
    if latest:
        print(f"{INDICATORS[ind_id]}: {latest['date']} = {latest['value']:,.2f}")

URL 参考

基础 URL

https://api.worldbank.org/v2

HTTP 会 302 重定向到 HTTPS。请始终直接使用 HTTPS,避免一次多余往返。

端点模式

端点说明
/country/{code}/indicator/{id}单国家 + 单指标时间序列
/country/{c1};{c2};{c3}/indicator/{id}多国家(分号分隔)
/country/all/indicator/{id}所有国家
/country/{code}国家元数据
/country所有国家元数据(可过滤)
/indicator全部指标列表
/indicator/{id}单指标元数据
/topic/{id}/indicator某主题下的指标
/topics全部主题

查询参数

参数取值说明
formatjson、xml(默认)始终设置 format=json
per_page整数,默认 50,最大 1000批量抓取设大更快
page整数,默认 1分页
date2020、2000:2023单年或冒号分隔区间
mrv整数最近 N 个非空值
gapfillY与 mrv 配合时向前填充空值
incomeLevelLIC、MIC、HIC、LMC、UMC按收入水平过滤国家
regionEAS、ECS、LAC、MEA、NAC、SAS、SSF按区域过滤国家

常用指标 ID(2026-04-18 实测可用)

ID名称
NY.GDP.MKTP.CDGDP (current US$)
NY.GDP.PCAP.CDGDP per capita (current US$)
SP.POP.TOTLPopulation, total
SL.UEM.TOTL.ZSUnemployment (% of labor force)
FP.CPI.TOTL.ZGInflation, consumer prices (%)
NE.EXP.GNFS.ZSExports of goods and services (% of GDP)
SP.DYN.LE00.INLife expectancy at birth
SE.ADT.LITR.ZSLiteracy rate, adult total (%)
EG.USE.PCAP.KG.OEEnergy use per capita (kg of oil equiv.)

查找更多指标:https://api.worldbank.org/v2/indicator?format=json&per_page=50&page=N

国家代码(ISO2)

使用标准 ISO 3166-1 alpha-2 代码:US、CN、GB、DE、JP、IN、BR 等。特殊值 all 表示所有国家。

响应结构

每个端点都返回 2 元素数组,元数据元素包含分页信息,第二元素为数据记录列表:

[
  {
    "page": 1,
    "pages": 2,
    "per_page": 50,
    "total": 66,
    "sourceid": "2",
    "lastupdated": "2026-04-08"
  },
  [
    {
      "indicator": {"id": "NY.GDP.MKTP.CD", "value": "GDP (current US$)"},
      "country": {"id": "US", "value": "United States"},
      "countryiso3code": "USA",
      "date": "2024",
      "value": 28750956130731.2,
      "unit": "",
      "obs_status": "",
      "decimal": 0
    },
    ...
  ]
]

国家元数据端点返回同样的 2 元素结构,但索引 1 中是 country 对象而非指标行。

Gotchas:全部踩坑点汇总

以下条目全部来自领域技能文档中的实测经验,是抓取 World Bank API 最容易出错的地方:

  • 响应永远是 2 元素数组,不是 dict。 d = json.loads(raw) 得到的是 list。d[0] 是分页元数据,d[1] 是数据列表。直接访问 d["page"] 会抛 TypeError。这是最常见的错误。

  • value 可能为 null。 近年份(如 2025)和数据稀疏国家经常返回 null。使用前务必检查 if r["value"] is not None;或用 mrv=N 让 API 自动跳过空值。

  • 务必追加 ?format=json。 默认响应格式是 XML。不带该参数会得到无法被 json.loads 解析的 XML 字符串。

  • all/indicator/{id} 包含区域聚合。 "所有国家"端点返回 266 个实体,包含 "Africa Eastern and Southern"(id: "ZH")这类区域聚合。用 len(r["country"]["id"]) == 2 过滤出真实国家(ISO2 代码恒为 2 字符)。

  • 多国家用 URL 路径中的分号,不是查询字符串。 正确写法是 country/US;CN;GB/indicator/...,?countries=US,CN,GB 或逗号分隔都无效。

  • HTTP 302 将 HTTP 重定向到 HTTPS。 直接使用 https:// 避免额外一次往返。

  • 元数据中的 per_page 有时是字符串、有时是整数。 某些端点返回 "per_page": "50"(字符串),另一些返回 "per_page": 50(整数)。不要直接 == 比较,先转换:int(meta["per_page"])。

  • 无效国家代码返回错误对象而非 2 元素数组。 错误响应形如 [{"message": [{"id": "120", "key": "Invalid value", ...}]}]——只有 1 个元素的错误 dict。访问 d[1] 前先检查 isinstance(d[0], dict) and "message" in d[0]。

  • mrv + gapfill=Y 会把最新值向前填充到未来年份。 若 2024 是最新数据点且设了 mrv=3,gapfill=Y 会返回 2025(当前年份)并复制 2024 的值。适合"当前值"查询,但填充出来的日期具有误导性。

  • API 未文档化速率限制,但稳定 3 req/s 是安全的。 接口可承受突发请求(文档实测 ThreadPoolExecutor(max_workers=3) 并行无压力)。若要爬取数千个指标,建议页间加 time.sleep(0.5)。

  • date 区间返回的记录按最新在前排序。 区间内结果按年份降序。需要升序时抓取后自行排序:sorted(rows, key=lambda r: r["date"])。

  • 指标 ID 区分大小写。 ny.gdp.mktp.cd 会返回错误;必须使用大写点分形式 NY.GDP.MKTP.CD。

小结

World Bank Open Data API 是 browser-harness 中"纯 HTTP 即可完成"类任务的典型代表:无需浏览器、无需认证,仅靠 http_get 与 json.loads 即可完成从单指标时间序列到全量 29511 个指标的各类抓取。核心纪律只有三条:响应是 2 元素数组(d[0] 元数据 / d[1] 数据)、始终带 ?format=json、对 null 值做好防御。在此基础上结合 mrv、per_page=1000 与 ThreadPoolExecutor,即可高效、稳健地构建全球经济数据管道。相关领域技能文件见 world-bank/scraping.md,http_get 底层实现见 src/browser_harness/helpers.py。

  • 浏览器控制
  • GUI 自动化
  • AI Agent
  • 人工智能
  • MCP 服务
  • AI 技能

【免费下载链接】browser-harness

Browser Harness | Self-healing harness that enables LLMs to complete any task.

项目地址: https://gitcode.com/gh_mirrors/br/browser-harness
点击查看 免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值