The essential points from this guide -- each one is explained in detail below.
Cheerio parses static HTML with jQuery-style selectors, making it the fastest choice for a lightweight nodejs website scraper handling simple pages.
Puppeteer controls a real Chromium browser, so it can handle nodejs screen scraping on pages that load content with JavaScript after the initial request.
Axios and node-fetch fetch the raw HTML that Cheerio parses; Puppeteer replaces both when a page needs full JavaScript rendering.
Node.js handles concurrent scraping well with its native async model, while Python offers a wider library ecosystem built specifically for scraping.
Route every request through a proxy agent to avoid IP bans once a node js web scraping project sends more than a few dozen requests.
Cheerio parses HTML on the server using the same selector syntax as jQuery, without loading a browser or running any JavaScript. It is one of the most common tools for scraping with node js on static pages that do not depend on client-side rendering. Install it with npm install cheerio alongside an HTTP client such as node-fetch or Axios to fetch the raw page first. See what web scraping is for a broader definition if this is your first scraping project.
A typical cheerio scraper fetches a URL, loads the response body into Cheerio, and selects elements with CSS-style selectors, the same approach used in cheerio web scraping tutorials across the ecosystem:
import fetch from 'node-fetch';
import * as cheerio from 'cheerio';
const res = await fetch('https://example.com/products');
const html = await res.text();
const $ = cheerio.load(html);
const titles = [];
$('.product-title').each((i, el) => {
titles.push($(el).text().trim());
});
console.log(titles);This pattern covers most web scraping cheerio use cases: product listings, article titles, prices, and links. Cheerio has no rendering engine, so it only sees the HTML returned by the server, not content added later by client-side JavaScript. For sites that build their content with a framework like React or Vue, a cheerio scraper returns an empty or near-empty page. That is the signal to switch to Puppeteer, which is covered next.
Puppeteer controls a real headless Chromium browser from Node.js, so it can scrape pages that load their content after the initial HTML response. This makes it the right tool for nodejs screen scraping on single-page applications, infinite-scroll feeds, and any site that fetches data through its own API calls after the page loads.
Install Puppeteer with npm install puppeteer, then launch a browser, navigate to the target page, and wait for the content to appear before reading it:
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch({ headless: true });
const page = await browser.newPage();
await page.goto('https://example.com/products', { waitUntil: 'networkidle2' });
await page.waitForSelector('.product-title');
const titles = await page.$$eval('.product-title', els =>
els.map(el => el.textContent.trim())
);
console.log(titles);
await browser.close();The waitUntil: 'networkidle2' option pauses navigation until network activity settles, giving JavaScript-rendered content time to load. waitForSelector adds a second check for content that loads after the network goes quiet. A step-by-step walkthrough for one common Puppeteer target is the Google search scraping guide, which covers selectors and pagination in detail. Puppeteer is heavier than Cheerio, since it runs a full browser process, so most node js scrape website projects use Cheerio for static pages and reserve Puppeteer for pages that genuinely need JavaScript execution.
Axios and node-fetch are the two HTTP clients most Node.js scrapers use to fetch page HTML before handing it to Cheerio. Both send a GET request and return the response body as text, but they differ in API shape and built-in features.
Axios includes automatic JSON parsing, request and response interceptors, and built-in timeout handling:
import axios from 'axios';
const response = await axios.get('https://example.com/products', {
timeout: 10000,
headers: { 'User-Agent': 'Mozilla/5.0' },
});
const html = response.data;node-fetch mirrors the browser fetch API and requires an extra step to read the response body as text:
import fetch from 'node-fetch';
const res = await fetch('https://example.com/products', {
headers: { 'User-Agent': 'Mozilla/5.0' },
});
const html = await res.text();Either client works well for node scraping inside a node web scraper. Axios is the more common default in production scrapers because of its interceptor system, which makes it simple to attach a proxy agent and retry logic to every request in one place, rather than repeating configuration on each call. Whichever client is chosen, set a realistic User-Agent header, since many sites block requests from clients using default library user agents. Either pattern is enough to node scrape website targets that return their content directly in the initial HTML response.
Node.js and Python both scrape the web well, but they solve the problem differently: Node.js relies on an async, event-driven model built into the language, while Python relies on a larger set of purpose-built scraping libraries. Neither is universally faster; the right choice depends on the project.
Node.js handles concurrent requests naturally because async/await and Promises are core language features, not an add-on. A scraper fetching thousands of pages can fire many requests in parallel with Promise.all without extra threading libraries. This makes Node.js a strong choice for scraping jobs that are I/O-bound, meaning most of the time is spent waiting on network responses rather than processing data.
Python's advantage is its library ecosystem. Scrapy provides a full scraping framework with built-in crawling, retries, and pipelines. BeautifulSoup offers a Cheerio equivalent parser, and Selenium or Playwright handle JavaScript rendering the way Puppeteer does in Node.js. For data-heavy scraping that feeds directly into pandas or a machine learning pipeline, Python keeps everything in one language.
The web scraping javascript vs python decision often comes down to the rest of the stack. A team already running a Node.js backend gains more from keeping the scraper in Node.js than from introducing a second language just for data collection.
A Node.js scraper needs a proxy to avoid IP bans once it sends more than a handful of requests to the same site. Pass a proxy agent to Axios or node-fetch instead of rewriting each request's connection logic by hand.
Create an HttpsProxyAgent once and attach it to every Axios request:
import axios from 'axios';
import { HttpsProxyAgent } from 'https-proxy-agent';
const agent = new HttpsProxyAgent('http://USER:PASS@gw.knoxproxy.com:7000');
const response = await axios.get('https://example.com/products', {
httpsAgent: agent,
timeout: 10000,
});node-fetch uses the same agent object:
import fetch from 'node-fetch';
import { HttpsProxyAgent } from 'https-proxy-agent';
const agent = new HttpsProxyAgent('http://USER:PASS@gw.knoxproxy.com:7000');
const res = await fetch('https://example.com/products', { agent });
const html = await res.text();Puppeteer needs a different approach since Chrome's --proxy-server flag does not accept credentials directly; see the dedicated Puppeteer proxy guide for the proxy-chain package pattern that handles authentication.
This is the standard javascript web scraping setup for any nodejs scraper running at volume: KnoxProxy's backconnect gateway rotates the exit IP on each request, so a target site sees varied traffic instead of one address hammering it thousands of times. KnoxProxy residential proxies start at $2.10/GB, and rotating proxies handle the IP-switching automatically without any extra code in the scraper. See web scraping proxy plans and current pricing before choosing a package for a production scraper.
Ready to put this into practice? See KnoxProxy proxies for web scraping
KnoxProxy Research Team · Technical Content
Network engineers and proxy infrastructure specialists with 10+ years in anti-bot systems, web scraping, and IP routing.
90.4M+ ethically sourced residential IPs across 195 countries. Instant activation, 14-day money-back guarantee.