搜索之后用
GET /api/v1/fetch?url=拉取干净正文,适合「点开阅读/总结」流水线。失败的 fetch 计费以文档为准。
先看清成品
- mock 返回 title/word_count/content。
- 1 项 pytest 通过。
合规提醒:Andi 回答与搜索结果可能不完整,重要信息须打开原文核对。API 密钥仅放服务端;
metrics.cost_dollars反映计费,注意配额。遵守目标站点 robots/使用条款,勿用 fetch 做未授权批量抓取。本文不编造价格表。
运行说明:无正式
ANDI_API_KEY。A/B 用 mock;对官方api.andiai.com无效密钥实测 HTTP 401。C 为参数本地校验。2026-10-23(Asia/Shanghai)pytest 通过。
做完之后你会得到什么?
| 成果 | 说明 |
|---|---|
andi_fetch.py |
Fetch 客户端 |
mock_andi.py |
假服务 |
test_107b.py |
测试 |
需要准备哪些环境?
| 项目 | 说明 |
|---|---|
| Python | 3.10+ |
| 环境变量 | ANDI_API_KEY |
关键事实
| 事实 | 来源 |
|---|---|
| Fetch 返回抽取后的页面内容 | 官方 API reference |
| 仅拉取你有权访问的公开页;勿绕过登录墙 | 合规 |
| 可与 search 结果里的 link 串联 | 实践 |
文件结构
107-andi-B-src/
├── andi_fetch.py
├── mock_andi.py
└── test_107b.py
第 1 步:客户端
#!/usr/bin/env python3
"""Andi Fetch API: GET /api/v1/fetch?url=... clean page content."""
from __future__ import annotations
import argparse, json, os, sys, urllib.error, urllib.parse, urllib.request
def fetch(base, key, url):
params = urllib.parse.urlencode({"url": url})
req = urllib.request.Request(
f"{base.rstrip('/')}/api/v1/fetch?{params}",
headers={"x-api-key": key},
method="GET",
)
try:
with urllib.request.urlopen(req, timeout=60) as resp:
return json.loads(resp.read().decode())
except urllib.error.HTTPError as e:
raise SystemExit(f"HTTP {e.code}: {e.read().decode(errors='replace')}") from e
def main(argv=None) -> int:
ap = argparse.ArgumentParser(description=__doc__)
ap.add_argument("url")
ap.add_argument("--base", default=os.environ.get("ANDI_BASE_URL", "https://api.andiai.com"))
ap.add_argument("--key", default=os.environ.get("ANDI_API_KEY", ""))
args = ap.parse_args(argv)
if not args.key:
print("设置 ANDI_API_KEY", file=sys.stderr); return 2
if not args.url.startswith(("http://", "https://")):
print("url 须为 http(s)", file=sys.stderr); return 2
print(json.dumps(fetch(args.base, args.key, args.url), ensure_ascii=False, indent=2))
return 0
if __name__ == "__main__":
raise SystemExit(main())
第 2 步:测试
import subprocess, sys, time
from pathlib import Path
HERE = Path(__file__).resolve().parent
def test_fetch():
port = 19171
proc = subprocess.Popen([sys.executable, str(HERE/"mock_andi.py"), "--port", str(port)], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
try:
time.sleep(0.3)
r = subprocess.run([sys.executable, str(HERE/"andi_fetch.py"), "https://example.com/x", "--base", f"http://127.0.0.1:{port}", "--key", "andi-test-key"], capture_output=True, text=True, check=True)
assert "word_count" in r.stdout
finally:
proc.terminate(); proc.wait(timeout=5)
第 3 步:运行
$ pytest fetch
. [100%]
1 passed in 0.36s
怎么检查结果?
- 输出含 word_count。
- url 必须是 http(s)。
调试示例
对登录墙页面 fetch。 会失败或拿到登录页。换公开文档,或用你们自己有授权的镜像。
常见问题
| 现象 | 原因 | 处理 |
|---|---|---|
| 422/503 | 文档称部分失败不计费 | 以官网为准 |
| 只要 markdown | 查 format 参数 | 对照最新文档 |
| 批量 | 加限速 | 尊重站点条款 |
模板规则
- 不未授权抓取。
- 摘要仍要核对。
- 记录 cost。
项目资源
| 用途 | 地址 |
|---|---|
| API reference(官方) | https://docs.andiai.com/api-reference/introduction |
| Authentication(官方) | https://docs.andiai.com/getting-started/authentication |
| Docs 首页 | https://docs.andiai.com/ |
搜索找候选,fetch 读全文。