AI 工具

Andi Fetch API:安全读取公开网页正文

搜索之后用 GET /api/v1/fetch?url= 拉取干净正文,适合「点开阅读/总结」流水线。失败的 fetch 计费以文档为准。

BINBHO · 图文教程

搜索之后用 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 读全文。