Recensione di Playwright: rendering JavaScript su Chromium, lasciando a te la parte di crawling

Ultimo aggiornamento il August 18, 2026
Recensione di Playwright: rendering JavaScript su Chromium, lasciando a te la parte di crawling
Riepilogo AI
Playwright è il framework di Microsoft per l’automazione del browser: una libreria Apache-2.0, pensata prima di tutto per TypeScript, che avvia un browser reale, lo controlla tramite una sola API e restituisce la pagina dopo l’esecuzione del JavaScript. Viene presentato come framework end-to-end per i test, ma il motore sottostante è quello a cui molti ricorrono in silenzio quando una richiesta HTTP restituisce solo una shell vuota al posto dei dati. Per struttura compete con Puppeteer e Selenium: browser reali da pilotare con script, non client HTTP da parsare. Ho eseguito microsoft/playwright 1.56.0 su una serie fissa di test di scraping — un catalogo statico con paginazione, un articolo, un catalogo renderizzato in JavaScript, una JSON API, un errore 500, un piccolo grafo di crawl e due siti pubblici di pratica — su Node v22.22.3, macOS arm64, solo Chromium.

Playwright è il framework di Microsoft per l’automazione del browser: una libreria Apache-2.0, pensata prima di tutto per TypeScript, che avvia un browser reale, lo controlla tramite una sola API e restituisce la pagina dopo l’esecuzione del JavaScript. Viene presentato come framework end-to-end per i test, ma il motore sottostante è quello a cui molti ricorrono in silenzio quando una richiesta HTTP restituisce solo una shell vuota al posto dei dati. Per struttura compete con Puppeteer e Selenium: browser reali da pilotare con script, non client HTTP da parsare.

Ho eseguito microsoft/playwright 1.56.0 su una serie fissa di test di scraping — un catalogo statico con paginazione, un articolo, un catalogo renderizzato in JavaScript, una JSON API, un errore 500, un piccolo grafo di crawl e due siti pubblici di pratica — su Node v22.22.3, macOS arm64, solo Chromium. La parte di rendering ha funzionato alla perfezione. La parte di crawling, invece, non esiste: ed è proprio questa la cosa più utile da capire prima di adottarlo.

Cosa è emerso

Due risultati spiccano su tutti, ma vanno in direzioni leggermente diverse.

Il primo è il risultato browser atteso, vincolato alle attese che avevo impostato. Dopo page.goto(..., { waitUntil: 'domcontentloaded' }), il test sul fixture dinamico ha atteso #dynamic-products article.product-card, mentre il test pubblico Quotes to Scrape ha atteso .quote. Quando quei selettori specifici dell’app sono comparsi, le esecuzioni hanno restituito 8/8 elementi del fixture e 10 citazioni del sito pubblico; il risultato del fixture ha ottenuto recall 1.0 rispetto alla ground truth di otto elementi. Non è servito alcun loop di polling personalizzato, ma Playwright non ha eliminato il problema della readiness: è stato il test a definire la condizione di completamento. Anche uno screenshot dell’intera pagina è stato salvato al primo tentativo.

Il secondo risultato ha usato browserContext.request: in particolare, ctx.request.get(...) dopo che Chromium era già stato avviato ed esisteva un browser context. Ha interrogato direttamente l’endpoint JSON del fixture e ha restituito 8/8 prodotti senza creare né renderizzare una pagina. Questo salta il lavoro sul DOM, ma non il costo del processo browser in questo harness. Il client request associato al context può condividere lo stato dei cookie con le pagine del browser; un playwright.request.newContext() standalone evita di richiedere un browser context, ma non condivide automaticamente quella sessione. Il test ha coperto solo il primo percorso.

Playwright, inoltre, non fornisce una coda di crawl, un writer per il dataset o un throttling automatico. Il mio test sul grafo di crawl — seguire i link interni, tracciare la profondità ed evitare i duplicati — ha raggiunto 12 pagine con profondità {0:1, 1:4, 2:7}, ma l’attraversamento breadth-first era codice scritto da me. Playwright apre e ispeziona le pagine; persistenza del fronte, policy sugli URL, retry e pianificazione appartengono a un altro livello.

