Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Scrapeless + Playwright

Drive real Chromium in the cloud from Playwright over CDP — clean fingerprints, residential egress, no local browser download and no proxy plumbing. Powered by the Scrapeless Scraping Browser.

The only change to normal Playwright code is the connection line:

# instead of pw.chromium.launch()
browser = await pw.chromium.connect_over_cdp(
    "wss://browser.scrapeless.com/browser?token=YOUR_KEY&session_ttl=300&proxy_country=US"
)

Everything after that — new_page, goto, locators, $$eval, screenshots — is unchanged.

Use case

Reach for this when a local headless browser stops being enough:

  • Protected targets — pages behind bot management that return an interstitial to plain Playwright.
  • Geo-specific rendering — prices, catalogs, and search results that differ by country, via proxy_country.
  • Scale without a proxy fleet — no IP pool to rent, rotate, or monitor.
  • CI and containers — nothing to install; no Chromium in your image, no --no-sandbox juggling.

Requirements

  • A Scrapeless API key — create a free account
  • Python 3.9+ with playwright, or Node.js 18.3+ with playwright-core

Setup

git clone https://github.com/<owner>/scrapeless-playwright.git
cd scrapeless-playwright
cp .env.example .env          # then put your key in .env
export SCRAPELESS_API_KEY=your_key_here

Python:

pip install -r python/requirements.txt

Node.js — playwright-core only, because the browser is remote. You never download Chromium:

cd nodejs && pnpm install

Examples

Each example runs standalone and writes its output to results/.

# Python Node.js What it shows
1 python3 python/01_hello_cdp.py node nodejs/01-hello-cdp.mjs Connect and check what a bot-detection page reports
2 python3 python/02_wait_for_challenge.py node nodejs/02-wait-for-challenge.mjs The wait mistake — why a working session looks blocked
3 python3 python/03_extract_listings.py --pages 3 node nodejs/03-extract-listings.mjs --pages 3 Paginate a listing page into structured records

Run examples 1 and 3 from the repo root or their own directory — paths are resolved relative to the script.

1. Fingerprint baseline

Verified output (results/fingerprint.json):

user agent : Mozilla/5.0 (Windows NT 10.0; Win64; x64) ... Chrome/140.0.0.0 Safari/537.36
webdriver  : False
WebDriver (New)       : missing (passed)
WebDriver Advanced    : passed
Chrome (New)          : present (passed)
Plugins Length (Old)  : 5
Languages (Old)       : en,en-US
WebGL Vendor          : Google Inc. (NVIDIA)

navigator.webdriver is absent, the plugin array is populated, and the WebGL vendor reports real hardware. That vendor rotates per session — across three runs it reported AMD, Intel, and NVIDIA, so do not pin assertions to a specific GPU string.

2. The wait mistake (read this one)

domcontentloaded fires on the challenge page, not the real one. Read page.content() there and you get a few KB of interstitial, conclude you were blocked, and discard a session that was about to succeed.

Real timeline from results/challenge-timing.json:

  t=  0.3s      2,551 bytes             interstitial 'g2.com'
  t=  2.5s      2,551 bytes         +0  interstitial 'g2.com'
  t=  5.1s          —       redirect in flight (content unreadable)
  t=  7.4s     85,901 bytes    +83,350  CONTENT      'Best CRM Software: User Reviews from Aug'

Three lessons, each observed rather than assumed:

  1. The interstitial is small and looks like a block. 2,551 bytes with the title g2.com, versus 85,901 bytes and a real title once it settles.
  2. The duration is variable. Settled at 7.4s (Python) and 8.5s (Node) on this URL, but one run was still pending at 10.6s. Any hard-coded sleep is a coin flip — poll instead, with a ceiling.
  3. Reading mid-redirect throws. Playwright raises Unable to retrieve content because the page is navigating and changing the content. Catch it and keep waiting; it is a state, not a failure. Example 2 has the handler.

