OpenCLI DuckDuckGo 适配器实战:浏览器搜索 + 补全建议双命令深度解析
2026/9/19 21:41:37 网站建设 项目流程

OpenCLI DuckDuckGo 适配器实战:浏览器搜索 + 补全建议双命令深度解析

【免费下载链接】OpenCLIMake Any Website into CLI & Use your logged-in browser by AI agent.项目地址: https://gitcode.com/gh_mirrors/ope/OpenCLI

本文以 OpenCLI 仓库中的 DuckDuckGo 适配器文档(docs/adapters/browser/duckduckgo.md)为主体,结合其源码实现(clis/duckduckgo/search.js、clis/duckduckgo/suggest.js)与测试用例,完整讲解opencli duckduckgo searchopencli duckduckgo suggest两条命令的参数语义、底层原理与使用边界。读完本文,你将能够在终端中完成 DuckDuckGo 的区域化检索、时间过滤、跨页分页、结果提取与搜索词联想,并理解浏览器模式与纯 HTTP API 两种适配器实现路线的差异。

适配器总览

DuckDuckGo 适配器属于 OpenCLI 的公开(Public)站点适配器,工作模式为 🌐 公开访问,覆盖两个域名:

域名用途
html.duckduckgo.comsearch命令使用的 HTML 精简版搜索结果页
duckduckgo.comsuggest命令使用的补全 API 所在域名

适配器对外暴露两条命令:

CommandDescription
opencli duckduckgo search <keyword>Search DuckDuckGo and extract results from the page
opencli duckduckgo suggest <keyword>Get DuckDuckGo search suggestions

从源码注册信息可以看到两条命令的元数据:search为只读(access: 'read')、公开策略(Strategy.PUBLIC)、需要浏览器(browser: true);suggest同样为只读公开策略,但browser: false,即完全不需要浏览器环境(见 clis/duckduckgo/search.js 与 clis/duckduckgo/suggest.js)。

search命令:浏览器模式下的结果提取

参数语义

search命令共支持 5 个参数,其中keyword为必填位置参数,其余均有默认值:

参数类型默认值说明
keywordstring(位置参数)必填搜索关键词
--limitint10单页结果数,范围1~10(HTML 版每页最多 10 条)
--offsetint0分页偏移量,必须是10 的倍数(0、10、20…),内部走 XHR POST
--regionstring全部区域区域代码,如jp-jpus-encn-zh
--timestring时间范围:d(天)、w(周)、m(月)、y(年)

这些参数并非"文档建议",而是在源码中被严格校验后才会发起网络请求:

  • --limit通过共享工具requireBoundedInteger校验,越界直接抛出ArgumentError(见 clis/duckduckgo/search.js);
  • --offset通过requireNonNegativeInteger校验后,还会单独检查offset % 10 !== 0并抛出--offset must be a multiple of 10 for DuckDuckGo HTML pagination错误(clis/duckduckgo/search.js);
  • --time必须匹配^(d|w|m|y)$正则,否则报错(clis/duckduckgo/search.js)。

输出列结构

search的结果以表格/JSON 形式输出,固定 7 个列(源码columns定义于 clis/duckduckgo/search.js):

列名含义
rank排名,从 1 开始;分页时等于index + 1 + offset
title结果标题
url解码后的真实目标 URL(已处理uddg=重定向)
snippet摘要文本,取自.result__snippet节点
displayUrl页面展示的 URL 文本
icon站点图标地址,取自.result__icon__img
resultType结果类型:web/news/video/image

输出格式与 OpenCLI 全局约定一致,search同样支持-f json-f yaml-f csv-f md等格式化输出,便于管道传递给jq或 LLM 消费。

使用示例

# Basic search opencli duckduckgo search "machine learning" # Limit results opencli duckduckgo search "machine learning" --limit 5 # Region-specific search opencli duckduckgo search "machine learning" --region jp-jp # Time filter (past week) opencli duckduckgo search "machine learning" --time w # Pagination (second page) opencli duckduckgo search "machine learning" --offset 10 # JSON output opencli duckduckgo search "machine learning" -f json # Search suggestions opencli duckduckgo suggest "machine" --limit 5