Cos’è davvero Playwright

Lo strumento — microsoft/playwright su GitHub — è scritto in TypeScript, ha licenza Apache-2.0 ed è mantenuto da Microsoft. La build testata qui era 1.56.0 del 9 luglio 2026. I risultati vanno letti come riferiti a quella build, non come dichiarazione di compatibilità con versioni successive.

Il posizionamento ufficiale è molto preciso: un framework per test e automazione web che controlla Chromium, Firefox e WebKit tramite un’unica API. Il percorso principale di Playwright è un test runner con fixture, asserzioni e trace viewer. Usarlo come base per lo scraping significa seguire la Library mode documentata: chromium.launch(), poi un context, poi una page, fuori dall’harness di test. Tutto ciò che trovi in questa recensione usa quella superficie API pubblica. Non ho eseguito un test di compatibilità tra versioni, quindi non sto affermando che ogni comportamento osservato sia stabile tra release diverse.

L’ampiezza documentata è il suo grande punto di forza altrove, quindi la riporto con precisione. Playwright controlla tre motori browser — Chromium, Firefox e WebKit — con una sola API, e offre client di primo livello in Python, Java e .NET oltre a JavaScript. Questo è documentato ed è davvero il vantaggio più ampio del tool in termini di struttura. Ciò che questo pass ha effettivamente verificato è più ristretto:

FunzionalitàStato in questa recensione
Motore ChromiumVerificato — tutti i test qui sono stati eseguiti su Chromium
Motore FirefoxDocumentato, non verificato qui
Motore WebKitDocumentato, non verificato qui
Un’unica API per i tre motoriDocumentato, non verificato qui
Client Python, Java e .NETDocumentato, non verificato qui
ProxyNon testato
Scala con più context in paralleloNon testata
Intercettazione rete per scraping API-firstNon testata

Se un target si comporta in modo diverso con WebKit di Safari, o il tuo team scrive in Python, questa ampiezza è l’argomento di Playwright — ma non prendere i miei risultati come prova di parità con Firefox o WebKit, perché non li ho testati.

Come funziona sotto il cofano

Il modello mentale è quello di un motore browser che controlli con script. chromium.launch() avvia un processo browser. Un context è una sessione isolata, con cookie, storage e cache propri; una page è una scheda dentro quel context. Chiami page.goto(url), aspetti la condizione che rappresenta la prontezza dell’applicazione e leggi il DOM risultante con helper come page.$$eval. È più vicino a un browser usato da una persona che al parsing di una risposta HTTP, ma non equivale all’ambiente reale: segnali headless, viewport, lingua, font, stato del profilo, percorso TLS/rete e difese del sito possono ancora modificare ciò che viene servito. Questa recensione non ha testato né il comportamento anti-bot né la parità con un browser di produzione.

page.screenshot() cattura la pagina renderizzata, intera o ritagliata, e nel mio run ha funzionato al primo colpo. E l’API request che ho citato — context.request.get — usa i cookie dello stesso context ma salta il rendering, quindi puoi mescolare “carica la pagina e leggi il DOM” e “colpisci direttamente l’endpoint JSON” nello stesso script senza cambiare strumento.

Ciò che non fa parte del suo motore interno è la meccanica del crawling. Non esistono scheduler delle richieste, visited-set persistente, policy di cortesia o pipeline di esportazione. Un attraversamento limitato è facile da scrivere, ma un frontier work affidabile richiede anche normalizzazione degli URL, gestione dei redirect, retry, regole di scope, throttling e recovery. Quel livello lo costruisci tu, oppure usi un framework che lo aggiunga attorno al browser engine.

Setup e installazione nella realtà

