Files
searxng-use-cli/README.md
T
thzxx e94cbe0783 feat(v2.1.0): 研究模式 + fetch.py Wayback 兜底 + 被墙站点智能回退
A. fetch.py 补齐 Wayback 兜底 (修复重大 gap)

- v2.0.1 gap: fetch.py 独立调用 403 时无 Wayback 兜底 (仅 search.py --fetch 有)

- AI Agent 用 fetch.py -u URL 直接抓取被墙站点时, 403 后无任何回退

- 修复: fetch.py main() 增加 Wayback 兜底逻辑 + --no-fallback flag

- 共享逻辑抽取到 common.py: should_try_wayback() + build_wayback_url()

B. --research 研究模式

- 给定主题自动扩展 5 个多角度查询: overview/profile/background/works/review

- 确定性规则 (不依赖 AI 判断), 跨进程可复现

- 输出含 research_topic + research_queries 元数据, AI Agent 可按角度结构化汇编

- 与 --query/--queries-file 互斥, 支持所有输出格式 (json/brief/urls/csv)

- 三态退出码: 0=有结果, 2=全部空, 1=全部错误

C. 被墙站点智能回退

- common.py 增加 HARD_BLOCKED_DOMAINS: 百度百科/知乎/微博/微信公众号/豆瓣等

- is_hard_blocked_domain() 精确匹配 + 子域匹配

- 命中被墙站点时: 主抓取失败后立即 Wayback (不等 should_try_wayback 判断)

- search.py _should_try_fallback 增加 url 参数, 被墙站点直接触发兜底

真实测试验证 (search.metona.cn 实例):

- fetch.py 百度百科兜底: 403 → Wayback 恢复 150,493 chars ✓

- --research 模式: 5 角度查询扩展 + research 元数据 + 三态退出码 ✓

- 被墙站点检测: Hard-blocked domain detected 日志 + 自动 Wayback ✓

测试: 503 个全部通过 (新增 45 个: test_wayback_shared + test_research_mode)

来源: 另一个 AI Agent 反馈 Wikipedia/百度百科/知乎 fetch 失败, 需要多角度搜索+失败回退+被墙站点列表
2026-08-02 08:27:45 +08:00

