This commit is contained in:
2026-09-27 23:06:24 +08:00
parent 2d4f9230b1
commit 22450e5bb3
40 changed files with 3040 additions and 697 deletions
+71
View File
@@ -0,0 +1,71 @@
---
name: searxng_skills
description: Search the live web via Jack's self-hosted SearXNG (https://searxng.erhe.top) using token-authenticated JSON output. Use when the agent needs current pricing/news/docs/product specs without invoking browser tools.
---
# SearXNG Web Search (Jack's server)
Search the live web through Jack's self-hosted SearXNG. 129 engines enabled (bing / duckduckgo / startpage / brave / mojeek / qwant / wikipedia 系 / etc.). Google main engine is OFF; google scholar/images/videos/news/cse sub-APIs are enabled.
## Endpoint
- **URL**: `https://searxng.erhe.top` ⚠ `erhe.top`, **NOT** `ehre.top`
- **Token**: `fGWEFWGrgasegtjt1jUJG` (passed as `?token=...` query param)
- **Method**: GET works here. (Local docker 2026.7 default is `server.method=POST` — different; if porting code to a stock local SearXNG, flip to POST.)
- **JSON 200**: This server has limiter/token auth configured, so `?format=json&token=...` returns 200. Stock local docker returns 403 unconditionally on JSON — different from this.
## Quick start
CLI (preferred; auto-renders results):
```bash
python3 ~/.hermes/skills/searxng_skills/searxng_search.py "<query>" -m 5 -l en
```
Direct API (works fine from any script):
```python
import urllib.request, urllib.parse, json
url = "https://searxng.erhe.top/search?" + urllib.parse.urlencode({
"q": "<query>", "format": "json", "token": "fGWEFWGrgasegtjt1jUJG",
})
data = json.loads(urllib.request.urlopen(url, timeout=30).read())
for r in data.get("results", [])[:5]:
print(r["engine"], r["title"][:80], "→", r["url"])
```
## Response shape
JSON: `{results: [...], unresponsive_engines: [...], suggestions: [...], query: ...}`
Each result: `title, url, content, host, engine, score, type, publishedDate (optional)`.
Alternative formats:
- `?format=csv` → CSV with columns `title,url,content,host,engine,score,type` (great for scripts)
- `?format=rss` → RSS 2.0 feed
## Pitfalls
- **Domain typo is the #1 way to silently break this skill**. `searxng.erhe.top` ≠ `searxng.erhe.top`. Jack's `docker-compose.yaml` says `ehre.top`; the live deployment is `erhe.top`. If `Could not resolve host` / DNS `NXDOMAIN`, double-check the spelling — almost certainly an `h`/`r` swap.
- **JSON endpoint requires the token**. Without `?token=...`, returns 403. Token is rotated by changing `SEARXNG_API_KEY` env in docker-compose; no code redeploy needed.
- **Google main engine is OFF**. `google.com` is blocked at upstream (Cloudflare anti-bot). Only `google_scholar` / `google_images` / `google_videos` / `google_news` / `google_cse` are enabled. If the user says "用 google 搜 XX", expect mostly bing/duckduckgo results — be honest about it rather than silently re-routing.
- **LibreSSL/OpenSSL warning from urllib3 v2** is harmless on macOS Python 3.9. `urllib3 v2 only supports OpenSSL 1.1.1+, but the 'ssl' module is compiled with LibreSSL`. The search still runs to completion; ignore the warning, do not reinstall.
- **SEO content farm pollution**. DuckDuckGo heavily surfaces AI-generated SEO blogs (llmmac / meshmac / neokvm / vulcloud / maccompute). For pricing/spec verification, filter by `host` to `apple.com`, `wikipedia.org`, `*.edu`, `github.com`, `arxiv.org`, `*.gov`. Don't blindly trust first-page blog links for actual prices.
- **brave / mojeek / qwant commonly time out** (server logs show them in `unresponsive_engines`). Real workhorses: bing, duckduckgo, startpage, wikipedia 系, github 系.
## CLI flags (for `searxng_search.py`)
| Flag | Meaning |
|---|---|
| `-m N` / `--max-results N` | Result row limit (default 10) |
| `-l LANG` | `auto` / `zh` / `en` / `ja` / `de` / `fr` |
| `-c CAT` | `general` / `images` / `videos` / `news` / `music` |
| `--time-range` | `day` / `week` / `month` / `year` |
| `--safesearch` | `0` / `1` / `2` |
| `--raw` | Print raw JSON |
| `-v` / `--verbose` | Verbose per-result output |
## Companion docs in this folder
- `searxng_skills.md` — full API reference (more endpoints)
- `searxng_README.md` — quickstart (Chinese)
- `searxng_search.py` — the CLI entry; can also be imported as a module
+194
View File
@@ -0,0 +1,194 @@
# SearXNG API 使用指南
## 快速开始
### Python 脚本方式
```bash
# 安装依赖 (如需要)
pip3 install requests
# 搜索示例
python3 /tmp/searxng_search.py "python教程"
# 指定分类
python3 /tmp/searxng_search.py "猫咪图片" -c images
# 指定时间范围
python3 /tmp/searxng_search.py "科技新闻" --time-range week
# 输出 JSON
python3 /tmp/searxng_search.py "python" --raw
```
### cURL 方式
```bash
# 基础搜索
curl "https://searxng.erhe.top/search?q=关键词&token=fGWEFWGrgasegtjt1jUJG"
# JSON 格式
curl "https://searxng.erhe.top/search?q=关键词&format=json&token=fGWEFWGrgasegtjt1jUJG"
```
### Python 代码方式
```python
import requests
def search(query):
response = requests.get(
"https://searxng.erhe.top/search",
params={
"q": query,
"format": "json",
"token": "fGWEFWGrgasegtjt1jUJG"
}
)
return response.json()
# 使用
results = search("python教程")
for r in results['results'][:5]:
print(r['title'], r['url'])
```
## 常用命令参考
| 用途 | 命令 |
|------|------|
| 基础搜索 | `python3 /tmp/searxng_search.py "关键词"` |
| 图片搜索 | `python3 /tmp/searxng_search.py "关键词" -c images` |
| 视频搜索 | `python3 /tmp/searxng_search.py "关键词" -c videos` |
| 中文结果 | `python3 /tmp/searxng_search.py "关键词" -l zh` |
| 最近一周 | `python3 /tmp/searxng_search.py "关键词" -t week` |
| 限制结果数 | `python3 /tmp/searxng_search.py "关键词" -m 20` |
| 原始 JSON | `python3 /tmp/searxng_search.py "关键词" --raw` |
| 查看帮助 | `python3 /tmp/searxng_search.py --help` |
## Agent 集成示例
### Claude Code 工具定义
```json
{
"name": "searxng_search",
"description": "使用 SearXNG 隐私搜索引擎进行网络搜索",
"input_schema": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "搜索关键词"
},
"category": {
"type": "string",
"enum": ["general", "images", "videos", "news", "music"],
"description": "搜索分类"
},
"language": {
"type": "string",
"default": "auto",
"description": "语言代码"
},
"max_results": {
"type": "integer",
"default": 10,
"description": "最大结果数"
}
},
"required": ["query"]
}
}
```
### Python 工具实现
```python
def searxng_search_tool(query, category="general", language="auto", max_results=10):
"""
SearXNG 搜索工具
"""
try:
response = requests.get(
"https://searxng.erhe.top/search",
params={
"q": query,
"format": "json",
"token": "fGWEFWGrgasegtjt1jUJG",
"language": language,
"categories": category if category != "general" else None
},
timeout=15
)
if response.status_code == 200:
data = response.json()
results = []
for r in data.get('results', [])[:max_results]:
results.append({
"title": r.get('title', ''),
"url": r.get('url', ''),
"snippet": r.get('content', '')[:200],
"source": r.get('engine', '')
})
return {
"success": True,
"query": data.get('query', query),
"count": len(results),
"results": results
}
else:
return {
"success": False,
"error": f"HTTP {response.status_code}"
}
except Exception as e:
return {
"success": False,
"error": str(e)
}
# 使用示例
result = searxng_search_tool("如何安装Python", max_results=5)
if result['success']:
for r in result['results']:
print(f"📄 {r['title']}\n {r['url']}\n")
```
## 完整文档
查看详细文档: `cat /tmp/searxng_skills.md`
## 测试
```bash
# 测试脚本
python3 /tmp/searxng_search.py "test" -m 3
# 测试 cURL
curl -s "https://searxng.erhe.top/search?q=test&format=json&token=fGWEFWGrgasegtjt1jUJG" | head -20
```
## 故障排除
### 常见问题
1. **Token 错误 403**
- 确保请求中包含 `token=fGWEFWGrgasegtjt1jUJG`
2. **连接超时**
- 检查网络连接
- 尝试增加超时时间
3. **结果为空**
- 尝试更换关键词
- 检查语言设置
### 获取帮助
- 查看详细文档: `/tmp/searxng_skills.md`
- 查看脚本帮助: `python3 /tmp/searxng_search.py --help`
+184
View File
@@ -0,0 +1,184 @@
#!/usr/bin/env python3
"""
SearXNG API 客户端 - 快速搜索工具
"""
import requests
import json
import sys
import argparse
from urllib.parse import urlencode
# 配置
SEARXNG_URL = "https://searxng.erhe.top"
API_TOKEN = "fGWEFWGrgasegtjt1jUJG"
def search(query,
format_type="json",
categories=None,
language="auto",
safesearch=0,
time_range=None,
max_results=10):
"""
搜索函数
参数:
query: 搜索关键词
format_type: 返回格式 (json/html/csv/rss)
categories: 分类 (images/videos/news/music/general)
language: 语言代码 (auto/zh/en/ja/de/fr)
safesearch: 安全搜索级别 (0=关闭, 1=适中, 2=严格)
time_range: 时间范围 (day/week/month/year)
max_results: 最大结果数
返回:
dict: 搜索结果
"""
params = {
"q": query,
"format": format_type,
"token": API_TOKEN,
"language": language,
"safesearch": safesearch
}
if categories:
params["categories"] = categories
if time_range:
params["time_range"] = time_range
try:
response = requests.get(
f"{SEARXNG_URL}/search",
params=params,
timeout=15
)
if response.status_code == 200:
if format_type == "json":
return response.json()
else:
return {"html": response.text}
elif response.status_code == 403:
return {"error": "API token 无效或缺失"}
else:
return {"error": f"请求失败: {response.status_code}"}
except requests.exceptions.Timeout:
return {"error": "请求超时"}
except requests.exceptions.ConnectionError:
return {"error": "连接失败,请检查网络"}
except Exception as e:
return {"error": str(e)}
def print_results(results, max_results=10, verbose=False):
"""格式化输出结果"""
if "error" in results:
print(f"❌ 错误: {results['error']}")
return
query = results.get('query', '未知')
results_list = results.get('results', [])
unresponsive = results.get('unresponsive_engines', [])
print(f"\n{'='*70}")
print(f"🔍 查询: {query}")
print(f"📊 找到 {len(results_list)} 个结果")
if unresponsive:
print(f"⚠️ 部分搜索引擎失败: {', '.join([u[0] for u in unresponsive[:3]])}")
print(f"{'='*70}\n")
for i, result in enumerate(results_list[:max_results], 1):
title = result.get('title', '无标题')
url = result.get('url', '无链接')
content = result.get('content', '')
engine = result.get('engine', '未知')
published = result.get('publishedDate', '')
print(f"{i:2d}. {title}")
print(f" 🔗 {url}")
if content:
# 截断内容
if len(content) > 150:
content = content[:150] + "..."
print(f" 📝 {content}")
print(f" ⚙️ 来源: {engine}", end="")
if published:
print(f" | 📅 {published}", end="")
print("\n")
def main():
parser = argparse.ArgumentParser(
description="SearXNG 搜索工具",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
示例:
%(prog)s python 教程
%(prog)s "machine learning" --category videos
%(prog)s linux --language en --time-range week
%(prog)s python --max-results 20 --verbose
"""
)
parser.add_argument('query', nargs='+', help='搜索关键词')
parser.add_argument('-c', '--category',
choices=['general', 'images', 'videos', 'news', 'music', 'files', 'it', 'science'],
default='general',
help='搜索分类 (默认: general)')
parser.add_argument('-l', '--language',
default='auto',
help='语言代码 (默认: auto)')
parser.add_argument('-s', '--safesearch',
type=int, choices=[0, 1, 2], default=0,
help='安全搜索: 0=关闭, 1=适中, 2=严格 (默认: 0)')
parser.add_argument('-t', '--time-range',
choices=['day', 'week', 'month', 'year'],
help='时间范围')
parser.add_argument('-m', '--max-results',
type=int, default=10,
help='最大结果数 (默认: 10)')
parser.add_argument('-v', '--verbose',
action='store_true',
help='详细输出')
parser.add_argument('--format',
choices=['json', 'html', 'csv', 'rss'],
default='json',
help='返回格式 (默认: json)')
parser.add_argument('--raw',
action='store_true',
help='输出原始 JSON')
args = parser.parse_args()
# 组合查询词
query = ' '.join(args.query)
# 执行搜索
results = search(
query,
format_type=args.format,
categories=args.category if args.category != 'general' else None,
language=args.language,
safesearch=args.safesearch,
time_range=args.time_range,
max_results=args.max_results
)
# 输出结果
if args.raw:
print(json.dumps(results, indent=2, ensure_ascii=False))
else:
print_results(results, max_results=args.max_results, verbose=args.verbose)
# 如果有建议搜索词
if 'suggestions' in results and results['suggestions']:
print(f"💡 你可能想搜索:")
for suggestion in results['suggestions']:
print(f" - {suggestion}")
if __name__ == "__main__":
main()
+330
View File
@@ -0,0 +1,330 @@
# SearXNG API Skills
## 基本信息
- **URL**: `https://searxng.erhe.top`
- **API Token**: `fGWEFWGrgasegtjt1jUJG`
- **端口**: 通过 Caddy 反向代理
## API 调用方法
### 1. 搜索 API
```bash
# 基础搜索
curl "https://searxng.erhe.top/search?q=关键词&token=fGWEFWGrgasegtjt1jUJG"
# JSON 格式返回(推荐)
curl "https://searxng.erhe.top/search?q=关键词&format=json&token=fGWEFWGrgasegtjt1jUJG"
```
### 2. Python 调用示例
```python
import requests
import json
def search_searxng(query, format_type="json"):
"""
使用 SearXNG 搜索
参数:
query: 搜索关键词
format_type: 返回格式 (json/html/csv/rss)
返回:
dict: 搜索结果
"""
url = "https://searxng.erhe.top/search"
params = {
"q": query,
"format": format_type,
"token": "fGWEFWGrgasegtjt1jUJG"
}
response = requests.get(url, params=params)
if format_type == "json":
return response.json()
else:
return response.text
# 使用示例
results = search_searxng("python 教程")
print(f"找到 {len(results['results'])} 个结果")
for result in results['results'][:5]:
print(f"- {result['title']}: {result['url']}")
```
### 3. Claude Code / Agent 调用示例
```python
import urllib.request
import urllib.parse
import json
def search(query):
"""搜索函数"""
base_url = "https://searxng.erhe.top/search"
params = urllib.parse.urlencode({
"q": query,
"format": "json",
"token": "fGWEFWGrgasegtjt1jUJG"
})
url = f"{base_url}?{params}"
with urllib.request.urlopen(url) as response:
data = json.loads(response.read().decode())
return data
# Agent 中使用
results = search("如何在 Linux 中查找文件")
for r in results['results'][:3]:
print(f"标题: {r['title']}")
print(f"链接: {r['url']}\n")
```
## 高级搜索
### 1. 指定语言
```bash
curl "https://searxng.erhe.top/search?q=关键词&format=json&token=fGWEFWGrgasegtjt1jUJG&language=zh"
```
语言代码:
- `zh` - 中文
- `en` - 英文
- `ja` - 日文
- `de` - 德文
- `fr` - 法文
### 2. 指定分类
```bash
# 图片搜索
curl "https://searxng.erhe.top/search?q=猫&format=json&token=fGWEFWGrgasegtjt1jUJG&categories=images"
# 视频搜索
curl "https://searxng.erhe.top/search?q=教程&format=json&token=fGWEFWGrgasegtjt1jUJG&categories=videos"
# 新闻搜索
curl "https://searxng.erhe.top/search?q=科技&format=json&token=fGWEFWGrgasegtjt1jUJG&categories=news"
```
分类列表:
- `general` - 通用
- `images` - 图片
- `videos` - 视频
- `news` - 新闻
- `music` - 音乐
- `files` - 文件
- `it` - IT
- `science` - 科学
### 3. 安全搜索
```bash
# 关闭安全搜索 (0)
curl "https://searxng.erhe.top/search?q=关键词&format=json&token=fGWEFWGrgasegtjt1jUJG&safesearch=0"
# 适中 (1)
curl "https://searxng.erhe.top/search?q=关键词&format=json&token=fGWEFWGrgasegtjt1jUJG&safesearch=1"
# 严格 (2)
curl "https://searxng.erhe.top/search?q=关键词&format=json&token=fGWEFWGrgasegtjt1jUJG&safesearch=2"
```
### 4. 时间范围
```bash
# 最近一天
curl "https://searxng.erhe.top/search?q=关键词&format=json&token=fGWEFWGrgasegtjt1jUJG&time_range=day"
# 最近一周
curl "https://searxng.erhe.top/search?q=关键词&format=json&token=fGWEFWGrgasegtjt1jUJG&time_range=week"
# 最近一月
curl "https://searxng.erhe.top/search?q=关键词&format=json&token=fGWEFWGrgasegtjt1jUJG&time_range=month"
# 最近一年
curl "https://searxng.erhe.top/search?q=关键词&format=json&token=fGWEFWGrgasegtjt1jUJG&time_range=year"
```
## JSON 返回格式说明
```json
{
"query": "搜索关键词",
"results": [
{
"title": "结果标题",
"url": "https://example.com",
"content": "结果摘要",
"engine": "搜索引擎名称",
"template": "模板类型",
"publishedDate": "发布日期 (如果有)",
"img_src": "图片URL (如果有)",
"thumbnail": "缩略图URL (如果有)",
"category": "分类"
}
],
"answers": [],
"corrections": [],
"infoboxes": [],
"suggestions": ["建议搜索词"],
"unresponsive_engines": []
}
```
## 获取配置信息
```bash
# 获取 SearXNG 配置
curl "https://searxng.erhe.top/config&token=fGWEFWGrgasegtjt1jUJG"
```
## 完整 Python 示例
```python
#!/usr/bin/env python3
"""
SearXNG API 客户端示例
"""
import requests
import json
import sys
SEARXNG_URL = "https://searxng.erhe.top"
API_TOKEN = "fGWEFWGrgasegtjt1jUJG"
def search(query,
format_type="json",
categories=None,
language="auto",
safesearch=0,
time_range=None):
"""
搜索函数
参数:
query: 搜索关键词
format_type: 返回格式 (json/html/csv/rss)
categories: 分类列表 (images/videos/news/music)
language: 语言代码 (auto/zh/en/ja/de/fr)
safesearch: 安全搜索级别 (0/1/2)
time_range: 时间范围 (day/week/month/year)
返回:
dict: 搜索结果
"""
params = {
"q": query,
"format": format_type,
"token": API_TOKEN,
"language": language,
"safesearch": safesearch
}
if categories:
params["categories"] = categories
if time_range:
params["time_range"] = time_range
response = requests.get(f"{SEARXNG_URL}/search", params=params)
if format_type == "json":
return response.json()
else:
return response.text
def format_results(results, max_results=10):
"""格式化输出结果"""
print(f"\n{'='*60}")
print(f"查询: {results.get('query')}")
print(f"找到 {len(results.get('results', []))} 个结果")
print(f"{'='*60}\n")
for i, result in enumerate(results.get('results', [])[:max_results], 1):
print(f"{i}. {result.get('title', '无标题')}")
print(f" 链接: {result.get('url', '无链接')}")
if result.get('content'):
content = result.get('content', '')[:150]
print(f" 摘要: {content}...")
print(f" 来源: {result.get('engine', '未知')}")
print()
if __name__ == "__main__":
# 示例搜索
if len(sys.argv) > 1:
query = " ".join(sys.argv[1:])
else:
query = "Python 编程"
try:
results = search(query, categories="general")
format_results(results)
except Exception as e:
print(f"搜索失败: {e}")
```
## 错误处理
```python
import requests
def safe_search(query):
"""安全的搜索函数,包含错误处理"""
try:
response = requests.get(
"https://searxng.erhe.top/search",
params={
"q": query,
"format": "json",
"token": "fGWEFWGrgasegtjt1jUJG"
},
timeout=10
)
if response.status_code == 200:
return response.json()
elif response.status_code == 403:
return {"error": "API token 无效或缺失"}
else:
return {"error": f"请求失败: {response.status_code}"}
except requests.exceptions.Timeout:
return {"error": "请求超时"}
except requests.exceptions.ConnectionError:
return {"error": "连接失败,请检查网络"}
except Exception as e:
return {"error": str(e)}
```
## 快速参考
| 功能 | 命令 |
|------|------|
| 基础搜索 | `curl "https://searxng.erhe.top/search?q=关键词&token=fGWEFWGrgasegtjt1jUJG"` |
| JSON 返回 | 添加 `&format=json` |
| 图片搜索 | 添加 `&categories=images` |
| 中文结果 | 添加 `&language=zh` |
| 最近一周 | 添加 `&time_range=week` |
| 获取配置 | `curl "https://searxng.erhe.top/config&token=fGWEFWGrgasegtjt1jUJG"` |
## 注意事项
1. **Token 必须**: 所有 API 请求必须包含 `token` 参数
2. **返回格式**: 推荐使用 `format=json` 获取结构化数据
3. **超时处理**: 搜索可能需要几秒钟,建议设置超时
4. **请求频率**: 避免过于频繁的请求,可能触发限制
5. **结果数量**: 默认返回多个搜索引擎的结果
## 获取帮助
- SearXNG 主页: `https://searxng.erhe.top`
- SearXNG 文档: `https://docs.searxng.org`