L’installazione si fa in due passi, e il secondo porta il peso principale del deployment. npm install playwright installa la libreria; un npx playwright install separato scarica le build dei browser (nel mio caso Chromium). Bisogna considerare spazio su disco, tempo di download, caching dei browser in CI e pulizia dei processi, invece di trattare il pacchetto npm come se fosse l’intero sistema eseguibile.

Se installi Playwright aspettandoti uno scraper e segui il tutorial sui test, inizierai con file di test e asserzioni expect(). Il codice di scraping invece usa direttamente l’API della libreria. Entrambe le modalità sono documentate, ma la differenza conta quando cerchi esempi e scegli i comandi di deploy.

In questo run, gli aspetti ergonomici utili erano concreti: i browser context isolavano lo stato della sessione, le chiamate async si componevano bene, uno screenshot richiedeva una sola chiamata e un HTTP 500 restava ispezionabile tramite l’oggetto response. Il lato più scomodo era operativo, non sintattico: la build del browser andava installata e gestita separatamente dalla libreria.

Risultati pratici

Measured results chart: Three data paths exercised

Tutti i numeri locali sono stati ottenuti contro un server fixture su 127.0.0.1, con la ground truth definita prima del crawl. L’harness esatto è in run_playwright_material_tests.mjs, e gli artifact grezzi committati includono la ground truth e gli output di ogni test. Queste restano osservazioni dell’autore, da una sola macchina e una sola esecuzione; i link servono a rendere riproducibile il test, non a trasformarlo in un benchmark generale.

TestTargetRisultato
Catalogo statico + paginazionefixture locale12/12 prodotti, recall 1.0
Estrazione articolofixture localetitolo + 3/3 paragrafi, boilerplate separato
Pagina JS dinamica (render nativo)fixture locale8/8, recall 1.0, screenshot full-page salvato
JSON API dinamica (page.request)fixture locale8/8, recall 1.0, nessun DOM renderizzato
Gestione HTTP 500fixture localestatus 500 ispezionabile, navigazione senza eccezione
Grafo di crawl (BFS scritto a mano)fixture locale12 pagine, profondità {0:1, 1:4, 2:7}
Books to Scrapedemo pubblica20 prodotti
Quotes JS (renderizzato via JS)demo pubblica10 citazioni, render nativo

Il ciclo di paginazione seguiva esplicitamente il link successivo; Playwright non scopriva le pagine da solo. Il selettore dell’articolo teneva navigazione e testo del footer fuori dal risultato del body. Nel percorso di errore, la navigazione restituiva un oggetto response con status 500 invece di lanciare un’eccezione, lasciando al chiamante la scelta di loggare, riprovare o andare avanti. I due target pubblici di pratica hanno restituito i conteggi mostrati in tabella.

Un confine importante da dichiarare chiaramente: tutto qui è stato eseguito su Chromium, su una sola macchina, una sola volta. La tabella delle funzionalità separa l’ampiezza documentata dal comportamento effettivamente verificato. Non ho rieseguito la suite su un’altra build di Playwright, quindi non ne segue alcuna conclusione cross-versione. Anche i tempi per test sono esclusi come benchmark: una sola passata con cronometro su un laptop non basta per un confronto di velocità.

La readiness fa parte del contratto di estrazione

I risultati dinamici dipendevano da attese che rappresentavano i dati desiderati, non dalla semplice navigazione del browser. Per il catalogo locale, l’harness ha navigato con waitUntil: 'domcontentloaded' e poi ha chiamato waitForSelector('#dynamic-products article.product-card') con timeout di 15 secondi. Il run pubblico Quotes JS ha usato lo stesso stato di navigazione e ha atteso .quote con timeout di 20 secondi. L’estrazione è avvenuta solo dopo la comparsa di quei selettori.

