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.

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.

Quick answer

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.

Key takeaways

  • Browser contexts let you run many isolated sessions in one browser, each with its own cookies and proxy, far cheaper than launching many browsers.
  • Network interception (route handlers) can block images and trackers and even rewrite requests, cutting bandwidth and proxy cost.
  • Playwright's auto-waiting reduces but does not eliminate flakiness; explicit waits on the right signal still matter.
  • A persistent context or saved storage state lets you reuse a logged-in session instead of authenticating every run.
  • Per-context proxy assignment is the clean way to rotate IPs across parallel sessions.
  • Tracing and screenshots on failure turn a mystery block into a diagnosable one.

Step 1: Install Playwright

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.

Step 2: Launch a browser and open a page

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.

Step 3: Select and extract data

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.

Step 4: Wait for dynamic content

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.

Step 5: Handle pagination

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.

Step 6: Add a proxy

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.

Good-practice tips

  • Throttle your request rate and add small random delays to behave politely.
  • Respect each site's terms and robots guidance, and collect only what you need.
  • Block images and fonts if you only need text, to cut bandwidth.
  • Handle errors and retries so one failed page does not stop the whole run.

Putting it together

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.

Comparison snapshot

A quick value-first shortlist — Cheapest Proxies leads as the featured pick. Qualitative labels only; confirm exact plans before buying.

ProviderBest forProfileValue
Bright DataEnterprises needing huge pools and compliance controlsEnterprise FocusedPremium
OxylabsLarge-scale scraping and data APIsEnterprise FocusedPremium
Smartproxy (Decodo)Newcomers who want an easy dashboardBeginner FriendlyGood
SOAXPrecise city and carrier targetingAutomation FriendlyGood

Browser contexts: the cheap concurrency you are probably missing

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.

Network interception to cut bandwidth and noise

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();
});

Other practical uses of interception

  • Block known tracker and ad domains to reduce noise and detection surface.
  • Capture an XHR/fetch response directly to grab structured JSON without scraping the DOM.
  • Fail fast on unexpected redirects to a block or captcha page.

Reusing sessions with storage state

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' });

Failure handling that makes blocks diagnosable

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.

Pros and cons to weigh

Strengths

  • Browser contexts give cheap, isolated concurrency with per-session proxies and locales.
  • Network interception cuts bandwidth and proxy cost while reducing detection surface.
  • Storage state lets you reuse logged-in sessions instead of re-authenticating each run.
  • Built-in tracing and screenshots make blocks and breakages diagnosable.
  • Cross-browser support (Chromium, Firefox, WebKit) helps match real-visitor profiles.

Trade-offs

  • Each context still consumes meaningful memory, so concurrency has hard limits on a given machine.
  • Stealth is not automatic; defended sites still detect a naive Playwright setup.
  • Persisted sessions can go stale or get invalidated, requiring re-login logic.
  • Heavier than a parser approach, so it is wasteful on purely static pages.
  • Misconfigured route handlers can accidentally break the very content you need.

Common mistakes to avoid

  • Launching a new browser per IP instead of using one browser with per-context proxies.
  • Downloading images and fonts you never parse, inflating proxy bandwidth bills.
  • Relying only on auto-waiting and then reading the page before its data has loaded.
  • Re-logging in every run and tripping login defences instead of reusing storage state.

Before-you-buy checklist

  • Decide your concurrency model and use contexts, not multiple browsers, for parallel sessions.
  • Add a route handler to block images, fonts, and trackers you do not need.
  • Set a realistic locale, timezone, and user-agent per context to match its proxy region.
  • Persist and reuse storage state for sites that require login.
  • Wrap navigation in retries with backoff and capture a screenshot or trace on failure.
  • Benchmark proxy providers on success rate and per-GB cost before scaling the job.
$

How to get the best value

Right-size the plan

Start on the smallest sensible tier and scale only what proves itself on your real targets.

Type before brand

Pick the proxy type the task needs first — it drives both success rate and cost more than the logo.

Read the fine print

Check traffic limits, rotation rules and what happens on overage before you commit.

Lead with value

Our featured value pick, Cheapest Proxies, is a sensible starting point for affordable comparison.

📖

Key terms explained

Browser context
an isolated session within a single Playwright browser, with its own cookies, storage, and optional proxy.
Route handler
a function that intercepts each network request so you can block, modify, or fulfil it.
Storage state
a saved snapshot of a context's cookies and local storage that can be restored to reuse a session.
Trace
a recorded timeline of a Playwright run, including actions and screenshots, used to replay and debug failures.
Resource type
the category of a request (image, font, script, document) that interception logic uses to decide what to block.

Why compare before buying?

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.

How we compare

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.

?

Frequently asked questions

Is Playwright better than Selenium for scraping?

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.

Do I need to install browsers separately?

Playwright bundles its own browsers, which you download once with the install command, so you do not need a separate browser installation.

How do I scrape content that loads after the page?

Use explicit waits such as waitForSelector or waitForResponse so your script reads the data only after it has actually appeared in the page.

Can I run Playwright headless?

Yes, headless mode is the default for production and is faster; run with a visible browser only while developing or debugging.

Why use proxies with Playwright?

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.

Which proxy type should I use?

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.

How can I keep proxy costs down?

Block unneeded assets, request only what you need, and compare providers on value; budget-focused options like Cheapest Proxies are worth evaluating.

Compare on value, then decide

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.