ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

9Router Web Fetch 使用指南:通过统一 API 将任意网页转换为 Markdown

9Router Web Fetch 使用指南:通过统一 API 将任意网页转换为 Markdown 9Router Web Fetch 使用指南通过统一 API 将任意网页转换为 Markdown【免费下载链接】9routerUnlimited FREE AI coding. Connect Claude Code, Codex, Cursor, Cline, Copilot, Antigravity to FREE Claude/GPT/Gemini via 40 providers. Auto-fallback, RTK -40% tokens, never hit limits.项目地址: https://gitcode.com/GitHub_Trending/9r/9router导读本文围绕 9Router 的web-fetch能力展开它把 Firecrawl、Jina Reader、Tavily Extract、Exa Contents 四类网页抓取服务统一封装为一个 OpenAI 风格的 REST 端点POST /v1/web/fetch让 AI Agent 只需面对一套参数URL、模型、格式、截断长度即可完成网页抓取、文章阅读与 URL 转 Markdown 等任务并获得跨提供方完全一致的返回结构。读完本文你将掌握如何发现可用抓取模型、发起单提供方请求、使用fetch-combo组合与自动故障转移并理解服务端在鉴权、SSRF 防护、多账号回退上的底层实现。前置条件与配置使用web-fetch前需要先具备 9Router 实例的运行环境环境变量NINEROUTER_URL9Router 服务的基础地址例如http://localhost:3000。环境变量NINEROUTER_KEY若服务端开启了鉴权requireApiKey则需要提供 API Key本地未开启鉴权时可省略。完整的安装与初始化流程参见仓库内 skills/9router/SKILL.md。抓取接口本身是纯 HTTP 服务无需安装任何额外的 SDKcurl 即可调用。发现可用的抓取模型在发起抓取前可以先枚举当前实例支持哪些webFetch提供方curl $NINEROUTER_URL/v1/models/web | jq .data[] | select(.kindwebFetch) | .id该查询对应 src/app/api/v1/models/[kind]/route.js 中的 kind 路由web这一 URL 段被映射为[webSearch, webFetch]两类服务能力返回结果中每个条目的kind字段会标明webSearch或webFetchAI Agent 可以根据kind精确过滤出“能抓网页”而不是“只能搜网页”的模型。若需要查看某个提供方的专属参数例如 Jina Reader 的格式支持范围使用curl $NINEROUTER_URL/v1/models/info?idfirecrawl/fetch抓取类模型的 ID 统一以/fetch结尾如firecrawl/fetch、jina/fetch而fetch-combo是一个特殊的组合模型 ID它会把多个提供方串联起来并按策略自动故障转移fallback避免单个上游服务不可用或配额耗尽导致抓取失败。请求端点与参数抓取请求统一发送到POST $NINEROUTER_URL/v1/web/fetch字段必填说明model或provider是来自/v1/models/web的提供方 ID如firecrawl、jina-reader、tavily、exa或组合名fetch-combourl是需要提取内容的网页 URLformat否输出格式markdown默认/text/htmlmax_characters否截断输出的最大字符数0表示不截断关于model与provider的兼容服务端解析时接受body.provider || body.model两者之一见 src/sse/handlers/fetch.jsUI 层通常发送model因为对webFetch而言“提供方就是模型”。format与max_characters会透传给底层处理器max_characters为0或负数时表示不截断由 open-sse/handlers/fetch/index.js 中的truncate函数实现!max || max 0时原样返回。四类提供方的调用示例Jina Reader免费额度最高的纯 Markdown 方案curl -X POST $NINEROUTER_URL/v1/web/fetch \ -H Authorization: Bearer $NINEROUTER_KEY \ -H Content-Type: application/json \ -d {model:jina-reader,url:https://9router.com,format:markdown}Exa预索引页面快速文本提取curl -X POST $NINEROUTER_URL/v1/web/fetch \ -H Authorization: Bearer $NINEROUTER_KEY \ -H Content-Type: application/json \ -d {model:exa,url:https://example.com,format:markdown,max_characters:0}Firecrawl支持 JS 渲染页面curl -X POST $NINEROUTER_URL/v1/web/fetch \ -H Authorization: Bearer $NINEROUTER_KEY \ -H Content-Type: application/json \ -d {model:firecrawl,url:https://example.com,format:markdown,max_characters:0}Tavily批量提取返回raw_contentcurl -X POST $NINEROUTER_URL/v1/web/fetch \ -H Authorization: Bearer $NINEROUTER_KEY \ -H Content-Type: application/json \ -d {model:tavily,url:https://example.com,format:markdown,max_characters:0}Node.js 调用组合模型 截断const r await fetch(${process.env.NINEROUTER_URL}/v1/web/fetch, { method: POST, headers: { Authorization: Bearer ${process.env.NINEROUTER_KEY}, Content-Type: application/json }, body: JSON.stringify({ model: fetch-combo, url: https://example.com, format: markdown, max_characters: 5000 }), }); const { data } await r.json(); console.log(data.title, data.content.length);这里使用fetch-combo的好处是即使首选提供方超时或返回错误9Router 会自动切换到组合内的其他提供方对调用方完全透明。统一响应结构无论底层调用的是哪个提供方返回的数据结构都被归一化为同一形状{ provider: jina-reader, url: ..., title: ..., content: { format: markdown, text: ..., length: 1234 }, metadata: { author: null, published_at: null, language: null }, usage: { fetch_cost_usd: 0 }, metrics: { response_time_ms: 850, upstream_latency_ms: 700 } }字段说明provider实际执行抓取的提供方 ID组合模式下会反映出真正命中者title页面标题。Jina 通过解析响应文本中的Title:元数据或首个#一级标题得到见 open-sse/handlers/fetch/index.js 的parseJinaTitlecontent.text提取到的正文content.length为字符数metadata作者、发布时间、语言当前统一占位为nullusage.fetch_cost_usd单次抓取的成本来自提供方注册表的costPerQuerymetricsresponse_time_ms为服务端总耗时upstream_latency_ms为上游请求耗时便于排查慢请求。该统一结构由 open-sse/handlers/fetch/index.js 中的buildData函数生成四个上游分支runFirecrawl、runJina、runTavily、runExa最终都汇入同一个响应骨架。提供方特性对比与选型SKILL 文档给出了一份快速选型表结合仓库内各提供方注册表open-sse/providers/registry/可以进一步量化提供方鉴权方式最佳场景costPerQuery免费月配额注册表maxCharacters上限默认超时firecrawlBearerJS 渲染页面formatmarkdown/html0.002 USD50020000030000 msjina-readerBearer可选免费档约 100 万字符/月纯 Markdown 最快0100000020000030000 mstavilyBearer批量提取返回raw_content0.008 USD100010000015000 msexax-api-key预索引页面快速文本提取0.001 USD100010000015000 ms各注册表文件中的fetchConfig还声明了可用的formatsFirecrawl 支持markdown/html/text注册表 firecrawl.jsJina Reader 声明支持markdown/text/html注册表 jina-reader.jsTavily 声明markdown/text注册表 tavily.jsExa 声明text/markdown注册表 exa.js。选型建议追求零成本与速度首选 Jina Reader其免费月配额为 100 万字符costPerQuery为 0目标页面依赖 JavaScript 渲染SPA、动态内容选 Firecrawl需要与搜索能力共用同一账号/配额Tavily 与 Exa 同时注册了webSearch与webFetch两种服务能力可以考虑这两家追求稳定与免维护直接使用fetch-combo组合模型。服务端实现原理从请求到返回1. 路由入口公开端点定义在 src/app/api/v1/web/fetch/route.jsPOST直接委托给handleFetch并附带宽松的 CORS 预检允许任意 Origin便于浏览器端与 Agent 侧直接调用。2. 请求校验与安全防线src/sse/handlers/fetch.js 在进入业务逻辑前做了四层防护JSON 解析失败返回 400若服务端设置requireApiKeytrue缺失或无效的Authorization: Bearer请求会被 401 拒绝与open-sse其他 API 端点一致的鉴权策略provider/model与url缺失返回 400URL 先经new URL(targetUrl)格式校验再通过assertPublicUrl来自 src/shared/utils/ssrfGuard.js做 SSRF 防护拒绝内网、私网段与云元数据地址等非公网目标命中即返回 400。3. 组合模型展开当请求中的providerInput命中已保存的组合combo时服务端会从本地数据库读取组合配置getCombos将请求交给handleComboChat按策略分发默认fallback故障转移并支持组合级的comboStrategies覆盖与comboStickyRoundRobinLimit粘性轮询设置。这就是fetch-combo自动容灾的来源。4. 单提供方执行与多账号回退单提供方路径handleSingleProviderFetch的核心逻辑是“凭据 回退循环”通过resolveProviderId解析提供方并校验其注册表存在fetchConfig否则说明该提供方不支持网页抓取循环调用getProviderCredentials获取可用账号凭据可排除指定连接对 OAuth 类账号先执行checkAndRefreshToken刷新令牌调用handleFetchCore执行真实抓取成功则clearAccountError并返回归一化 JSON失败则markAccountUnavailable记录该连接不可用若shouldFallback为真将当前连接加入排除集合并继续尝试下一个账号直到所有账号耗尽。5. 上游请求细节open-sse/handlers/fetch/index.js 中每个上游分支都基于tryFetch默认超时 15 秒可被providerConfig.timeoutMs覆盖使用AbortController中止超时请求并将超时区分映射为 504请求头经sanitizeHeaders清理非 ASCII 字符以保证 HTTP 头合法。四家上游端点分别为FirecrawlPOST https://api.firecrawl.dev/v1/scrapebody 为{ url, formats: [fmt] }Jina ReaderPOST https://r.jina.ai/body 为{ url }TavilyPOST https://api.tavily.com/extractbody 为{ urls: [url], extract_depth: basic }ExaPOST https://api.exa.ai/contentsbody 为{ ids: [url], text: true }。各上游响应的正文提取策略不同Firecrawl 取data.markdown || data.html || data.text与data.metadata.titleJina 直接取响应文本并用parseJinaTitle解析标题Tavily 取results[0].raw_contentExa 取results[0].text与results[0].title。最终统一经过truncate按maxCharacters截断后进入buildData。在 AI Agent / Skill 中的实战姿势该 SKILL 文件skills/9router-web-fetch/SKILL.md本身就是为 AI Agent 设计的“工具使用说明书”Agent 读取其 frontmatter 中的description即可判断“何时该用”抓网页、提取 URL 内容、读文章、URL 转 Markdown。实战建议抓取前先用/v1/models/web确认提供方存在并用kindwebFetch过滤对不确认是否可抓的目标 URL 使用fetch-combo利用自动故障转移提升成功率长网页务必设置max_characters如 5000控制 Token 消耗0表示完整返回若只需要纯文本给 LLM 喂上下文formattext或markdown均可需要保留原始结构排查问题时用html前提是该提供方注册表中声明了该格式通过响应中的usage.fetch_cost_usd与metrics字段监控成本与延迟选择性价比合适的提供方。小结9Router 的 Web Fetch 能力本质上是“一个端点、统一契约、多家上游”对外暴露POST /v1/web/fetch与 OpenAI 风格的模型发现接口对内完成鉴权、SSRF 防护、账号凭据管理、令牌刷新、故障转移与响应归一化。无论底层是 Firecrawl 的 JS 渲染抓取、Jina Reader 的高性价比 Markdown、Tavily 的批量提取还是 Exa 的预索引内容调用方拿到的都是同一份 JSON 结构。这使得 AI Agent 可以用极少的代码稳定接入任意网页内容作为 RAG 资料采集、新闻阅读、文档抓取等场景的统一基础设施。【免费下载链接】9routerUnlimited FREE AI coding. Connect Claude Code, Codex, Cursor, Cline, Copilot, Antigravity to FREE Claude/GPT/Gemini via 40 providers. Auto-fallback, RTK -40% tokens, never hit limits.项目地址: https://gitcode.com/GitHub_Trending/9r/9router创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表