Questa distinzione conta quando adatti lo script. domcontentloaded dice che il documento iniziale è stato analizzato; non dice che sia arrivata una risposta API ritardata, che l’hydration sia finita, che una lista infinita abbia smesso di crescere o che una riga virtualizzata sia entrata nel viewport. Un selettore è utile quando basta la presenza di un solo elemento corrispondente. Se la completezza dipende da una risposta nota, dal numero di elementi, dallo stato dell’app o da una finestra di rete silenziosa, conviene invece attendere quella condizione. La condizione dovrebbe essere legata al contratto di output: “esiste almeno una card” e “tutte le pagine attese sono caricate” non sono la stessa cosa.

System diagram: Readiness Is an Extraction Contract

Anche la gestione dei timeout spetta al chiamante. Il test usava timeout finiti sui selettori, ma non studiava una policy di retry né distingueva una pagina lenta da un selettore cambiato in modo permanente. Un wrapper di produzione dovrebbe registrare quale condizione di readiness è fallita, salvare abbastanza stato della pagina da diagnosticarla e decidere se un altro tentativo di navigazione sia sicuro. Playwright ti dà eventi e DOM; non può dedurre da solo cosa significhi “dati completi” per il tuo lavoro.

Questo confine andrebbe documentato accanto a ogni extractor, non lasciato come timeout implicito.

Anche il percorso API ha un contratto parallelo. ctx.request.get era appropriato perché il browser context esisteva già e la condivisione della sessione può essere utile. Se un job scopre che il proprio endpoint dati funziona senza alcuna sessione browser, un request context standalone è un’architettura diversa, con lifecycle e comportamento dei cookie differenti. In questo run non ho confrontato le due opzioni. Considera “nessun DOM renderizzato” come il fatto misurato, poi decidi separatamente se il browser process serve davvero nel workflow più ampio.

La questione del crawl

Il risultato sul grafo di crawl è quello che ti dice come interpretare Playwright. Dodici pagine, tre livelli, corretto — e tutta la logica di attraversamento era mia. Playwright ha fornito la metà “apri questo URL e leggilo”; io ho fornito coda, visited-set e tracciamento della profondità.

Per lavori piccoli e ben delimitati non è un problema. Per lavori su scala crawl significa che stai scrivendo un crawler sopra una libreria browser, oppure affiancando Playwright a qualcosa che lo ha già fatto. Il pattern documentato è Crawlee, che avvolge Playwright (e Puppeteer) con una vera coda di richieste, storage per il dataset e auto-throttling — tu mantieni il rendering di Playwright e ne prendi in prestito l’orchestrazione. Se vuoi che la coda sia integrata nel framework stesso e non aggiunta dopo, Scrapy è progettato proprio così, anche se Scrapy è HTTP-first e non renderizza JavaScript da solo. Il punto non è che Playwright sia carente; è che “automazione browser” e “crawling” sono due lavori diversi, e Playwright rivendica solo il primo.

System diagram: The Crawl Layer Is Yours

La BFS di dodici pagine rende concreto il confine di responsabilità. Ha fornito una coda, un visited set e il tracciamento della profondità per un grafo controllato. Un frontier di produzione deve ancora definire canonicalizzazione degli URL, gestione dei redirect, host consentiti, chiavi duplicate, retry, concorrenza, delay per host, persistenza e semantica del riavvio. Anche l’export è una scelta a parte: il fixture ha scritto JSON e CSV perché l’harness lo faceva, non perché Playwright offra un’astrazione per dataset.

Anche il design della sessione influenza il wrapper. Un singolo browser può contenere più context con cookie e storage isolati, ma questa recensione non ha misurato la scalabilità con più context in parallelo né l’isolamento dei failure. Riutilizzare un context può preservare un login e ridurre il setup; crearne di separati può evitare contaminazioni di stato tra job. Sono politiche da crawler, anche se Playwright fornisce il primitivo context. Occorre benchmarkare il lifecycle scelto con la build del browser e l’ambiente di deployment che userai davvero.

Pro e contro