请求构造细节

search的请求 URL 在源码中按如下规则拼装(clis/duckduckgo/search.js):

https://html.duckduckgo.com/html/?q=<编码后的关键词>[&kl=<区域代码>][&df=<时间范围>]

其中kl是 DuckDuckGo 的区域(region)参数,df是时间范围参数。区域代码遵循 DuckDuckGo 自己的格式(如jp-jpus-enuk-en),默认不传即表示全部区域。区域与时间过滤均以用户传入值为准直接透传,未做白名单枚举,因此传入有效区域代码即可生效。

suggest命令:免浏览器的公开 JSON API

search不同,suggest走的是 DuckDuckGo 的公开补全接口duckduckgo.com/ac/,全程使用 Node 内置fetch,不依赖 Chrome 浏览器。

参数与实现

参数类型默认值说明
keywordstring(位置参数)必填搜索词前缀
--limitint8返回建议数量上限,范围1~20

实现要点(见 clis/duckduckgo/suggest.js):

  1. 校验keyword非空、limit在 1~20 之间;
  2. 请求https://duckduckgo.com/ac/?q=<关键词>&type=list
  3. 解析 JSON,取数组第二个元素(data[1])作为建议短语列表;
  4. 过滤空字符串,按limit截断,逐条映射为{ phrase }行输出,输出列为['phrase']

fetch失败(网络错误、非 2xx 状态码、畸形 JSON)时,分别被包装为带COMMAND_EXEC错误码的CommandExecutionError,保证错误可被程序化捕获而非静默吞掉(clis/duckduckgo/suggest.js)。

使用示例

# 基本联想 opencli duckduckgo suggest "machine" # 指定建议数量 opencli duckduckgo suggest "opencli" --limit 5 # JSON 输出便于程序消费 opencli duckduckgo suggest "machine" -f json

底层原理:从源码看两条命令的工程实现

1.uddg=重定向解码

DuckDuckGo 的搜索结果链接统一经过uddg=参数跳转(形如/l/?uddg=https%3A%2F%2Fexample.com%2F...),适配器在decodeDdgUrl函数中解析该参数并还原出干净的最终 URL;解码失败时退回原href,并统一通过toHttpsUrl保证只输出http/https协议地址(见 clis/duckduckgo/search.js 与共享工具 clis/_shared/search-adapter.js)。测试用例 clis/duckduckgo/search.test.js 专门验证了/l/?uddg=...https://github.com/jackwener/OpenCLI的解码正确性。

2. 浏览器内 DOM 提取脚本

search之所以必须走浏览器模式,是因为 DuckDuckGo 存在反爬保护,普通 HTTP 请求难以稳定获取结果。适配器通过page.evaluate在页面上下文内执行一段注入脚本(buildExtractFn),其提取逻辑为(clis/duckduckgo/search.js):

  • 遍历.result节点;
  • 通过 CSS 类名识别并跳过广告结果result--adresult--adsbadge--ad);
  • 分别抓取标题.result__a、摘要.result__snippet、展示 URL.result__url、图标.result__icon__img
  • 通过类名判定结果类型:news-resultnewsvideo-resultvideoimage-resultimage,否则为web
  • 以 href 为 key 去重,按limit提前截断。

测试用例使用JSDOM模拟了广告结果与自然结果的混合 DOM,验证广告被过滤、自然结果被正确提取并映射为规范行结构(clis/duckduckgo/search.test.js)。

3. 分页为何用 XHR POST

DuckDuckGo HTML 版的分页跳转依赖form.submit()行为,在浏览器自动化场景下容易引发页面导航问题。适配器因此改用页面内 XHR POST实现翻页:在首屏加载后,通过XMLHttpRequest/html/提交表单编码参数(qs=<offset>v=lo=json、可选kl),再用DOMParser解析返回的 HTML 并复用同一套提取函数(见buildPaginateJs,clis/duckduckgo/search.js)。

