AI PLAY玩法更新持续收录
← 回到更新流

2026-10-10 · 原文发布日

输入软件名自动汇总全网替代品清单

自托管n8n专用的调研模板:在表单里输入一个软件产品名,工作流用DuckDuckGo搜出前十二条结果页并读取正文,代码节点只保留真正列举替代品的页面、排除产品官网、每站取一页。然后让聊天模型(默认OpenRouter的Gemini 3.8 Flash,可换任意模型节点)列出各页面提到的替代工具并引用原文句子,代码再校验引句确实存在于页面上,按独立站点数给每个工具计票,最后在表单里展示表格、读过的站点和被排除页面及原因。DuckDuckGo部分不需要API key。模板免费。

原文要点

这是n8n.io上的免费模板(作者Sam),仅支持自托管n8n,因为需要安装社区节点n8n-nodes-duckduckgo-search(Settings→Community nodes),n8n Cloud装不了社区节点。流程:表单输入软件名(可选填品类),一次DuckDuckGo搜索取前12条结果页并读正文(不需要任何API key或账号);Code节点只保留列举替代品的页面、按域名排除产品官网、每站只取一页;聊天模型默认OpenRouter的Gemini 3.8 Flash,可换成任意chat-model节点,要求模型列出各页提到的替代工具并引用原文句子;Code节点校验引句确实出现在页面上才计票,按独立站点数给每个工具计票(某站给自家产品的投票不计),引句补全到句尾。表单最后展示表格、读过的站点、被排除页面及原因。Code节点最多用8个来源、最多展示15个工具。表格只代表站点计数,不代表质量排名。作者给出的源码说明在github.com/samnodehi/n8n-nodes-duckduckgo。

这次做成什么

在自己的自托管n8n上搭一个“输入任意软件名→自动读多个评测/盘点页→输出按站点计数的替代品清单+原文引句”的调研小工具,并用一个常见软件名演练。

准备什么

  • 一台自托管n8n(版本支持社区节点)且能访问外网
  • 管理员权限,用于在Settings→Community nodes安装n8n-nodes-duckduckgo-search
  • 一个OpenRouter API key(默认走Gemini 3.8 Flash)或任何n8n chat-model节点的凭据
  • 一个用于练习的知名软件名,例如Notion或Slack
  • 明白表单会抓取搜索结果页,需保持在n8n登录保护后使用

具体怎么做

  1. 装社区节点和模型凭据

    Settings→Community nodes里安装n8n-nodes-duckduckgo-search并重启生效;Credentials里新建OpenRouter凭据填入key;确认实例能访问duckduckgo.com。

  2. 搭表单触发

    用n8n Form Trigger,添加两个字段:productName(必填,文本)、category(可选,文本,如‘笔记软件’)。保持表单在n8n登录保护之下——原作者明确提醒这一点,因为节点会去抓搜索返回的任意页面。

  3. 搜索并读页

    接DuckDuckGo搜索节点,查询词拼成 {{$json.productName}} alternatives({{$json.category}}有值时追加),Region保持us-en或改成你关心的国家;Max Results设12,Max Results for Page Content控制实际读正文的页数。

  4. 筛选来源

    Code节点做三件事:丢弃正文里没有‘列举多个替代品’特征的页面(可用是否出现多个工具名+列表结构粗判);从productName提取域名,排除该域名页面;按域名去重,每站只留一页。输出保留的sources数组和excluded数组(含排除原因)。

  5. 模型抽取+引句校验

    chat-model节点对每个保留页面用材料A的提示词,要求返回JSON数组[{tool, quote}]。接Code节点(参考材料B):strip空白后检查quote是否是页面正文的子串,不是就丢弃;同一站点对自家产品的提名不计票;按工具聚合独立站点数,按票数降序,最多取15个。

  6. 表单展示结果

    用Form Ending节点渲染三张表:替代品清单(工具名/站点数/一条引句)、已读站点列表、被排除页面及原因,并在页面上注明‘站点数不等于质量排名’。

可直接复制的材料

材料A:替代品抽取提示词

本站独立编写的示例提示词;{{productName}}和{{pageText}}需替换为n8n表达式。

你在分析一个网页,任务是找出页面中作为“{{productName}}替代品”被推荐的工具。

只输出一个JSON数组,不要markdown、不要解释。每个元素格式:
{"tool": "工具名", "quote": "从页面正文逐字复制的、明确把该工具列为替代品或竞品的完整句子"}