Pro:

  • Esecuzione di JavaScript tramite un vero motore Chromium; entrambi i target dinamici hanno raggiunto i selettori usati come condizione di readiness.
  • Screenshot full-page catturato al primo tentativo.
  • I selettori hanno estratto correttamente i campi del catalogo statico e dell’articolo dai fixture controllati.
  • browserContext.request ha raggiunto l’endpoint JSON senza renderizzare una pagina, mentre il processo browser già avviato restava parte dell’harness.
  • Robusto su una risposta errata: l’HTTP 500 era ispezionabile e la navigazione non generava eccezione.
  • Supporto documentato per tre motori (Chromium, Firefox, WebKit) tramite una sola API, più client Python, Java e .NET (documentati; qui è stato esercitato solo Chromium).
  • Apache-2.0 e mantenuto da Microsoft.
  • Esperienza di sviluppo pulita in library mode: una sola API tra i motori, async di primo livello, screenshot banali da ottenere.

Contro:

  • Nessuna coda di crawl, dataset o auto-throttle integrati: il lavoro su scala crawling è codice tuo oppure un wrapper come Crawlee.
  • Peso del browser: il download del binario e il costo per pagina sono il vero dazio rispetto a uno strumento solo HTTP.
  • Il framing predefinito è il test runner; per fare scraping bisogna sapere che esiste la library mode e uscire dal percorso promosso.
  • Sono stati esercitati solo Playwright 1.56.0 e Chromium; la parità tra versioni e tra motori non è stata testata.
  • Nessun output strutturato JSON-by-schema nativo; selettori e forma dei dati li scrivi tu.

A chi serve e chi dovrebbe evitarlo

Se il tuo problema è renderizzare pagine in cui i dati compaiono dopo l’esecuzione di JavaScript, o raccogliere screenshot insieme ai dati del DOM, Playwright è un candidato ragionevole da provare sui tuoi target. I team che già usano i test di Playwright possono riutilizzare gli stessi concetti e le stesse competenze sui selettori in library mode. I client Python, Java e .NET sono opzioni documentate, ma questa recensione ha esercitato solo Node e Chromium.

Considera un altro livello in tre casi. Se i dati richiesti sono già presenti in una risposta HTTP, uno strumento HTTP-first evita l’avvio del browser e il costo del rendering; Colly è una libreria crawler in quella categoria, mentre Trafilatura punta all’estrazione di articoli. Se ti servono coda, persistenza e throttling, usa un framework di crawling o un wrapper di Playwright. Se ti serve output con struttura a schema senza mantenere selettori, confronta i servizi di estrazione gestita. Nessuna di queste alternative è stata benchmarkata in questa recensione.

Se stai decidendo in particolare tra Playwright e Puppeteer, è un confronto a sé; la nostra analisi fianco a fianco li esegue entrambi sugli stessi fixture e mostra dove cade davvero la scelta.

Alternative e dove si colloca l’estrazione gestita

Playwright è gratuito, Apache-2.0 e self-hosted. Sei tu a dover gestire deployment del browser, selettori, condizioni di readiness, codice di crawl, aggiornamenti e gestione dei failure. Questa recensione non ha misurato le performance anti-bot né confrontato il costo operativo complessivo con un servizio gestito.

Nell’open source, i confronti utili si fanno per tipo di lavoro. Per il crawling su scala con un browser, Crawlee aggiunge la coda e il dataset che Playwright lascia fuori. Se il tuo obiettivo è ottenere Markdown pronto per LLM da un browser reale invece di righe modellate a mano, Crawl4AI avvia un browser e produce Markdown per quella pipeline. E se stai valutando più opzioni insieme, il nostro riepilogo degli scraper open source mette le categorie una accanto all’altra.

Disclosure: Thunderbit è il prodotto dell’editore e non è stato eseguito tramite questo fixture di Playwright. Rappresenta la categoria dell’estrazione gestita: il servizio si occupa del rendering e restituisce testo della pagina o record strutturati a schema, mentre Playwright lascia al developer il controllo del browser e della logica dei selettori. Il confronto riguarda quindi modello di hosting, forma dell’output e modello di costo, non un risultato di performance di questa recensione.

Prova Thunderbit per l’estrazione di dati web

Verdetto

Usa Playwright quando il tuo target richiede un motore browser e sei disposto a gestire condizioni di readiness, selettori e orchestrazione del crawl. La tabella dei risultati mostra che la sua library mode su Chromium ha gestito come previsto, in questo pass svolto dall’autore, i fixture controllati statici, dinamici, API, screenshot e di errore.

Mantieni intatto il perimetro delle evidenze: sono stati esercitati solo Chromium e Node, il percorso sulle 12 pagine dipendeva da una BFS scritta a mano, browserContext.request saltava il rendering della pagina ma non il processo browser già attivo, e ogni estrazione dinamica usava un selettore di readiness dichiarato. Questi vincoli fanno di Playwright un primitivo browser in questa recensione, non un sistema di crawling end-to-end misurato.

Prova Thunderbit per l’estrazione di dati web Get Started Free

FAQ

Mi servono ancora delle attese quando faccio scraping con Playwright? Sì. Il fatto che il browser stia eseguendo la pagina non dice allo script quando i dati dell’app sono pronti. Questi test hanno navigato fino a domcontentloaded e poi hanno atteso un selettore specifico prima di estrarre. Le pagine di produzione possono richiedere un segnale diverso, come una response, lo stato di un locator o un evento dell’applicazione.

Playwright può fare crawl di un intero sito da solo? Non out of the box. Non esistono una coda di richieste, un writer per il dataset o un auto-throttle integrati: il mio test sul grafo di crawl è arrivato a 12 pagine con profondità {0:1, 1:4, 2:7} solo perché ho scritto io il breadth-first search. Per lavori su scala crawling, abbina Playwright a Crawlee, che aggiunge un vero livello di crawling, oppure usa direttamente un framework da crawler.

Quando devo usare browserContext.request invece di un request context standalone? Usa browserContext.request quando le chiamate HTTP devono condividere i cookie con le pagine di un browser context già esistente. Usa playwright.request.newContext() quando vuoi un context solo API, senza avviare un browser, e non ti serve la condivisione automatica dei cookie con le pagine del browser. Qui è stato testato solo il primo percorso.

Firefox e WebKit sono stati testati qui? No. Tutti i test sono stati eseguiti su Chromium, su una sola macchina, in una sola run. Il supporto documentato di Playwright per tre motori (Chromium, Firefox, WebKit) e i client Python, Java e .NET è riportato come tale, non verificato; la parità con Firefox e WebKit, il proxying, la scala parallela e l’intercettazione di rete sono tutti fuori da ciò che questi numeri coprono.

Quale ambiente copriva questa recensione? Playwright 1.56.0, Node v22.22.3, macOS arm64 e solo Chromium. Firefox, WebKit, proxying, scala parallela, comportamento anti-bot e versioni successive di Playwright erano fuori dal run. L’installazione richiedeva la libreria più un download separato della build del browser.

Ke
Ke
CTO di Thunderbit | Senior Data Scientist ed esperto di ML Con quasi un decennio di esperienza nel machine learning e nella data science, Ke Shen è un ex studente della Columbia University ed ex Senior Data Scientist presso Walmart Labs. Grazie a una profonda competenza, riconosciuta dai suoi pari, in Python, R, Java e statistica, condivide insight collaudati sul passaggio di algoritmi AI complessi dalla teoria a un'architettura pronta per la produzione.
Indice
Thunderbit · Agente AI per dati web

Estrai dati da qualsiasi pagina in 1 clic

Scelto da oltre 250.000 utenti
piano gratuito disponibile
Da pagina web a foglio di calcolo
Descrivi ciò che ti serve — l'AI Agent di Thunderbit lo estrae ed esporta in Excel, Google Sheets, Airtable o Notion. Gratis per iniziare.
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week