这种方式避免了二次页面导航,同时让--offset与结果rank精确对齐(第 2 页首条即rank: 11,测试见 clis/duckduckgo/search.test.js)。代价是分页边界处结果可能与上一页存在少量重叠,且--offset必须是 10 的倍数。

4. 共享校验与错误封装

两条命令复用clis/_shared/search-adapter.js中的一组工具函数,这体现了 OpenCLI 搜索类适配器的通用工程约束(clis/_shared/search-adapter.js):

  • requireSearchQuery:关键词去空格后非空校验;
  • requireBoundedInteger/requireNonNegativeInteger:整数区间校验;
  • requireRows/unwrapBrowserResult:兼容浏览器会话信封({session, data})与纯数组两种载荷形态,载荷形状异常时抛出带COMMAND_EXEC码的类型化错误;
  • runBrowserStep:将浏览器步骤(导航、提取)的底层异常统一包装为类型化CommandExecutionError
  • emptySearchResults:结果为空时抛出EmptyResultError

search测试中有一例专门验证:当提取载荷形状不符合预期(如{rows: []})时,命令以COMMAND_EXEC错误失败,而不是静默返回空数组(clis/duckduckgo/search.test.js)。suggest测试则验证了空关键词/超限 limit 在fetch之前就被拦截(clis/duckduckgo/suggest.test.js)。

前置条件与运行环境

  • suggest不需要 Chrome,任何能运行 OpenCLI 的环境均可直接使用;
  • search需要 Chrome 处于运行状态(Standalone 模式会自动启动),或安装并启用 Browser Bridge 扩展(详见 docs/guide/browser-bridge.md)。

Browser Bridge 是 OpenCLI 连接浏览器会话的轻量方案:安装扩展后,守护进程(daemon)会在首次执行浏览器命令时自动启动,无需手工配置。可用opencli doctor检查扩展与守护进程的连通性。需要强调的是,浏览器命令复用的是你 Chrome 中已登录的会话状态,凭证不会离开浏览器;DuckDuckGo 为公开站点,无登录要求,但浏览器模式本身仍是其反爬防护下最稳定的取数路径。

已知限制与注意事项

结合文档与源码,使用本适配器时需注意以下边界:

  1. search必须使用浏览器模式:由于 DuckDuckGo 的反爬保护,纯 HTTP 方式不可行;
  2. 单页最多 10 条结果:HTML 版每页固定返回最多 10 条,--limit只能在 1~10 之间取值,需要更多结果请用--offset翻页;
  3. 分页结果可能重叠:分页走 POST 导航,页面边界处可能与前页结果重复;
  4. 摘要提取依赖 DOM 结构:摘要取自 HTML 版的.result__snippet节点,若 DuckDuckGo 调整前端结构,提取逻辑(clis/duckduckgo/search.js)需同步跟进;
  5. CJK 关键词联想为拼音近似ac/补全 API 对中文等 CJK 查询返回的是拼音音近建议(phonetic suggestions),可能与预期结果不完全一致;
  6. 区域代码格式:遵循 DuckDuckGo 自身格式(如jp-jpus-enuk-en),默认全部区域。

测试与质量保障

本适配器带有完整的 Vitest 测试套件,是理解其行为契约的最佳入口:

  • clis/duckduckgo/search.test.js:覆盖命令注册元数据、参数校验前置拦截、uddg=解码、广告过滤与 DOM 提取、分页信封解包、畸形载荷的类型化报错;
  • clis/duckduckgo/suggest.test.js:覆盖命令注册、参数前置校验、公开 API 载荷解析与过滤、网络/JSON 异常映射为类型化错误。

这些测试同时印证了本文所述的全部参数边界与错误语义——ARGUMENT类错误在发请求前抛出,COMMAND_EXEC类错误统一封装底层异常,空结果抛出EMPTY_RESULT。在 OpenCLI 中编写或审计新适配器时,DuckDuckGo 适配器是一个兼具"浏览器模式 + 纯 API 模式"双路实现的参考范例。

【免费下载链接】OpenCLIMake Any Website into CLI & Use your logged-in browser by AI agent.项目地址: https://gitcode.com/gh_mirrors/ope/OpenCLI

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询