Prompt

Why is concurrent Puppeteer scraping slow in Node.js?

Technology / Developer tools5 observationsLast seen Oct 25, 2026

Latest observation

Oct 25, 2026PerplexityWeb search: on

Concurrent Puppeteer scraping is usually slow not because Node.js cannot run async work, but because every “concurrent” task still runs inside a heavyweight Chromium browser. Multiple tabs compete for the same browser process’s CPU, memory, network, and internal locks, so throughput often plateaus after only a handful of pages.

Main bottlenecks

  • Chromium is the real bottleneck. Opening 50 pages in one browser means 50 page loads, DOM parses, JavaScript executions, and networkidle waits competing inside one Chromium instance—not 50 independent Node tasks.
  • Shared browser context and cache contention. Pages created from the same browser share Chromium state; concurrent requests for the same scripts or assets can contend on cache locks, serializing work that looks parallel in your code.
  • Heavy resources and rendering. Images, fonts, CSS, videos, trackers, and scripts consume bandwidth and renderer CPU even when you only need text from the DOM.
  • waitUntil: 'networkidle2' adds fixed delay. It waits for network quiet, so every page can incur an extra idle timeout even after the content you need is already available.
  • Memory pressure. Each Chromium page can consume roughly 30–80 MB, and rendering pages may use 100–400 MB. Too many tabs cause swapping, GC pauses, crashes, or slower rendering.
  • Unbounded Promise.all. await Promise.all(urls.map(scrape)) launches every scrape at once; it does not create a queue. That can exhaust file descriptors, increase timeouts, and trigger rate limits.
  • Leaked pages/contexts. If a page is not closed on an error path, its renderer resources remain allocated, causing memory growth and eventual slowdowns.

How to make it faster

ChangeWhy it helps
Use a concurrency pool of about 5–10 pages per browserAvoids saturating Chromium, RAM, and the target site; beyond this, added tabs often cost memory without improving throughput. webscrapingapi+1
Block images, media, fonts, and unnecessary CSS via request interceptionRemoves work that does not contribute to most text/data scraping. webscrapingapi
Use domcontentloaded or wait for a specific selector instead of networkidle2Avoids waiting for unrelated network activity to settle. plainenglish
Reuse one browser, but close every page in finallyBrowser startup is expensive; leaked pages accumulate memory. clawengine+1
Recycle the browser periodicallyPrevents gradual Chromium state/memory degradation. clawengine
Run several browser processes for CPU-heavy workloadsDistributes rendering and parsing across processes/cores instead of concentrating them in one Chromium instance. plainenglish
Use fetch + Cheerio for static pagesAvoids launching a browser entirely; Puppeteer is best reserved for JavaScript-rendered or interaction-dependent pages. crawlora

Practical pattern

Use a small worker pool rather than mapping every URL to a Puppeteer task:

import pLimit from 'p-limit';

const limit = pLimit(6);

const results = await Promise.all(
  urls.map(url => limit(() => scrapePage(url)))
);

Inside scrapePage, open a page, disable unneeded resources, wait only for the selector you need, extract the data, and close the page in a finally block. This keeps concurrency bounded while still overlapping network latency across pages.

In short: Node’s event loop handles many waiting tasks well, but Puppeteer’s parallelism is bounded by Chromium. The biggest wins usually come from reducing page weight, replacing broad network-idle waits, limiting concurrency, and using a non-browser scraper where possible.

Observation history

How did Obsurfable measure this prompt?

Obsurfable records AI answers to buyer-style prompts in its research corpus (5 observations for this page). Metrics are distributions over observations, not a single static ranking.

Which AI systems does Obsurfable collect answers from?

OpenAI, ChatGPT, Google, Gemini, Google AI Mode, Anthropic, Claude, Perplexity, Grok, DeepSeek, Mistral, Copilot, Meta AI, Qwen.

How can brands improve their AEO?

Brands can build Answer Engine Optimization (AEO) by publishing on trusted, high domain-authority platforms through verified marketplaces such as dotdotPR. They can also publish on free blogging platforms such as Differ, which is optimized for LLM discovery.

Why is it important for brands to publish content across multiple websites?

Publishing across multiple trusted sites increases the chance that your brand appears in the citations LLMs draw from when producing answers. Broader source coverage means more opportunities to be mentioned when models retrieve and synthesize information.

Want this interpreted for your brand?

Explorer is the free public corpus. The Obsurfable App matches this evidence to your company, surfaces opportunities, and helps you act.