NEWScrapingAnt MCP for Claude Code, Cursor & Windsurf — try it free →
Skip to main content

Web Scraping with JavaScript and Node.js: A Tested Guide

· 7 min read
Oleg Kulyk
Co-Founder @ ScrapingAnt

Web Scraping with Javascript

Updated 2026-10-26

Rewritten for Node.js 22: built-in fetch and ES modules, current Cheerio, jsdom, Axios, Playwright and Puppeteer, and a web scraping API example. The request and SuperAgent sections and an untested Selenium Google search example were removed. Every snippet was run on 2026-10-26 and its output is shown; the code is in the evidence packet.

Node.js is a good fit for web scraping: fetch is built in, the parsers are fast, and the two main headless browser libraries, Playwright and Puppeteer, are JavaScript-first. This guide goes from a one-line request to a page that only renders in a browser, then shows the same job through a web scraping API.

Setup​

Use Node.js 22.13 or newer; the examples ran on 22.20. Node 18 added the built-in fetch, but the current Puppeteer and jsdom releases need Node 22.12 and 22.13 respectively. Create a project, switch it to ES modules so you can use import and top-level await, and install what you need:

npm init -y
npm pkg set type=module
npm install cheerio jsdom axios playwright puppeteer
npx playwright install chromium

Save each example as a .js file and run it with node file.js. The targets are public practice sites built for scraping: books.toscrape.com and quotes.toscrape.com.

The smallest scraper: fetch​

fetch downloads the page; a regular expression is enough to pull one value out of it:

const response = await fetch('https://example.com/');
const html = await response.text();
const title = html.match(/<title>(.*?)<\/title>/)[1];
console.log(response.status, title);
200 Example Domain

Regular expressions break as soon as the markup changes, so for anything beyond a single value, use a parser.

Parse HTML with Cheerio​

Cheerio parses HTML and gives you jQuery-style selectors. This collects every book on the first page with its price:

import * as cheerio from 'cheerio';

const response = await fetch('https://books.toscrape.com/');
const $ = cheerio.load(await response.text());

const books = $('article.product_pod').map((_, el) => ({
title: $(el).find('h3 a').attr('title'),
price: $(el).find('p.price_color').text(),
})).get();

console.log(books.length, 'books');
console.log(books.slice(0, 3));
20 books
[
{ title: 'A Light in the Attic', price: '£51.77' },
{ title: 'Tipping the Velvet', price: '£53.74' },
{ title: 'Soumission', price: '£50.10' }
]

Cheerio does not run JavaScript or build a browser DOM, which is why it is fast. For more parsers, see HTML parsing libraries for JavaScript.

jsdom: the browser DOM API​

jsdom implements the DOM in Node.js, so you can use querySelectorAll and other browser APIs you already know:

import { JSDOM } from 'jsdom';

const response = await fetch('https://books.toscrape.com/');
const { document } = new JSDOM(await response.text()).window;

const prices = [...document.querySelectorAll('p.price_color')].map((p) => p.textContent);
console.log(prices.length, 'prices, first three:', prices.slice(0, 3).join(' '));
20 prices, first three: £51.77 £53.74 £50.10

jsdom is slower and heavier than Cheerio; pick it when you need the DOM API itself.

Axios instead of fetch​

Axios is still the most common HTTP client in older codebases. It parses JSON for you, supports timeouts and interceptors, and works the same way with a parser:

import axios from 'axios';
import * as cheerio from 'cheerio';

const { status, data } = await axios.get('https://books.toscrape.com/catalogue/page-2.html', { timeout: 10000 });
const $ = cheerio.load(data);
console.log(status, $('article.product_pod').length, 'books on page 2,', $('li.current').text().trim());
200 20 books on page 2, Page 2 of 50

To send Axios through a proxy, see how to use proxies with Axios. For following "next" links across pages, see pagination techniques.

Pages that need JavaScript​

Many sites send an almost empty HTML page and build the content in the browser. quotes.toscrape.com/js/ is one of them, and a plain request finds nothing to parse:

import * as cheerio from 'cheerio';

