Implementing Scraping with Puppeteer/Playwright (Headless Browser)
When trying to scrape an e-commerce site built on Next.js, we hit a familiar wall: a static HTML parser returned an empty page. Content on React, Vue, or Angular loads dynamically—lazy-loading, infinite scroll, and asynchronous API calls. Without a headless browser, you simply can't get the data. We've tested several tools and settled on Playwright combined with proxy rotation.
Our experience across 50+ projects shows that the right tool choice and process optimization reduce scraper development time by 30–50%. For instance, one client automated competitor price monitoring, cutting manual data collection costs by 40%. On a large e-commerce platform with 10,000 products, we blocked images and fonts—memory usage dropped by 60%, and scraping time per page fell from 8 to 1.2 seconds.
Why a Headless Browser Is Essential for Dynamic Sites
Modern frontend frameworks (React, Vue, Angular) render content on the client. An HTTP request to the page returns only the HTML shell. To get the real data, you need to execute JavaScript. A headless browser does this in the background—you obtain the DOM as in a normal browser, but without a GUI.
Which Sites Require a Headless Browser?
- SPAs (React, Vue, Angular) — content is built on the client.
- Infinite scroll and lazy-loading — data loads on scroll.
- CAPTCHAs and authentication — require JS execution.
- Bot-protected sites (Cloudflare, DataDome) — headless is unavoidable.
Which Headless Browser Is Best for Scraping?
| Parameter | Puppeteer | Playwright |
|---|---|---|
| Browsers | Chrome/Chromium | Chrome, Firefox, Safari |
| Language | Node.js | Node.js, Python, Java, C# |
| Auto-wait | No (explicit waits) | Yes (auto-wait for elements) |
| Development speed | Medium | 30-40% faster |
Playwright is preferable for new projects: its auto-wait significantly reduces errors—you don't need to manually wait for each element. According to official documentation, this speeds up script development by 30-40%. In Puppeteer, every waitForSelector must be configured individually, slowing down work.
How to Avoid Headless Browser Blocking?
Protection systems (DataDome, PerimeterX, Cloudflare Bot Management) analyze dozens of automation signals. Key evasion methods:
-
playwright-stealth— patchesnavigator.webdriverand other fields. - Realistic mouse movements using
playwright-mouse-helper. - Unique fingerprints — different viewport, timezone, locale per session.
- Proxy and user-agent rotation.
Without these measures, up to 80% of requests are blocked. In our projects, the combination yields a pass-through rate of 95%+.
Details on Stealth Library Setup
- Install
playwright-stealthand apply patches before launching the browser. - Configure realistic mouse movements with
playwright-mouse-helper. - Generate a new fingerprint for each session: viewport, timezone, locale, user-agent.
- Use a proxy pool with rotation every N requests.
Typical Scraping Scenario
// Playwright: catalog scraping with infinite scroll const browser = await chromium.launch({ headless: true }); const context = await browser.newContext({ userAgent: 'Mozilla/5.0 ...', viewport: { width: 1280, height: 900 } }); const page = await context.newPage(); await page.goto('https://example.com/catalog'); // Scroll to the bottom let prevHeight = 0; while (true) { const height = await page.evaluate(() => document.body.scrollHeight); if (height === prevHeight) break; await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight)); await page.waitForTimeout(1500 + Math.random() * 1000); prevHeight = height; } // Extract data const items = await page.$$eval('.product-card', cards => cards.map(card => ({ title: card.querySelector('.title')?.textContent?.trim(), price: card.querySelector('.price')?.textContent?.trim(), url: card.querySelector('a')?.href })) ); Performance Optimization: How to Reduce Load?
Launching a browser is expensive. For production scraping:
- Browser context pool — one Chrome process with multiple isolated contexts.
-
Resource blocking — block font, image, and analytics loading via
page.route(). -
Clustering —
playwright-clusteror custom pool withworker_threads.
Blocking unnecessary traffic reduces page load time by 40–70% and memory usage. For example, our scraper for a 10,000-product store required 60% less memory after disabling images.
Scraper Development Process
| Stage | Duration | Outcome |
|---|---|---|
| Target site analysis | 0.5–1 day | Data structure schema, endpoint list |
| Scraping logic development | 1–3 days | Working script with pagination/scroll handling |
| Anti-blocking integration | 1–2 days | Stealth libraries, proxies, rotation |
| Testing and debugging | 0.5–1 day | Verification on 100+ requests, bug fixes |
| Deployment and monitoring | 0.5 day | Server launch, failure alerts |
What's Included
- Complete deployment documentation.
- Code with comments and launch instructions.
- Proxy rotation and user-agent configuration.
- Test run on the target site (up to 1000 pages).
- 30-day performance guarantee after delivery.
Timeline and Pricing
A basic scraper for one site: 2–4 days. A scraper with anti-bot measures, proxy rotation, and monitoring: 7–10 days. Pricing is determined individually after analyzing your project. Contact us for a free consultation—we'll help you choose the optimal solution and estimate the savings for your business.







