Szybka odpowiedź
Jak przekonwertować stronę internetową do Markdown w Node.js?
Użyj wbudowanego fetch w Node.js 18+: wywołaj https://webreaderapi.com/api/v1/read?url=… z kluczem w nagłówku Authorization i odczytaj markdown z odpowiedzi JSON. WebReaderAPI zwraca główną treść strony wraz z tytułem, autorem i datą. Jedna strona kosztuje 1 kredyt, 2 z przeglądarką, od 0,07 USD/1K stron według stanu na październik 2026. Bez KYC; płatność kryptowalutą.
Czego potrzebuję?
Node.js 18 lub nowszy, dla globalnego fetch, oraz klucz API w zmiennej środowiskowej WEBREADER_API_KEY. Przykłady to moduły ES: zapisz je z rozszerzeniem .mjs lub ustaw "type": "module" w swoim package.json, aby działał top-level await.
Komentarze JSDoc opisują kształt odpowiedzi, więc edytory takie jak VS Code dają autouzupełnianie bez etapu kompilacji TypeScript.
/**
* @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)');
Jak odczytać listę URL-i bez przekraczania limitu?
Uruchom stałą liczbę workerów, które pobierają URL-e ze wspólnej kolejki. To prostsze niż Promise.all na całej liście, które uruchomiłoby wszystkie zapytania naraz i przekroczyło 429 concurrency_limit. Każde konto ma limit liczby stron czytanych jednocześnie; Twój dashboard go pokazuje, a support może go podnieść.
Poniższa pula trzyma błędy obok wyników, więc jedna odrzucona strona nie zatrzymuje przebiegu. Odrzucenia takie jak blocked_by_robots są ostateczne i bezpłatne; policz je i idź dalej.
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);
Jak śledzić zadanie crawlingu w Node.js?
Uruchom zadanie przez POST https://webreaderapi.com/api/v1/crawl, a następnie odczytuj https://webreaderapi.com/api/v1/jobs/{job_id} w pętli. Przekazuj cursor za każdym razem, gdy wróci next_cursor; gdy nie ma kursora, a status to done, zadanie jest zakończone. Odczekaj między pustymi odpytaniami i podwajaj czas oczekiwania aż do 30 sekund, ponieważ crawling, który respektuje Crawl-delay witryny, może być wolny.
Strona batch i crawl opisuje, jak crawling wybiera strony i jak limit ogranicza to, co odczytuje.
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);
Jak przekazać wynik do modelu?
Wyślij Markdown, a nie cały JSON. Pole markdown to treść, którą powinien czytać model; metadane lepiej zachować do cytowań. Poprzedź ją tytułem i final_url, aby model mógł podać źródło, i utrzymuj max_chars w rozmiarze mieszczącym się w kontekście modelu, np. 20 000 znaków na stronę.
Strona OpenAI function calling pokazuje pełną pętlę narzędzia w Pythonie; ta sama funkcja readPage powyżej może służyć jako treść narzędzia w agencie Node.js.
Node.js: najczęstsze pytania
Czy to działa w Deno, Bun lub w przeglądarce?
Deno i Bun obsługują ten sam fetch i moduły ES. Nie wywołuj API ze strony przeglądarki: klucz byłby widoczny dla każdego odwiedzającego. Wywołuj je ze swojego serwera.
Czy jest pakiet npm?
Nie. Funkcje na tej stronie to cała integracja. WebReaderAPI to zwykły HTTPS i JSON, więc zachowujesz kontrolę nad ponowieniami, timeoutami i logowaniem.
Jak obsłużyć 429 concurrency_limit?
Zmniejsz liczbę workerów lub ponów po krótkim oczekiwaniu, które za każdym razem się podwaja. Błąd 429 nic nie kosztuje, a strona błędów ma gotową funkcję backoffu.
Ile kosztuje odczyt 10 000 stron?
Około 10 000 kredytów dla zwykłych stron, więcej, jeśli część wymaga przeglądarki. 10 000 stron kosztują 3,75 USD; Przedpłacone kredyty nigdy nie wygasają: niewykorzystane strony pozostają na Twoim koncie, dopóki ich nie wykorzystasz.