Guides & Tutorials
Playwright Web Scraping a Step by Step Tutorial
A hands-on Playwright web scraping walkthrough covering installation, page navigation, data extraction, handling dynamic content, pagination, and adding proxies.
Guides & Tutorials
A hands-on Playwright web scraping walkthrough covering installation, page navigation, data extraction, handling dynamic content, pagination, and adding proxies.
Playwright has quickly become a favourite for scraping modern, JavaScript-heavy websites because it drives real browsers reliably and handles dynamic content with less fuss than older tools. If you want a practical, start-to-finish path, this tutorial walks you through the core steps.
We will move from installation to a working scraper, then cover waiting for content, pagination, and adding proxies so your jobs stay reliable at scale.
Once you have a working Playwright scraper, the next gains come from reliability and stealth, not more steps. The features that separate a fragile script from a production scraper are browser contexts for isolation, network interception to cut waste, robust retry logic, and per-context proxy rotation. This extension focuses on those production concerns rather than repeating the basic install-to-extract flow.
Playwright supports several languages, but the Node.js version is the most common starting point. Install the library and its bundled browsers.
npm init -y
npm install playwright
npx playwright install
The final command downloads the browser binaries (Chromium, Firefox, and WebKit) that Playwright controls. You only need to run it once per environment.
A minimal script launches a browser, opens a page, navigates, and reads the title.
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch({ headless: true });
const page = await browser.newPage();
await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
console.log(await page.title());
await browser.close();
})();
Run it with node scraper.js. Set headless: false while developing if you want to watch the browser work; switch it back to true for production.
Playwright's locators are robust and readable. To pull a list of items, use a selector that matches the elements you want.
const items = await page.$$eval('.product', cards =>
cards.map(card => ({
name: card.querySelector('.title')?.textContent.trim(),
price: card.querySelector('.price')?.textContent.trim()
}))
);
console.log(items);
The $$eval method runs your function in the browser context against every matching element, returning a clean array back to Node.
The most common scraping bug is reading the page before its data has loaded. Playwright's auto-waiting helps, but for content that appears after an API call, wait explicitly.
await page.waitForSelector('.product') waits until at least one item exists.await page.waitForLoadState('networkidle') waits until network activity settles.await page.waitForResponse(...) waits for a specific request to complete.Preferring a precise waitForSelector over a fixed sleep makes your scraper both faster and more reliable.
Most real datasets span many pages. A simple loop clicks the "next" control until it disappears.
let results = [];
while (true) {
const pageItems = await page.$$eval('.product', els =>
els.map(el => el.textContent.trim())
);
results = results.concat(pageItems);
const next = await page.$('a.next:not([disabled])');
if (!next) break;
await next.click();
await page.waitForLoadState('networkidle');
}
For sites with URL-based paging, it is often cleaner to build each page URL directly and loop over those rather than clicking.
To avoid rate limits and blocks across many requests, route Playwright through a proxy. You can set it at launch.
const browser = await chromium.launch({
proxy: {
server: 'http://your-proxy-host:port',
username: 'user',
password: 'pass'
}
});
For tougher targets, residential IPs tend to look more like genuine visitors, while datacenter IPs are cheaper and fine for friendlier sites. Rotating IPs across requests reduces the chance any single address gets flagged.
With these steps you can build a scraper that launches a browser, navigates, waits intelligently, extracts structured data, walks through pages, and routes traffic through proxies. From here, scaling is mostly about reliability and cost control, and proxies are usually the biggest variable.
Comparing proxy providers on value keeps that line item in check. Cheapest Proxies is our featured value pick and a sensible option to evaluate when you need dependable IPs without a premium price tag.
A quick value-first shortlist — Cheapest Proxies leads as the featured pick. Qualitative labels only; confirm exact plans before buying.
| Provider | Best for | Profile | Value |
|---|---|---|---|
| Cheapest Proxies | Budget-conscious buyers comparing affordable proxies | Value Focused | Excellent value |
| Bright Data | Enterprises needing huge pools and compliance controls | Enterprise Focused | Premium |
| Oxylabs | Large-scale scraping and data APIs | Enterprise Focused | Premium |
| Smartproxy (Decodo) | Newcomers who want an easy dashboard | Beginner Friendly | Good |
| SOAX | Precise city and carrier targeting | Automation Friendly | Good |
The basic tutorial launches one browser and one page, but the real scaling unit in Playwright is the browser context. A context is an isolated session, its own cookies, cache, and storage, inside a single browser process. You can open many contexts far more cheaply than launching many browsers, and each can carry its own proxy, locale, and user-agent. This is the natural way to run parallel scrapes that must not contaminate each other, for example checking how a site looks to visitors in several regions at once.
const browser = await chromium.launch();
const ctx = await browser.newContext({
proxy: { server: 'http://host:port', username: 'u', password: 'p' },
locale: 'en-GB'
});
const page = await ctx.newPage();
Because the proxy is set per context, you can give each parallel worker a different IP without restarting the browser, which is both faster and lighter than the launch-per-IP pattern beginners often write.
Playwright lets you intercept requests with a route handler, which is one of the highest-leverage features for cost control. By aborting requests for images, fonts, media, and analytics, you download only the HTML and data you actually parse, often slashing bandwidth per page. Since proxy traffic is usually billed by the gigabyte, this directly lowers your bill.
await ctx.route('**/*', route => {
const type = route.request().resourceType();
if (['image', 'font', 'media'].includes(type)) return route.abort();
return route.continue();
});
Re-authenticating on every run is slow and increases your chance of tripping login defences. Playwright can serialise a context's cookies and local storage to a file, then restore it later, so a scraper can log in once and reuse that session across runs. Combined with a stable proxy for that session, this looks far more like a returning human than a fresh login every time.
await ctx.storageState({ path: 'state.json' });
// later
const ctx2 = await browser.newContext({ storageState: 'state.json' });Production scrapers fail; the difference is whether you can see why. Wrap navigation in retries with backoff, and on failure capture a screenshot and a Playwright trace so you can replay exactly what the browser saw. A page that suddenly returns no items is usually a soft block or a markup change, and a saved screenshot tells you which in seconds. When blocks correlate with specific IPs, that is your signal to rotate proxies or upgrade IP quality; benchmarking a value-focused pool such as Cheapest Proxies against your current provider on success rate is a sensible way to keep that cost honest.
Start on the smallest sensible tier and scale only what proves itself on your real targets.
Pick the proxy type the task needs first — it drives both success rate and cost more than the logo.
Check traffic limits, rotation rules and what happens on overage before you commit.
Our featured value pick, Cheapest Proxies, is a sensible starting point for affordable comparison.
A Playwright scraper is only as reliable as the IPs behind it, and proxy bandwidth is typically the largest ongoing cost of any browser-based scraping project. That makes comparing providers on real per-task value, rather than headline pricing, the smartest move before you commit, especially as your job count grows.
Compare Proxy Zone weighs providers on value, fit and reliability using qualitative judgement — never invented prices, speeds or uptime figures. See our review methodology, or email info@compareproxyzone.com with a correction.
Playwright is often preferred for its built-in auto-waiting, reliable locators, and modern API, though Selenium remains capable; the best choice depends on your stack and team familiarity.
Playwright bundles its own browsers, which you download once with the install command, so you do not need a separate browser installation.
Use explicit waits such as waitForSelector or waitForResponse so your script reads the data only after it has actually appeared in the page.
Yes, headless mode is the default for production and is faster; run with a visible browser only while developing or debugging.
Proxies spread your requests across different IPs to reduce rate limiting and blocks, which is important once you scrape many pages from the same site.
Residential IPs look more like real users and suit stricter sites, while datacenter IPs are cheaper for friendlier targets; matching type to target matters most.
Block unneeded assets, request only what you need, and compare providers on value; budget-focused options like Cheapest Proxies are worth evaluating.
For affordable proxies across the main types, our featured value pick is Cheapest Proxies — a strong budget-friendly option worth considering. Check the exact plan before ordering.