规则:
1. quote必须是页面原文里逐字存在的句子,不得改写、拼接或翻译。
2. 只收录被当作{{productName}}的替代/竞品提到的工具,页面顺带提及但无关的不算。
3. 如果页面没有列出任何替代品,输出空数组[]。

页面正文:
{{pageText}}

材料B:引句校验与计票的Code节点参考实现

本站独立编写的示意代码,未在原模板实测;原模板另有‘把引句补全到句尾’的处理,可在norm比对后用原文截取到下一个句点。

// n8n Code节点(Run Once for All Items),本站独立编写的参考实现
const pages = $input.all(); // 每项含 {url, domain, text, modelOutput}
const votes = new Map(); // tool -> Set(domain)
const quoteOf = new Map(); // tool -> 第一条校验通过的引句

const norm = s => s.replace(/\s+/g, ' ').trim();

for (const item of pages) {
  const { domain, text } = item.json;
  let list;
  try { list = JSON.parse(item.json.modelOutput); } catch { continue; }
  if (!Array.isArray(list)) continue;
  for (const { tool, quote } of list) {
    if (!tool || !quote) continue;
    if (!norm(text).includes(norm(quote))) continue; // 引句必须逐字存在于页面
    const key = tool.trim();
    if (key.toLowerCase().includes(domain.split('.')[0])) continue; // 本站给自家产品投票不计
    if (!votes.has(key)) { votes.set(key, new Set()); quoteOf.set(key, quote); }
    votes.get(key).add(domain); // 每域名一票
  }
}

const table = [...votes.entries()]
  .map(([tool, set]) => ({ tool, sites: set.size, quote: quoteOf.get(tool) }))
  .sort((a, b) => b.sites - a.sites)
  .slice(0, 15);
return [{ json: { table } }];

材料C:来源筛选的判定字段约定

本站设计的字段方案,方便你在排除页展示‘为什么被排除’。

Code节点输入项约定(每项一个页面):
url: 页面完整URL
domain: 从URL提取的主域名,如 g2.com
text: 页面正文纯文本(HTML已剥离)
modelOutput: 模型对该页的原始字符串输出

排除原因枚举(写入excluded数组):
own_site: 域名与productName匹配,判定为官网
no_list: 正文未呈现替代品列表结构
duplicate_domain: 同站已有页面入选
fetch_failed: 页面读取失败

输入与输出示例

本站虚构演示,引句为编写示例、计票数字与输入假设对应,非真实搜索结果。

输入

productName = Notion,category = 笔记软件(本站虚构演练:假设12条结果中notion.so官网2条、抓取失败1条,其余9条可读;9条中有3条因域名重复被去重,最终剩6个独立站点)

对应输出

表单展示表格前3行:Obsidian | 5 | “If you want a local-first alternative to Notion, Obsidian keeps your notes as plain Markdown files.”;Logseq | 4 | “Logseq is an open-source outliner often recommended as a Notion alternative.”;Anytype | 3 | “Anytype positions itself as a privacy-focused Notion alternative.”;已读站点6个,被排除页6条:notion.so×2(own_site)、同域重复页×3(duplicate_domain)、某论坛页×1(fetch_failed)

做完检查这些

  • 输入一个软件名后,表单页出现三张表:替代品清单、已读站点、被排除页面及原因
  • 清单中每个工具的引句能在对应来源页面正文中逐字找到(norm后子串匹配)
  • 同一域名的多个页面只贡献一票,官网域名页面全部出现在排除列表
  • 把productName换成小众软件、结果不足12条时,流程不报错,表格按实际站点数展示

容易卡住的地方

  • 仅自托管n8n可用,n8n Cloud无法安装社区节点,这是硬前提
  • 搜索节点会抓取任意结果页,务必保持表单在登录保护后,别公开暴露
  • 模型可能编造引句,所以引句校验这步不能省,否则计票失真
  • DuckDuckGo无需key但有频控,连续调试太密会被限流,间隔几分钟再试
  • 原作者说明计数最多8个来源、展示最多15个工具,别期待全网穷尽

原文与作者资料 ↗

本站借助AI整理来源事实,独立编写中文操作指南与示例。示例不代表实际运行结果;来源效果尚未由本站独立复现。付费模板与模型、服务费用分别计算。

原文标题
Find software alternatives with DuckDuckGo and OpenRouter Gemini
原作者
Sam
原始发布日期
2026-10-10T13:33:04.452Z
来源
https://n8n.io/workflows/20738