// quotes.toscrape.com/js/ builds its quotes with JavaScript after the page loads
const response = await fetch('https://quotes.toscrape.com/js/');
const $ = cheerio.load(await response.text());
console.log(response.status, $('div.quote').length, 'quotes in the raw HTML');
200 0 quotes in the raw HTML

For these pages you need a browser that runs the page's JavaScript: Playwright or Puppeteer, or an API that runs the browser for you.

Playwright​

Playwright drives Chromium, Firefox and WebKit. Actions such as click() wait for their element automatically; reading a list of elements does not, so wait for the first one before you read them:

import { chromium } from 'playwright';

const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('https://quotes.toscrape.com/js/');
await page.locator('div.quote').first().waitFor(); // reading many elements does not wait, so wait for the first one

const quotes = await page.locator('div.quote').evaluateAll((nodes) =>
nodes.map((q) => ({ text: q.querySelector('.text').textContent, author: q.querySelector('.author').textContent })),
);
console.log(quotes.length, 'quotes');
console.log(quotes[0]);
await browser.close();
10 quotes
{
text: '“The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.”',
author: 'Albert Einstein'
}

Puppeteer​

Puppeteer is the Chrome team's library. It downloads its own Chrome on install, and you wait for elements explicitly:

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://quotes.toscrape.com/js/');
await page.waitForSelector('div.quote');

const authors = await page.$$eval('div.quote .author', (nodes) => nodes.map((n) => n.textContent));
console.log(authors.length, 'quotes, first authors:', authors.slice(0, 3).join(', '));
await browser.close();
10 quotes, first authors: Albert Einstein, J.K. Rowling, Albert Einstein

Both work. Playwright has the broader browser support and auto-waits on actions; Puppeteer is a little lighter if you only need Chrome. Our Playwright vs Puppeteer comparison goes into detail. Running many browsers at once is where the cost shows up, as scraping with Puppeteer Cluster describes.

ScrapingAnt API

Web scraping API from JavaScript​

Skip the browser install: one fetch to the ScrapingAnt API returns the rendered page, loaded in headless Chrome behind rotating proxies, and you parse it with Cheerio exactly as above. Pick any proxy_country from the documented list at no extra charge; requests the API answers with a non-200 status are not charged, and there is a free plan to start.

import * as cheerio from 'cheerio';

const params = new URLSearchParams({
url: 'https://quotes.toscrape.com/js/',
'x-api-key': process.env.SCRAPINGANT_API_KEY,
}); // browser=true is the default: the page renders in headless Chrome
const response = await fetch(`https://api.scrapingant.com/v2/general?${params}`);
const $ = cheerio.load(await response.text());

console.log(response.status, response.headers.get('ant-page-status-code'), $('div.quote').length, 'quotes');
console.log($('div.quote .author').first().text(), '|', $('div.quote .text').first().text().slice(0, 50));
200 200 10 quotes
Albert Einstein | “The world as we have created it is a process of o

The second number is Ant-Page-Status-Code, the target's own status. Run on 2026-10-26 with Node.js 22.20 and cheerio 1.2.0. Set SCRAPINGANT_API_KEY to the key from your dashboard before running it. There is also an npm client if you prefer one.

Which tool to use​

The pageUse
Returns the data in its HTMLfetch (or Axios) and Cheerio
Needs browser DOM APIs in Nodejsdom
Builds content with JavaScript, a few pagesPlaywright or Puppeteer
Builds content with JavaScript at volume, or blocks your IPA web scraping API, or a browser plus rotating proxies

When a site starts refusing requests, check how to scrape without getting blocked and, for headless browsers, detection avoidance libraries for JavaScript. If you are still choosing a language, see JavaScript vs Python for web scraping.

Examples tested on 2026-10-26 with Node.js 22.20.0, cheerio 1.2.0, jsdom 29.1.1, axios 1.20.0, playwright 1.64.1 and puppeteer 25.16.0. Code: scrapingant-examples/examples/web-scraping-javascript.

This article was drafted with AI assistance from a tested evidence packet and reviewed by the named author, who is responsible for the code, measurements and corrections.

Skip the proxy and browser setup

Pass the URL to one ScrapingAnt endpoint and the request goes out through rotating proxies and a headless browser.