There is also an SDK-level solver. If you already depend on @scrapeless-ai/sdk, you can ask the session to clear a detected challenge explicitly instead of polling for it:

import { createPuppeteerCDPSession } from '@scrapeless-ai/sdk';
const cdp = await createPuppeteerCDPSession(page);
await cdp.solveCaptcha({ timeout: 60000 });
await page.waitForSelector('<a selector only the real content has>');

That is the better path when you know a content selector to wait on. This repo polls on page size instead, because it keeps the examples dependency-free (playwright-core only, no SDK) and works on targets where you do not yet know a stable selector. See the official example in scraperapigather/scrapeless-scraping-browser.

3. Structured extraction

60 records across 3 pages, all fields parsed (results/books.json):

{ "title": "A Light in the Attic", "price": "£51.77", "availability": "In stock",
  "url": "https://books.toscrape.com/catalogue/a-light-in-the-attic_1000/index.html",
  "rating": 3, "price_value": 51.77 }

Session options

Query parameters on the CDP URL:

Parameter Meaning
token Your API key. Treat the whole URL as a secret — never log it unredacted.
session_ttl Seconds the session lives before teardown. Size it to your job; 300 suits a few pages.
proxy_country ISO country code for the egress IP (US, DE, BR, …).

Both helpers (python/scrapeless_session.py, nodejs/session.mjs) build this URL and expose a redacted() printer so the key never lands in logs.

Two endpoint forms both work. Verified side by side — each connected and rendered the same page:

Endpoint Parameter style
wss://browser.scrapeless.com/browser snake_case: session_ttl, proxy_country
wss://browser.scrapeless.com/api/v2/browser camelCase: sessionTTL, proxyCountry

This repo uses the first because it is what the official Python SDK generates. The official JS examples use the second. Keep the parameter style matched to the endpoint form you pick — mixing them was not tested here, so treat it as unsupported.

Known limitations

  • set_viewport_size is a no-op over CDP. Verified: window reported 2560×1408, a request for 1280×800 was accepted without error, and the window stayed 2560×1408. The remote window size is not yours to set, so never rely on viewport-relative coordinates. Use selectors and locator.click() instead of coordinate clicking, and pass clip to screenshot() when you need a fixed region.
  • Connect timeout. Playwright's 30s default is tight for a cold session — one run failed with TimeoutError after the WebSocket connected. Every example passes timeout: 90_000.
  • Not every target clears. The cloud browser is not a universal bypass. In testing, yellowpages.com returned Cloudflare's "Sorry, you have been blocked" page through the same session that rendered G2 fully. Test your specific target before building on it.

Troubleshooting

Symptom Cause and fix
SCRAPELESS_API_KEY is not set Export the key or fill in .env.
TimeoutError on connect Cold session. Pass timeout: 90_000 to connect_over_cdp (examples already do).
A few KB of HTML and a bare-domain title You are holding the interstitial. See example 2 — poll until the size jumps.
Unable to retrieve content because the page is navigating Redirect in flight. Catch it and re-read after a short wait.
page.set_viewport_size appears ignored It is. See Known limitations.
Empty inner_text but large HTML Content is present but not laid out yet. Extract from HTML or wait for a content selector.
Session closes mid-run session_ttl elapsed. Raise it to cover the whole job.

Project structure

scrapeless-playwright/
├── python/
│   ├── scrapeless_session.py     # builds the CDP URL, redacts the key
│   ├── 01_hello_cdp.py
│   ├── 02_wait_for_challenge.py
│   ├── 03_extract_listings.py
│   └── requirements.txt
├── nodejs/
│   ├── session.mjs
│   ├── 01-hello-cdp.mjs
│   ├── 02-wait-for-challenge.mjs
│   ├── 03-extract-listings.mjs
│   └── package.json
└── results/                      # committed output from real runs

Related

License

MIT — see LICENSE.

About

Run Playwright against the Scrapeless anti-detection cloud browser over CDP — real Chromium, clean fingerprints, residential egress, no local browser download.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages