JavaScript and Node.js libraries for web scraping
Scraping
12 minutes reading time

JavaScript Web Scraping Libraries: What to Use in 2026

Table of contents

The short answer: use fetch + Cheerio if the data is in the HTML source, Playwright if it isn't, and Crawlee once you need queues, retries and concurrency. Everything else in the Node scraping ecosystem is either a niche parser or, in several well-known cases, abandoned.

That last part is the reason to re-read this list. Several libraries that every "best of" article recommended in 2024 have not shipped a release in three years — including the stealth plugin most tutorials still tell you to install. Every version and date below was checked against the npm registry and GitHub on 2026-07-28.

Key Takeaways

  • puppeteer-extra-plugin-stealth is dead. Last npm release 2023-03-01, last repo commit 2024-07-18, 273 open issues. It is still the top recommendation in most 2024-era tutorials. Use Patchright or Camoufox instead.
  • node-fetch has not shipped a release since 2023-07-25. You don't need it — Node 18+ has a global fetch() backed by undici.
  • Nightmare's last release was 2019-04-27. Seven years ago. Any 2026 article still listing it did not check.
  • Cheerio only parses; it never fetches. It cannot execute JavaScript, so a React or Vue page returns an empty shell no matter what selector you write.
  • Playwright ships roughly 10× more often than Puppeteer — 343 releases in 2026 vs 27 — which is why it tracks browser changes faster.
  • Rule of thumb: curl the URL first. If your data is in the response, a headless browser costs you ~10× the CPU for nothing.

Which JavaScript web scraping library should I use?

JavaScript web scraping library decision guide

Work down this list and stop at the first match:

  1. Data is in the HTML sourcefetch + Cheerio. Fastest, ~50 MB of RAM, no browser.
  2. Data comes from an XHR/JSON call → open DevTools, find the API endpoint, fetch it directly. No parser needed at all.
  3. Data is rendered client-side → Playwright. Puppeteer if you're Chrome-only and already invested.
  4. You need thousands of pages with retries, queues and proxy rotation → Crawlee, which wraps either of the above.
  5. You're getting blocked despite all of the above → a scraping API, so the anti-bot arms race isn't your problem.

The single biggest performance mistake in Node scraping is starting at step 3. A headless browser is a few hundred megabytes of Chrome per instance; an HTTP request is a socket.

The 2026 decision table

Versions and dates verified against the npm registry on 2026-07-28.

LibraryVersionWhat it's forWhen it breaksMaintained in 2026?
fetch (built-in)Node 18+HTTP requests, zero depsNo cookie jar, no retries, easy to fingerprint✅ Core Node
undici8.9.0The engine behind fetch; proxies, connection poolsLower-level API than axios✅ 48 releases in 2026
axios1.18.1HTTP with interceptors and timeoutsWon't run JavaScript✅ 2026-06-22
got-scraping4.2.1HTTP with browser-like TLS/header fingerprintsHeader spoofing alone rarely beats modern WAFs✅ 2026-02-24
cheerio1.2.0jQuery-style HTML parsingStatic HTML only — no JS execution✅ Repo active; slow releases
jsdom30.0.0Full DOM + limited script execution10–50× slower than Cheerio; not a real browser✅ 2026-07-27
htmlparser212.0.0Streaming parser, fastest optionLow-level; you handle the tree✅ 2026-03-20
node-html-parser9.0.0Lightweight DOM-ish parsingSmaller selector support than Cheerio✅ 2026-07-06
linkedom0.18.13DOM built for server-side renderingNot aimed at scraping specifically✅ 2026-07-07
puppeteer25.4.0Chrome/Chromium automationChrome-family only; detectable by default✅ 27 releases in 2026
playwright1.62.0Chromium, Firefox and WebKit automationHeavy; ~500 MB of browsers on install✅ 343 releases in 2026
selenium-webdriver4.46.0Cross-language, cross-browser via W3C protocolSlowest setup; extra driver process✅ 2026-07-11
crawlee3.17.0Full framework: queue, retries, proxies, storageOverkill under ~100 pages✅ 240 releases in 2026
patchright1.61.1Patched Playwright that removes automation leaksCat-and-mouse; no guarantees✅ 2026-07-16
camoufox-js0.11.5Firefox build hardened against fingerprintingFirefox only; larger download✅ 2026-07-27

