Initial commit with translated description
This commit is contained in:
71
SKILL.md
Normal file
71
SKILL.md
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
---
|
||||||
|
name: desearch-web-search
|
||||||
|
description: "搜索网页并获取实时SERP样式结果。"
|
||||||
|
metadata: {"clawdbot":{"emoji":"🌐","homepage":"https://desearch.ai","requires":{"env":["DESEARCH_API_KEY"]}}}
|
||||||
|
---
|
||||||
|
|
||||||
|
# Web Search By Desearch
|
||||||
|
|
||||||
|
Real-time web search returning structured SERP-style results with titles, links, and snippets.
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
1. Get an API key from https://console.desearch.ai
|
||||||
|
2. Set environment variable: `export DESEARCH_API_KEY='your-key-here'`
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Basic web search
|
||||||
|
desearch.py web "quantum computing"
|
||||||
|
|
||||||
|
# Paginated results
|
||||||
|
desearch.py web "quantum computing" --start 10
|
||||||
|
```
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
| Option | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| `--start` | Pagination offset (default: 0). Use to get the next page of results. |
|
||||||
|
|
||||||
|
## Response
|
||||||
|
|
||||||
|
### Example
|
||||||
|
```bash
|
||||||
|
desearch.py web "best sights in Georgia"
|
||||||
|
```
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"title": "Cool places and fun things to do in Georgia ? : r/solotravel",
|
||||||
|
"snippet": "I was in Georgia earlier this month. My favorite place was Mtirala National Park in Adjara. The mountains and forest were absolutely beautiful ...",
|
||||||
|
"link": "https://www.reddit.com/r/solotravel/comments/py4wls/cool_places_and_fun_things_to_do_in_georgia/",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Notes
|
||||||
|
- Returns up to 10 results per page. Use `--start` to paginate.
|
||||||
|
|
||||||
|
### Errors
|
||||||
|
Status 401, Unauthorized (e.g., missing/invalid API key)
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"detail": "Invalid or missing API key"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Status 402, Payment Required (e.g., balance depleted)
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"detail": "Insufficient balance, please add funds to your account to continue using the service."
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Resources
|
||||||
|
- [API Reference](https://desearch.ai/docs/api-reference/get-web)
|
||||||
|
- [Desearch Console](https://console.desearch.ai)
|
||||||
6
_meta.json
Normal file
6
_meta.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"ownerId": "kn70rkrynh1pbpb1nh8fztsbnx814573",
|
||||||
|
"slug": "desearch-web-search",
|
||||||
|
"version": "1.0.1",
|
||||||
|
"publishedAt": 1771578642605
|
||||||
|
}
|
||||||
131
scripts/desearch.py
Normal file
131
scripts/desearch.py
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Desearch Web Search CLI - Real-time SERP-style web search.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
desearch web "<query>" [options]
|
||||||
|
|
||||||
|
Environment:
|
||||||
|
DESEARCH_API_KEY - Required API key from desearch.ai
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from urllib.error import HTTPError, URLError
|
||||||
|
from urllib.parse import urlencode
|
||||||
|
from urllib.request import Request, urlopen
|
||||||
|
|
||||||
|
DESEARCH_BASE = "https://api.desearch.ai"
|
||||||
|
|
||||||
|
|
||||||
|
def get_api_key() -> str:
|
||||||
|
key = os.environ.get("DESEARCH_API_KEY")
|
||||||
|
if not key:
|
||||||
|
print("Error: DESEARCH_API_KEY environment variable not set", file=sys.stderr)
|
||||||
|
print("Get your key at https://console.desearch.ai", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
return key
|
||||||
|
|
||||||
|
|
||||||
|
def api_request(
|
||||||
|
method: str, path: str, params: dict = None, body: dict = None
|
||||||
|
) -> dict | str:
|
||||||
|
api_key = get_api_key()
|
||||||
|
url = f"{DESEARCH_BASE}{path}"
|
||||||
|
|
||||||
|
if method == "GET" and params:
|
||||||
|
filtered = {k: v for k, v in params.items() if v is not None}
|
||||||
|
if filtered:
|
||||||
|
url = f"{url}?{urlencode(filtered, doseq=True)}"
|
||||||
|
|
||||||
|
headers = {
|
||||||
|
"Authorization": f"{api_key}",
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"User-Agent": "Desearch-Clawdbot/1.0",
|
||||||
|
}
|
||||||
|
|
||||||
|
data = None
|
||||||
|
if method == "POST" and body:
|
||||||
|
data = json.dumps(body).encode()
|
||||||
|
|
||||||
|
try:
|
||||||
|
req = Request(url, data=data, headers=headers, method=method)
|
||||||
|
with urlopen(req, timeout=60) as response:
|
||||||
|
raw = response.read().decode()
|
||||||
|
try:
|
||||||
|
return json.loads(raw)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
return raw
|
||||||
|
except HTTPError as e:
|
||||||
|
error_body = e.read().decode() if e.fp else ""
|
||||||
|
try:
|
||||||
|
return json.loads(error_body)
|
||||||
|
except (json.JSONDecodeError, Exception):
|
||||||
|
return {"error": f"HTTP {e.code}: {e.reason}", "details": error_body}
|
||||||
|
except URLError as e:
|
||||||
|
return {"error": f"Connection failed: {e.reason}"}
|
||||||
|
except Exception as e:
|
||||||
|
return {"error": str(e)}
|
||||||
|
|
||||||
|
|
||||||
|
def cmd_web(args):
|
||||||
|
params = {
|
||||||
|
"query": args.query,
|
||||||
|
"start": args.start,
|
||||||
|
}
|
||||||
|
return api_request("GET", "/web", params=params)
|
||||||
|
|
||||||
|
|
||||||
|
def format_web_results(data):
|
||||||
|
lines = []
|
||||||
|
if isinstance(data, str):
|
||||||
|
return data
|
||||||
|
if isinstance(data, dict) and "error" in data:
|
||||||
|
return f"Error: {data['error']}"
|
||||||
|
|
||||||
|
results = data.get("organic_results", data.get("results", []))
|
||||||
|
if not isinstance(results, list):
|
||||||
|
results = []
|
||||||
|
|
||||||
|
lines.append("=== Web Results ===")
|
||||||
|
for i, r in enumerate(results[:10], 1):
|
||||||
|
lines.append(f"\n{i}. {r.get('title', 'No title')}")
|
||||||
|
lines.append(f" {r.get('link', r.get('url', ''))}")
|
||||||
|
snippet = r.get("snippet", r.get("description", ""))
|
||||||
|
if snippet:
|
||||||
|
lines.append(f" {snippet[:200]}")
|
||||||
|
|
||||||
|
return "\n".join(lines) if lines else "No results found."
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="Desearch Web Search CLI",
|
||||||
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||||
|
epilog=__doc__,
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"command", choices=["web"], help="Command to execute"
|
||||||
|
)
|
||||||
|
parser.add_argument("query", help="Search query")
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"--start",
|
||||||
|
type=int,
|
||||||
|
default=0,
|
||||||
|
help="Pagination offset (default: 0)",
|
||||||
|
)
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
results = cmd_web(args)
|
||||||
|
|
||||||
|
if isinstance(results, str):
|
||||||
|
print(results)
|
||||||
|
else:
|
||||||
|
print(json.dumps(results, indent=2))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user