无需 KYC — 无身份证、无自拍、无证件。加密货币支付:BTC, USDT, USDC, ETH, SOL, LTC
语言: 简体中文
获取 API 密钥

文档 · Node.js

Node.js 网页转 Markdown

Node.js 18 及更高版本自带 fetch,因此你完全不需要任何依赖。三种方案:读取一个页面、用小型并发池读取列表,以及跟踪 crawl 任务直到完成。

最后更新:

快速回答

如何在 Node.js 中将网页转换为 Markdown?

使用 Node.js 18+ 内置的 fetch:在 Authorization header 中带上你的密钥调用 https://webreaderapi.com/api/v1/read?url=…,并从 JSON 响应中读取 markdown。WebReaderAPI 返回页面的正文,以及标题、作者和日期。单个页面消耗 1 积分,使用浏览器则消耗 2 积分,价格自 2026 年 10 月起为 US$0.07/千页。无需 KYC;加密货币支付。

我需要什么?

Node.js 18 或更高版本,以使用全局 fetch,并在 WEBREADER_API_KEY 环境变量中设置 API 密钥。示例为 ES modules:保存时使用 .mjs 扩展名,或在 package.json 中设置 "type": "module",这样顶层的 await 才能正常工作。

JSDoc 注释描述了响应结构,因此 VS Code 等编辑器无需 TypeScript 构建步骤就能提供补全。

read.mjs:单个页面
/**
 * @typedef {Object} Page
 * @property {string} url
 * @property {string} final_url
 * @property {number} status_code
 * @property {boolean} rendered
 * @property {number} credits_used
 * @property {string} [title]
 * @property {string} [author]
 * @property {string} [published_at]
 * @property {string} [language]
 * @property {string} [markdown]
 * @property {number} word_count
 */

/** @returns {Promise<Page>} */
export async function readPage(url, options = {}) {
  const params = new URLSearchParams({ url, ...options });
  const res = await fetch(`https://webreaderapi.com/api/v1/read?${params}`, {
    headers: { Authorization: `Bearer ${process.env.WEBREADER_API_KEY}` },
    signal: AbortSignal.timeout(75_000),
  });
  const body = await res.json();
  if (!res.ok) throw Object.assign(new Error(body.error.message), { code: body.error.code });
  return body;
}

const page = await readPage('https://example.com/docs/getting-started', { max_chars: '20000' });
console.log(page.title, page.word_count, 'words,', page.credits_used, 'credit(s)');

如何读取 URL 列表而不触及限制?

运行固定数量的 worker,从共享队列中取出 URL。这比对整个列表使用 Promise.all 更简单,后者会同时发起所有请求并触及 429 concurrency_limit。每个账户都有同时读取页面数的上限;你的控制台会显示该上限,支持团队可将其提高。

下面的并发池会把错误与结果放在一起,因此一个被拒绝的页面不会中断整个运行。诸如 blocked_by_robots 这类拒绝是最终且免费的;记录下来然后继续。

pool.mjs:URL 列表,每次 8 个
import { readPage } from './read.mjs';

export async function readAll(urls, workers = 8) {
  const queue = [...urls];
  const results = [];
  async function worker() {
    while (queue.length) {
      const url = queue.shift();
      try {
        const page = await readPage(url, { max_chars: '50000' });
        results.push({ url, page });
      } catch (err) {
        results.push({ url, error: err.code ?? err.name });
      }
    }
  }
  await Promise.all(Array.from({ length: workers }, worker));
  return results;
}

const urls = Array.from({ length: 30 }, (_, i) => `https://example.com/blog/post-${i + 1}`);
for (const r of await readAll(urls)) console.log(r.url, '->', r.error ?? r.page.title);

如何在 Node.js 中跟踪 crawl 任务?

用 POST https://webreaderapi.com/api/v1/crawl 启动任务,然后在循环中读取 https://webreaderapi.com/api/v1/jobs/{job_id}。每当 next_cursor 返回时传入 cursor;当没有 cursor 且状态为 done 时,任务已完成。在空轮询之间等待,并将等待时间翻倍直至 30 秒,因为尊重网站 Crawl-delay 的 crawl 可能很慢。

batch 与 crawl 页面 描述了 crawl 如何选择页面,以及 limit 如何限制其读取内容。

crawl.mjs:启动 crawl 并收集每个页面
const headers = {
  Authorization: `Bearer ${process.env.WEBREADER_API_KEY}`,
  'Content-Type': 'application/json',
};
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));

const start = await fetch('https://webreaderapi.com/api/v1/crawl', {
  method: 'POST',
  headers,
  body: JSON.stringify({ url: 'https://docs.example.com/', limit: 300, include_paths: ['/docs/*'] }),
});
const { job_id } = await start.json();

const pages = [];
let cursor = '';
let wait = 2000;
for (;;) {
  const url = `https://webreaderapi.com/api/v1/jobs/${job_id}${cursor ? `?cursor=${encodeURIComponent(cursor)}` : ''}`;
  const job = await (await fetch(url, { headers })).json();
  pages.push(...job.results.filter((r) => !r.error));
  if (job.next_cursor) {
    cursor = job.next_cursor;
    wait = 2000;
    continue;
  }
  if (job.status === 'done') break;
  await sleep(wait);
  wait = Math.min(wait * 2, 30000);
}
console.log(pages.length, 'pages read, job', job_id);

如何将结果传给模型?

发送 Markdown,而不是整个 JSON。模型应读取的是 markdown 字段;元数据最好留作引用。在其前面加上标题和 final_url,以便模型能标明来源,并将 max_chars 保持在模型上下文能容纳的大小,例如每页 20,000 个字符。

OpenAI function-calling 页面 演示了 Python 中的完整工具循环;上面的同一个 readPage 函数可以作为 Node.js agent 中的工具主体。

Node.js:常见问题

这在 Deno、Bun 或浏览器中能用吗?

Deno 和 Bun 支持相同的 fetch 和 ES modules。不要从浏览器页面调用 API:密钥会暴露给每个访客。请从你的服务器调用。

有 npm 包吗?

没有。本页面的函数就是完整的集成。WebReaderAPI 就是普通的 HTTPS 和 JSON,因此你可以掌控重试、超时和日志记录。

如何处理 429 concurrency_limit?

减少 worker 数量,或短暂等待后重试,且每次将等待时间翻倍。429 不消耗任何费用,错误页面 提供了现成的退避函数。

读取 10,000 个页面需要多少费用?

普通页面约 10,000 积分,若部分页面需要浏览器则更多。10,000 个网页 花费 US$3.75;预付费积分永不过期:未使用的页面数会一直保留在你的账户中,直到你使用。

低至 US$0.07/千页 · 无需 KYC

获取 API 密钥