Which JavaScript scraping libraries are dead in 2026?

This is where most "best JavaScript web scraping library" listicles go stale. These packages still appear in top-ranking guides and still install cleanly from npm — which is exactly why people don't notice.

LibraryLast npm releaseLast repo commitWhat to use instead
puppeteer-extra + plugin-stealth2023-03-012024-07-18Patchright, Camoufox, or a scraping API
playwright-extra2023-03-012024-07-18Patchright
node-fetch2023-07-252026-05-12Built-in fetch (Node 18+)
nightmare2019-04-272024-04-20Playwright
x-ray2019-07-15Crawlee
osmosis2019-03-01Crawlee
rebrowser-patches2025-05-082025-05-09Stalled ~14 months; prefer Patchright

The stealth plugin deserves special attention. puppeteer-extra-plugin-stealth works by patching the specific detection signals that existed in 2022. Anti-bot vendors have shipped hundreds of releases since; the plugin has shipped none. Installing it in 2026 gives you a false sense of protection and a dependency that hasn't tracked three years of Chrome changes.

Also note jQuery, which several ranking articles still list. jQuery is a browser DOM library, not a scraping tool — on the server you want Cheerio, which is the jQuery API implemented over a parsed HTML string.

Cheerio vs Puppeteer: which one do you actually need?

Cheerio and Puppeteer compared for JavaScript scraping

They don't compete — they solve different halves of the problem. Cheerio parses HTML you already have. Puppeteer produces HTML by driving a browser.

Test which one you need in one command:

curl -s https://example.com/products | grep -c "product-item"

Non-zero means the data is server-rendered and Cheerio is enough. Zero means it's built client-side and you need a browser.

Cheerio with the built-in fetch — no axios, no node-fetch:

import * as cheerio from 'cheerio';

const res = await fetch('https://example.com/products', {
  headers: { 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) ' +
                           'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36' },
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);

const $ = cheerio.load(await res.text());

const products = $('.product-item').map((_, el) => ({
  name:  $(el).find('.product-name').text().trim(),
  price: parseFloat($(el).find('.price').text().replace(/[^0-9.]/g, '')),
  url:   new URL($(el).find('a').attr('href'), 'https://example.com').href,
})).get();

console.log(products);

Two details worth copying: res.ok is checked explicitly because fetch does not throw on a 404 or 403, and relative hrefs are resolved with new URL() rather than string concatenation. Rotating that User-Agent across requests matters more than most people expect — see user agent rotation for web scraping.

For selector syntax beyond CSS, the XPath cheat sheet covers what Cheerio's CSS selectors can't express.

Puppeteer vs Playwright in 2026

Puppeteer vs Playwright browser automation comparison

Both drive real browsers over the same underlying protocols, and the APIs are close enough that porting a script takes minutes. The differences that actually decide it:

Puppeteer 25.4.0Playwright 1.62.0
BrowsersChrome, Chromium, Firefox (partial)Chromium, Firefox, WebKit
Release cadence 202627 releases343 releases
Auto-waitingManual waitForSelectorBuilt into every locator
ParallelismOne page at a time per browserBrowser contexts, isolated and cheap
Install sizeChrome only~500 MB for all three engines
MaintainerGoogle (Chrome DevTools team)Microsoft

Choose Playwright for new projects. Auto-waiting locators alone remove the most common source of flaky scrapers — the waitForTimeout(2000) guess. Browser contexts let you run isolated sessions, each with its own cookies and proxy, inside one browser process.

Choose Puppeteer if you're Chrome-only, want a smaller install, or already have a working codebase. It is not abandoned — 27 releases in 2026 is healthy maintenance.

One migration gotcha: page.waitForTimeout() was removed from Puppeteer's Page API in v22 (2024-02-05). Scripts copied from older tutorials will throw page.waitForTimeout is not a function. Wait on a condition instead:

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch({ headless: true });
try {
  const page = await browser.newPage();
  await page.goto('https://example.com/products', { waitUntil: 'domcontentloaded' });
  await page.waitForSelector('.product-item');          // not waitForTimeout

  const products = await page.$$eval('.product-item', els => els.map(el => ({
    name:  el.querySelector('.product-name')?.textContent.trim(),
    price: el.querySelector('.price')?.textContent.trim(),
  })));
  console.log(products);
} finally {
  await browser.close();                                 // always, or you leak Chrome processes
}