354 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# SearXNG CLI 技能包
[![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
一个面向 AI Agent 的网络搜索与网页抓取技能包。AI 通过终端调用 CLI 脚本即可获得"搜索网络"和"抓取网页"两项核心能力,无需 API Key,路由通过用户自建的 SearXNG 实例完成。
**这不是给人用的工具,而是给 AI 用的技能。** 任何能执行终端命令的 AI AgentHermes、Claude Code、Codex、OpenCode、Cursor、Trae 等)均可直接调用。
## 为什么需要这个技能
AI Agent 在以下场景中需要实时网络信息:
- 查询最新新闻、文档、技术资料
- 验证事实、获取实时数据
- 抓取网页内容进行阅读和分析
传统方案依赖付费搜索 API(Google、Bing)或专有爬虫服务。本技能通过用户自建的 SearXNG 实例(隐私元搜索引擎,聚合 70+ 搜索服务)实现零成本、零 API Key、隐私可控的网络搜索能力。
## AI 如何使用
### 搜索网络
```bash
python scripts/search.py -q "搜索词" -i https://your-searxng-instance
```
AI 调用后,stdout 输出 JSON 格式搜索结果,AI 可解析并使用:
```json
{
"query": "搜索词",
"results": [
{"title": "...", "url": "https://...", "content": "摘要...", "engine": "google", "score": 1.0}
],
"suggestions": ["相关建议词"],
"answers": ["直接答案(如有)"]
}
```
### 抓取网页
```bash
python scripts/fetch.py -u "https://example.com" --extract markdown
```
AI 调用后,stdout 输出网页正文(text/html/markdown 三种格式),AI 可直接阅读分析。
### 输入输出约定(AI 必读)
| 通道 | 内容 | 说明 |
|------|------|------|
| stdout | 数据 | JSON/CSV/文本,AI 解析的唯一来源 |
| stderr | 日志 | 进度、警告、错误,AI 可忽略或用于调试 |
| exit 0 | 成功 | 有结果 |
| exit 1 | 失败 | 致命错误(所有实例不可用、参数错误等) |
| exit 2 | 空结果 | 搜索成功但无结果 |
**错误处理**`--format json` 模式下,错误以 JSON 输出到 stdout(非 stderr),格式为 `{"error": "...", "error_code": "E_NETWORK", "recovery_hint": "...", "exit_code": 1, "query": "..."}`AI 可程序化捕获并按 `recovery_hint` 采取恢复行动。
**错误码体系**`error_code` + `recovery_hint` 字段):
| 错误码 | 含义 | recovery_hint(节选) |
|--------|------|----------------------|
| `E_CONFIG` | 配置错误(无实例) | 设置 `-i`/`SEARXNG_INSTANCE`/配置文件 |
| `E_AUTH` | 认证失败(401/403) | 检查凭证、token 过期与权限 |
| `E_NETWORK` | 网络错误(连接失败、5xx) | 重试退避、切换实例、检查代理 |
| `E_RATE_LIMIT` | 限流(429) | 等待重试、降低频率、分散负载 |
| `E_PARSE` | 解析错误 | 切换实例、切换 `--method` |
| `E_EMPTY` | 空结果(exit 2) | 调整查询词、扩大 `--time-range`/`--categories` |
| `E_INPUT` | 输入错误(参数/文件) | 检查语法、标志组合、文件路径 |
| `E_INTERNAL` | 内部错误 | 用 `--verbose` 重跑并报告 |
### 流式输出与进度事件(AI 高级用法)
**`--stream`**JSON Lines 流式输出,每条结果一行 JSON,AI 可增量处理:
```
{"type": "result", "result": {"title": "...", "url": "..."}}
{"type": "done", "schema_version": "1.0", "count": 10, "query": "..."}
```
搜索失败时输出 error 事件(含 `recovery_hint`):
```
{"type": "error", "error": "...", "error_code": "E_AUTH", "recovery_hint": "Verify credentials...", "query": "..."}
```
注意:`--stream` 仅在单查询 + `--format json` 下有效;与 `--queries-file` 或非 json 格式同用会立即报 `E_INPUT`
**`--progress`**:进度事件流(JSON Lines 到 stderr),AI 可实时跟踪执行:
```
{"event": "start", "query": "...", "instances": 2}
{"event": "instance_try", "url": "https://inst1.example.com", "attempt": 1}
{"event": "instance_ok", "url": "https://inst1.example.com", "latency": 0.342, "results": 10}
{"event": "instance_fail", "url": "https://inst2.example.com", "error": "HTTP 503", "error_code": "E_NETWORK"}
{"event": "cache_hit", "query": "...", "ttl": 30}
{"event": "fetch_ok", "url": "...", "chars": 12345}
{"event": "done", "results": 10, "query": "..."}
```
**`--dump-schema`**:输出当前版本的 JSON Schema 到 stdout 并退出,AI 可程序化发现字段名与类型,无需解析文档。
```bash
# AI 推荐用法:流式输出 + 进度事件
python scripts/search.py -q "research" -i https://your-instance --stream --progress
```
## 部署
### 前置条件
- Python 3.8+3.11+ 开箱即用;3.8-3.10 使用 `searxng.toml` 配置需 `pip install tomli`
- 一个 SearXNG 实例 URL(自建或受信任的实例)
### 安装
```bash
git clone https://git.metona.cn/MetonaTeam/searxng-use-cli.git
```
无需安装依赖。`search.py` 仅使用 Python 标准库,开箱即用。
可选安装:
```bash
# 提升 fetch.py 抓取质量(HTTP 连接池 + HTML 解析)
pip install requests beautifulsoup4
# Python 3.8-3.10 使用 searxng.toml 配置文件时需要(3.11+ 内置 tomllib
pip install tomli
```
### 配置实例
AI Agent 无需每次传入实例 URL 和认证信息,支持通过配置文件一次性设置:
1. **CLI 参数**`-i https://your-instance`(最高优先级)
2. **环境变量**`export SEARXNG_INSTANCE="https://your-instance"`
3. **配置文件**`./searxng.toml``~/.config/searxng-cli/searxng.toml``%APPDATA%/searxng-cli/searxng.toml`Windows
```toml
# searxng.toml
[searxng]
instance = "https://your-searxng.example.com"
# 私有实例认证(可选,二选一):
auth_basic = "user:password" # Basic 认证
# auth_bearer = "sk-token-123" # Bearer Token(与 auth_basic 同时设置时 bearer 优先)
```
**认证优先级**(从高到低):`--auth-*` CLI 参数 > `--auth-*-file` 文件 > `searxng.toml` 配置 > 环境变量
> **安全提醒**:配置文件中的明文密码有泄露风险。生产环境推荐使用 `--auth-basic-file ~/.searxng_auth` 或环境变量。
## 参数参考
### search.py — 网络搜索
```bash
python scripts/search.py -q "查询词" -i https://your-instance \
[--format json|brief|urls|csv] \
[--engines google,bing,brave] \
[--time-range day|month|year|none] \
[--language zh-CN] \
[--sort-by score|date|engine|none] \
[--no-dedup] \
[--max-results 10] \
[--fetch 3] \
[--fetch-report] \
[--no-fallback] \
[--referer URL] \
[--request-delay 0.3] \
[--cache-ttl 30] \
[--queries-file queries.txt] \
[--research "研究主题"] \
[--include-domain example.com] \
[--exclude-domain spam.com] \
[--proxy http://corp:8080] \
[--auth-bearer-file ~/.token] \
[--verify] \
[--stream] \
[--progress] \
[--verbose|-v] [--quiet]
```
| 参数 | 说明 | 默认值 |
|------|------|--------|
| `-q / --query` | 搜索词(必填,除非用 `--verify`/`--queries-file`/`--clear-cache`/`--cache-stats` | — |
| `-i / --instance` | SearXNG 实例 URL,逗号分隔实现故障转移 | — |
| `-f / --format` | 输出格式:json/brief/urls/csv | json |
| `--engines` | 搜索引擎列表 | google,bing,brave,duckduckgo,startpage,wikipedia,wikidata |
| `-t / --time-range` | 时间范围:day/month/year/none | year |
| `-s / --safesearch` | 安全搜索:0/1/2 | 0(关闭) |
| `-l / --language` | 语言代码 | — |
| `-p / --pageno` | 页码 | 1 |
| `--sort-by` | 排序:score/date/engine/none | score(降序) |
| `--no-dedup` | 禁用跨引擎去重 | 默认开启去重 |
| `--max-results` | 限制结果数(去重+排序后截取) | 不限 |
| `--fetch N` | 自动抓取前 N 个结果的网页正文 | 0(不抓取) |
| `--fetch-report` | v2.0.0 抓取报告(stderr,含 WAF/兜底/限流统计) | 关闭 |
| `--no-fallback` | v2.0.0 禁用 Wayback Machine 兜底 | 默认启用兜底 |
| `--referer` | v2.0.0 设置 Referer 头 | 实例 URL |
| `--request-delay` | v2.0.0 抓取请求间隔秒数(自适应限流可能增大) | 0.3 |
| `--cache-ttl` | 缓存分钟数 | 0(不缓存) |
| `--queries-file` | 批量查询文件(每行一个查询) | — |
| `--research` | v2.1.0 研究模式:给定主题自动扩展 5 个多角度查询 | — |
| `--include-domain` | 域名白名单 | — |
| `--exclude-domain` | 域名黑名单 | — |
| `--proxy` | 代理 URL | — |
| `--auth-bearer-file` | Bearer Token 文件 | — |
| `--auth-basic-file` | Basic Auth 文件 | — |
| `--verify` | 实例健康检查模式 | — |
| `--config` | 指定配置文件 | 自动发现 |
| `--stream` | JSON Lines 流式输出(每条结果一行) | 关闭 |
| `--progress` | 进度事件(JSON Lines 到 stderr | 关闭 |
| `-v / --verbose` | 调试日志 | — |
| `--quiet` | 仅输出警告和错误 | — |
### fetch.py — 网页抓取
```bash
python scripts/fetch.py -u https://example.com \
--extract text|html|markdown \
[--encoding gbk] \
[--max-size 5242880] \
[--timeout 15] \
[--retries 3] \
[--no-redirect] \
[--referer https://google.com/] \
[--proxy http://corp:8080] \
[--auth-bearer-file ~/.token]
```
| 参数 | 说明 | 默认值 |
|------|------|--------|
| `-u / --url` | 目标 URL(必填) | — |
| `-e / --extract` | 提取模式:text/html/markdown | text |
| `--encoding` | 强制字符编码 | 自动检测 |
| `--max-size` | 最大字节数 | 不限 |
| `--timeout` | 超时秒数(v2.0.0 内部拆分为 connect/read | 15 |
| `--retries` | 重试次数 | 3 |
| `--no-redirect` | 不跟随重定向 | 跟随 |
| `--no-fallback` | v2.1.0 禁用 Wayback Machine 兜底 | 默认启用兜底 |
| `--referer` | 设置 Referer 头(v2.0.0 反爬措施) | — |
| `--proxy` | 代理 URL | — |
| `--auth-bearer-file` | Bearer Token 文件 | — |
| `--auth-basic-file` | Basic Auth 文件 | — |
## 能力清单
**搜索**
- 多实例故障转移 + 并行探测
- 指数退避重试(429/5xx/连接错误)
- 跨引擎去重(忽略 utm_*/gclid 等跟踪参数)
- 结果排序(score/date/engine
- 域名白名单/黑名单
- 批量查询(`--queries-file`
- v2.1.0 研究模式(`--research`):给定主题自动扩展 5 个多角度查询(overview/profile/background/works/review),输出含 `research_topic``research_queries` 元数据
- 实例健康检查(`--verify`
**输出**
- JSON(默认,含完整元数据)
- brief(标题+URL+摘要)
- urls(纯 URL 列表)
- CSV(表格导出)
- 结构化 JSON 错误输出
**网页抓取**
- text:提取纯文本
- html:原始 HTML
- markdown:增强 Markdown 转换(GFM 表格、代码块、引用块、嵌套列表、定义列表)
- v2.0.0 readability-lite`<article>`/`<main>` 缺失时,用文本密度算法选最可能正文的 `<div>`
**反爬与抓取稳定性(v2.0.0)**
- 浏览器指纹头:`build_browser_headers()` 发送完整 Sec-Ch-Ua / Sec-Fetch-* / Accept-Language,不仅靠 User-Agent
- 12 个 UA 池:Chrome/Edge/Firefox × Windows/macOS/Linux × 129-131 版本
- 确定性 UA 轮换:`get_ua_for_domain()` 用 SHA-256 为每个域名固定一个 UA(会话内稳定,跨进程可复现)
- requests.Session 复用:连接池 + cookie 持久化 + TLS 会话恢复
- 超时分离:`(connect, read)` 元组,避免大页面下载中途超时浪费已建连接
- Retry-After 遵守:429/503 响应读取 Retry-After header(数字或 HTTP date)作为最小重试延迟
- 退避封顶 60s:原公式无上限,N=10 时达 1536s 会卡死进程
- WAF 指纹库:识别 Cloudflare / Imperva / PerimeterX / DataDome / Akamai / 通用反爬页,全文档扫描(非仅前 2000 字符)
- Wayback Machine 兜底:404/403/超时自动尝试 `https://web.archive.org/web/2/<url>`,默认启用,`--no-fallback` 关闭
- v2.1.0 fetch.py 独立调用也支持 Wayback 兜底(之前仅 search.py --fetch 路径有)
- v2.1.0 被墙站点智能回退:`is_hard_blocked_domain()` 识别百度百科/知乎/微博/微信公众号/豆瓣等强反爬站点,403 时自动优先 Wayback
- 自适应限流:`AdaptiveThrottle` 状态机,连续 3 次失败自动翻倍延迟 + 减半并发,429 触发全局暂停 30s
- `--fetch-report`:结构化抓取报告到 stderr(每 URL 状态/WAF 类型/兜底方式/字符数 + JSON 摘要)
- `--referer` / `--request-delay`:精细控制 Referer 头和请求间隔
- fetch 结果新增字段:`anti_bot_detected`bool)、`waf_type`str|null)、`fallback_used`str|null
**缓存**
- SQLite 缓存(`--cache-ttl`),相同查询在 TTL 内跳过网络
- `--clear-cache` / `--cache-stats` 管理缓存
**网络**
- 代理支持(`--proxy`
- 认证(Bearer/Basic,支持 CLI/文件/配置文件/环境变量四级优先级)
- `searxng.toml` 配置 `auth_basic` / `auth_bearer` 字段,AI Agent 一次配置即可
- 凭证文件权限警告(POSIX
**工程**
- 共享 `common.py`(统一重试/字符集/认证/日志/UA池/浏览器头/退避)
- 结构化日志(`--verbose` / `--quiet`
- UTF-8 stdout 强制(`force_utf8_stdout()`,修复 Windows GBK 崩溃)
- Windows 配置路径发现(`%APPDATA%/searxng-cli/`
- fetch.py 失败诊断增强(`status_code=` / `cause=` / `url=` 字段)
- 结构化错误码 + `recovery_hint` 恢复建议(`E_NETWORK` / `E_AUTH` / `E_RATE_LIMIT` 等)
- JSON 输出含 `schema_version` 字段,`--dump-schema` 输出 JSON Schema 文档
- JSON Lines 流式输出(`--stream`,含 `error` 事件类型)
- 进度事件(`--progress`,含 `instance_try`/`instance_ok`/`instance_fail`JSON Lines 到 stderr
- batch 模式统一 schema`status` 字段区分成功/失败)
- 503 个单元+集成测试
## 跨 Agent 兼容性
本技能与 Agent 无关——任何能执行终端命令的 AI 均可使用:
| Agent | 调用方式 |
|-------|---------|
| Hermes | `python scripts/search.py -q "..." -i https://your-instance` |
| Claude Code | 同上,通过终端工具调用 |
| Codex (OpenAI) | 同上 |
| OpenCode | 同上 |
| Cursor | 同上 |
| Trae | 同上 |
设计要点:
- 零外部依赖(`search.py` 仅标准库)
- 脚本自注入目录到 `sys.path`,可从任意工作目录运行
- stdout 纯数据,stderr 纯日志
- 无 Agent 专属 API 调用,纯 CLI,跨平台可移植
## 测试
```bash
pip install pytest
pytest -q
```
503 个测试覆盖:缓存操作、认证解析、域名过滤、Markdown 转换、搜索逻辑、集成流程、日志配置、HTML 回退、自动抓取、健康检查、输出格式化、实例解析、并行搜索、CLI 端到端、错误码分类、流式输出、进度事件、配置文件认证、schema_version、recovery_hint、batch 统一 schema、--dump-schema、UTF-8 stdout 强制、Windows APPDATA 路径、v2.0.0 浏览器指纹头、WAF 反爬检测(v2.0.1 收窄误判 + title 精准检测)、Wayback Machine 兜底(v2.0.1 修复 HTTP 200 反爬页触发)、自适应限流、v2.1.0 Wayback 共享逻辑(should_try_wayback/build_wayback_url)、被墙站点智能回退(is_hard_blocked_domain)、--research 研究模式(多角度查询扩展)。
## 项目结构
```
├── scripts/
│ ├── search.py # 搜索(多实例故障转移/缓存/批量/域名过滤)
│ ├── fetch.py # 网页抓取(text/markdown 提取)
│ ├── common.py # 共享工具(认证/重试/字符集/日志)
│ ├── cache.py # SQLite 结果缓存
│ └── _config.py # 版本号 + User-Agent
├── tests/ # pytest 单元+集成测试
├── SKILL.md # 完整技能文档(Agent 技能描述)
├── pytest.ini # 测试配置
└── README.md
```
## 许可证
MIT