The Playwright equivalent, with the wait implied by the locator:

import { chromium } from 'playwright';

const browser = await chromium.launch();
try {
  const page = await browser.newPage();
  await page.goto('https://example.com/products');

  const products = await page.locator('.product-item').evaluateAll(els => els.map(el => ({
    name:  el.querySelector('.product-name')?.textContent.trim(),
    price: el.querySelector('.price')?.textContent.trim(),
  })));
  console.log(products);
} finally {
  await browser.close();
}

Full API coverage lives in the Puppeteer web scraping guide and the Playwright web scraping guide.

Playwright vs Selenium for scraping

Selenium is not obsolete — selenium-webdriver 4.46.0 shipped 2026-07-11 — but for Node scraping specifically, Playwright wins on nearly every axis.

PlaywrightSelenium
ArchitectureDirect protocol connectionW3C WebDriver via a separate driver process
Setupnpm i playwright downloads browsersDriver binaries to install and version-match
WaitingAuto-waiting locatorsExplicit waits you write yourself
Language supportJS/TS, Python, Java, .NETNearly every language
Network interceptionFirst-classLimited (CDP only, Chrome)

Pick Selenium when you need a language Playwright doesn't support, must test a real Safari or Edge grid, or already run Selenium Grid infrastructure. Pick Playwright for everything else. The extra driver process is Selenium's biggest practical tax: version drift between the browser and its driver is a recurring source of CI breakage that Playwright simply doesn't have.

If you're scraping from Python rather than Node, the Python Selenium guide and the Python web scraping libraries comparison cover that side.

When you need a framework: Crawlee

Crawlee (3.17.0, 240 releases in 2026) is the only mature all-in-one scraping framework in the Node ecosystem — the rough equivalent of Scrapy in Python. It gives you a persistent request queue, automatic retries with backoff, proxy rotation, concurrency limits, and a storage layer, and it runs on top of Cheerio, Puppeteer or Playwright with the same API.

import { PlaywrightCrawler } from 'crawlee';

const crawler = new PlaywrightCrawler({
  maxRequestsPerCrawl: 500,
  maxConcurrency: 10,
  maxRequestRetries: 3,

  async requestHandler({ page, request, enqueueLinks, pushData }) {
    await pushData({
      url:   request.url,
      title: await page.title(),
      price: await page.locator('.price').first().textContent(),
    });
    await enqueueLinks({ selector: '.pagination a' });
  },

  failedRequestHandler({ request }) {
    console.error(`Gave up on ${request.url}`);
  },
});

await crawler.run(['https://example.com/products']);

Swap PlaywrightCrawler for CheerioCrawler and the same handler runs without a browser at roughly 10× the throughput. That swap is the main reason to adopt Crawlee: you can start browser-based and downgrade to HTTP once you learn the site doesn't need JavaScript.

Below ~100 pages, Crawlee is more machinery than the job needs. A for loop with a try/catch and a delay is fine.

Stealth in 2026: what still works

Anti-bot detection and stealth options for Node scraping

The honest state of things: the popular open-source stealth layer is unmaintained, and the alternatives are a moving target.

  • Patchright (1.61.1, updated 2026-07-16) — a drop-in Playwright replacement that patches the automation signals Playwright leaks. Actively maintained, 0 open issues.
  • Camoufox (camoufox-js 0.11.5, updated 2026-07-27) — a custom Firefox build with fingerprint spoofing at the C++ level rather than by patching JS properties.
  • got-scraping (4.2.1) — browser-like TLS and header ordering for HTTP-only scraping, from the Crawlee team.

None of these is a guarantee. Fingerprinting now combines TLS handshake order, HTTP/2 frame settings, canvas and WebGL rendering, and behavioural timing — a patched browser can still fail on the TLS layer before a single line of your JavaScript runs. If your scraper's uptime is a business requirement rather than a hobby, maintaining this layer yourself is a standing cost. See the headless browser guide for what actually leaks and why.

Skipping the library problem entirely

The libraries above solve parsing and automation. They don't solve proxies, browser fleets, or the fact that your stealth patches expire. WebScraping.AI handles the request side so your Node code only deals with data.

Fetch rendered HTML and parse it with the Cheerio you already know:

import * as cheerio from 'cheerio';

const params = new URLSearchParams({
  api_key: process.env.WEBSCRAPING_AI_API_KEY,
  url:     'https://example.com/products',
  js:      'true',
  wait_for: '.product-item',   // wait for the selector, not a fixed timeout
  proxy:   'residential',
  country: 'us',
});

const res  = await fetch(`https://api.webscraping.ai/html?${params}`);
const $    = cheerio.load(await res.text());
const rows = $('.product-item').map((_, el) => $(el).find('.product-name').text().trim()).get();

Or skip selectors altogether and describe the fields you want, which survives the markup changes that break CSS selectors:

const params = new URLSearchParams({
  api_key: process.env.WEBSCRAPING_AI_API_KEY,
  url:     'https://example.com/products/1',
  'fields[name]':  'Product name',
  'fields[price]': 'Numeric price, no currency symbol',
  'fields[stock]': 'In stock, yes or no',
});

const data = await (await fetch(`https://api.webscraping.ai/ai/fields?${params}`)).json();
// { name: "...", price: "24.99", stock: "yes" }

Costs are a published multiplier, not a mystery: 1 credit for a datacenter request without JS, 5 with JS, 10/25 for residential, 50 for stealth, +5 for AI extraction. Failed requests are free, which matters when you're scraping sites that block a fraction of attempts. The free tier is 2,000 credits/month with no credit card; paid starts at $29/mo for 250k credits. Credits do not roll over between months.

There's a JavaScript SDK if you'd rather not build query strings, plus an MCP server and an n8n node for agent and no-code workflows.

Teams typically reach for this on price monitoring, job listing aggregation, and RAG knowledge base pipelines — jobs where a scraper breaking silently is worse than it being slow.

Frequently Asked Questions

What is the best JavaScript library for web scraping?

There isn't one, because the job splits in two. For fetching and parsing static HTML, Cheerio with the built-in fetch is the fastest and lightest choice. For JavaScript-rendered pages, Playwright. For crawling at scale with queues and retries, Crawlee. Start with Cheerio and only escalate when curl shows your data isn't in the HTML source.

Is Cheerio still maintained in 2026?

Yes. Cheerio 1.0.0 finally shipped on 2024-08-09 after years of release candidates, followed by 1.1.0 (2025-06-08) and 1.2.0 (2026-01-23). The release cadence is slow, but the repository sees regular commits and the library is stable and effectively feature-complete.

Should I still use puppeteer-extra-plugin-stealth?

No. Its last npm release was 2023-03-01 and its repository's last commit was 2024-07-18, with 273 issues open. It patches detection signals from 2022 while anti-bot vendors have shipped continuously since. Patchright and Camoufox are the maintained alternatives.

Do I need axios or node-fetch for scraping in Node?

Neither is required. Node 18 and later ship a global fetch() built on undici, which covers most scraping needs with zero dependencies. node-fetch hasn't published a release since 2023-07-25 and exists mainly for legacy code. Axios is still actively maintained and worth adding if you specifically want interceptors, automatic JSON handling, or per-request timeout config.

Is Puppeteer or Playwright faster for web scraping?

For a single page they're comparable — both drive the same browser engines. Playwright pulls ahead on real workloads because browser contexts let you run many isolated sessions in one browser process, where Puppeteer typically needs a full browser per isolated session. The larger factor is that neither is fast compared to plain HTTP: a Cheerio scrape of a static page is roughly an order of magnitude cheaper than either.

It depends on what you scrape, where you are, and what you do with the data — public data, personal data, and copyrighted content are treated very differently, and terms of service, the CFAA, and GDPR can all apply. It's not a question a library choice answers. See is web scraping legal for the detail.


Working through a first scraper rather than choosing between libraries? Start with web scraping with JavaScript for the end-to-end walkthrough, then come back here when you outgrow it. Or get 2,000 free credits and let the request layer be someone else's problem.

Get Started Now

WebScraping.AI provides rotating proxies, Chromium rendering and built-in HTML parser for web scraping
Icon