Tutorial
Shotomatic Team
13 min di lettura

Come automatizzare gli screenshot dei siti web con Playwright

Usa Playwright e JavaScript per automatizzare gli screenshot di una pagina, pagine intere, viste mobile e batch di URL con nuovi tentativi.

Portatile che mostra il codice di uno script automatico per screenshot di siti web

In questo articolo costruiremo uno script Playwright per gli screenshot in più fasi: prima una singola viewport, poi una pagina intera, un esecutore riutilizzabile per gli URL e una viewport mobile.

Playwright è adatto quando gli screenshot devono stare accanto al codice che li produce. Offre il rendering di un browser reale, contesti isolati, controllo esplicito della viewport, emulazione dei dispositivi e strumenti per attendere lo stato della pagina che ti interessa.

Gli esempi usano la pagina delle funzioni di Shotomatic, https://www.shotomatic.com/features, così puoi confrontare la stessa pagina nelle acquisizioni per viewport, pagina intera, batch e mobile.

In breve: usa Playwright quando l'automazione degli screenshot deve vivere nel codice. Crea un contesto del browser, imposta viewport o dispositivo, attendi lo stato che ti interessa, chiama page.screenshot(), quindi aggiungi pagine intere, batch, nuovi tentativi e rapporti. Se non vuoi occuparti dello script e della sua manutenzione, Website Capture di Shotomatic può essere più semplice.

Nota: siamo gli sviluppatori di Shotomatic, quindi gli esempi usano come destinazione la nostra pagina pubblica delle funzioni. Playwright può acquisire altri siti pubblici per cui hai il permesso. Se invece ti serve un elenco di URL senza codice, usa Website Capture in Shotomatic.

Quando Playwright è lo strumento adatto

Usa Playwright quando lo screenshot deve seguire la logica del browser.

È indicato per:

  • acquisire screenshot nella CI;
  • eseguire l'accesso prima dell'acquisizione;
  • attendere un selettore o uno stato preciso dell'app;
  • eseguire lo stesso lavoro con Chromium, Firefox o WebKit;
  • creare screenshot adattivi da impostazioni esplicite della viewport;
  • generare screenshot dentro uno strumento interno.

Diventa meno comodo quando chi ha bisogno degli screenshot non mantiene lo script. Se il lavoro deve vivere nel codice, Playwright è un buon punto di partenza.

Installa Playwright per uno script di screenshot

Per un semplice script Node.js, installa la libreria Playwright e il browser che vuoi automatizzare:

$ mkdir website-screenshots-playwright
$ cd website-screenshots-playwright
$ npm init -y
$ npm i -D playwright
$ npx playwright install chromium

La documentazione della libreria di Playwright usa la stessa struttura di base: installazione del pacchetto e dei browser, importazione di Playwright, avvio di un browser e interazione con le pagine. Se in seguito ti servono Firefox o WebKit, installa anche quei browser.

Crea le cartelle per script e risultati:

$ mkdir scripts screenshots

Acquisisci lo screenshot di un sito

Crea scripts/capture-one.js:

const { chromium } = require("playwright");
const fs = require("node:fs/promises");

const url = "https://www.shotomatic.com/features";
const outputPath = "screenshots/shotomatic-features.png";

(async () => {
  await fs.mkdir("screenshots", { recursive: true });

  const browser = await chromium.launch();
  const context = await browser.newContext({
    viewport: { width: 1440, height: 1000 },
    deviceScaleFactor: 1,
  });

  const page = await context.newPage();

  try {
    await page.goto(url, { waitUntil: "load", timeout: 45_000 });
    await page.screenshot({ path: outputPath });
    console.log(`Saved ${outputPath}`);
  } finally {
    await context.close();
    await browser.close();
  }
})();

Eseguilo:

$ node scripts/capture-one.js

Il codice acquisisce la viewport visibile. L'API per gli screenshot di Playwright supporta anche pagine intere, singoli elementi, buffer, ritagli e opzioni di qualità.

L'esecuzione crea screenshots/shotomatic-features.png:

Risultato dello script Playwright capture-one con la pagina delle funzioni di Shotomatic
Pagina delle funzioni acquisita dallo script qui sopra con una viewport di 1440 × 1000.

Acquisisci uno screenshot a pagina intera

Per uno screenshot a pagina intera, passa fullPage: true:

await page.screenshot({
  path: "screenshots/shotomatic-features-full-page.png",
  fullPage: true,
});

Usa la pagina intera quando ti serve tutto il documento dall'alto verso il basso. Usa uno screenshot della viewport quando conta ciò che un visitatore vede su uno schermo di dimensioni precise.

Screenshot Playwright a pagina intera della pagina delle funzioni di Shotomatic
Risultato a pagina intera della stessa pagina `/features`, dopo il rendering delle sezioni sotto la piega.

Anche l'acquisizione a pagina intera dipende dallo stato della pagina. fullPage: true cambia l'area, ma non forza il rendering di immagini caricate in differita, animazioni o sezioni attivate quando entrano nella viewport. La sezione sul caricamento differito spiega perché conta lo stato della pagina.

Inserisci l'URL di destinazione in uno script batch

Quando lo script per una pagina funziona, sposta l'URL in uno script che accetta un elenco e salva i file con nomi prevedibili. L'esempio continua a usare soltanto /features, così il risultato resta facile da seguire. In seguito potrai aggiungere altri URL allo stesso array.

Crea scripts/capture-batch.js:

const { chromium } = require("playwright");
const fs = require("node:fs/promises");

const urls = ["https://www.shotomatic.com/features"];

const outputDir = "screenshots";

function filenameForUrl(url, index) {
  const parsed = new URL(url);
  const rawName = `${parsed.hostname}${parsed.pathname}`;

  const slug = rawName
    .replace(/^www\./, "")
    .replace(/\/$/, "")
    .replace(/[^a-z0-9]+/gi, "-")
    .replace(/^-+|-+$/g, "")
    .toLowerCase();

  return `${String(index + 1).padStart(2, "0")}-${slug || "home"}.png`;
}

async function captureUrl(browser, url, index) {
  const context = await browser.newContext({
    viewport: { width: 1440, height: 1000 },
    deviceScaleFactor: 1,
  });

  const page = await context.newPage();
  const outputPath = `${outputDir}/${filenameForUrl(url, index)}`;

  try {
    await page.goto(url, { waitUntil: "load", timeout: 45_000 });
    await page.locator("body").waitFor({ state: "visible", timeout: 15_000 });
    await page.screenshot({ path: outputPath, fullPage: true });

    console.log(`Captured ${url} -> ${outputPath}`);
    return { url, ok: true, outputPath };
  } catch (error) {
    console.error(`Failed ${url}: ${error.message}`);
    return { url, ok: false, error: error.message };
  } finally {
    await context.close();
  }
}

(async () => {
  await fs.mkdir(outputDir, { recursive: true });

  const browser = await chromium.launch();

  try {
    const results = [];

    for (const [index, url] of urls.entries()) {
      results.push(await captureUrl(browser, url, index));
    }

    const failed = results.filter((result) => !result.ok);

    if (failed.length > 0) {
      process.exitCode = 1;
      console.error(`${failed.length} screenshot(s) failed.`);
    }
  } finally {
    await browser.close();
  }
})();

Questa versione è volutamente sequenziale. È più lenta, ma molto più facile da correggere. Aggiungi la concorrenza soltanto quando funziona in modo affidabile sulle tue pagine.

Aggiungi un limite di concorrenza ai batch grandi

Per 5 URL basta un ciclo semplice. Per 50 o 500, probabilmente serve un piccolo limite di concorrenza.

Evita di aprire centinaia di pagine insieme. L'automazione dei browser usa molta memoria e molti siti applicano limiti o smettono di rispondere se ricevono richieste troppo aggressive. Parti da 2-4 acquisizioni simultanee e aumenta soltanto dopo avere ottenuto risultati stabili.

Aggiungi lo stesso helper usato nella versione Puppeteer di questa guida:

async function runWithConcurrency(items, limit, worker) {
  const results = new Array(items.length);
  let nextIndex = 0;

  async function runNext() {
    while (nextIndex < items.length) {
      const currentIndex = nextIndex;
      nextIndex += 1;
      results[currentIndex] = await worker(items[currentIndex], currentIndex);
    }
  }

  const workers = Array.from(
    { length: Math.min(limit, items.length) },
    () => runNext(),
  );

  await Promise.all(workers);
  return results;
}

Poi sostituisci il ciclo sequenziale:

const results = await runWithConcurrency(urls, 3, (url, index) =>
  captureUrl(browser, url, index),
);

In questo modo resta aperto un solo browser, ogni acquisizione usa un contesto isolato e il numero di pagine attive contemporaneamente rimane limitato. La documentazione Library di Playwright usa contesti espliciti nel flusso degli script Node, assegnando a ogni acquisizione il proprio ciclo di vita.

Acquisisci screenshot mobile o tablet

Playwright include profili di emulazione dei dispositivi per telefoni e tablet comuni. Per esempio:

const { chromium, devices } = require("playwright");

(async () => {
  const browser = await chromium.launch();
  const iPhone = devices["iPhone 13"];

  const context = await browser.newContext({
    ...iPhone,
  });

  const page = await context.newPage();
  await page.goto("https://www.shotomatic.com/features", {
    waitUntil: "load",
  });
  await page.screenshot({
    path: "screenshots/shotomatic-features-iphone-13.png",
  });

  await context.close();
  await browser.close();
})();

Ecco la stessa pagina delle funzioni acquisita con il profilo iPhone 13:

Screenshot Playwright della pagina delle funzioni di Shotomatic in una viewport mobile
Viewport mobile acquisita da Playwright con il profilo del dispositivo iPhone 13.

Usa i profili quando contano user agent, supporto del tocco, viewport e fattore di scala del dispositivo. Usa una viewport semplice quando ti servono soltanto dimensioni precise:

const context = await browser.newContext({
  viewport: { width: 390, height: 844 },
  deviceScaleFactor: 2,
  isMobile: true,
});

Gestisci il contenuto caricato in differita

Molte pagine caricano immagini o mostrano sezioni soltanto quando entrano nella viewport. Se acquisisci troppo presto la pagina intera, lo screenshot può conservare spazi vuoti dove il contenuto sotto la piega non è ancora comparso.

Screenshot Playwright a pagina intera con una grande area vuota prima del rendering delle sezioni caricate in differita
Acquisizione immediata: l'altezza è corretta, ma parte del contenuto non è ancora stata mostrata.
Screenshot Playwright a pagina intera dopo lo scorrimento delle sezioni caricate in differita
Acquisizione dopo lo scorrimento: le sezioni sotto la piega hanno avuto il tempo di comparire.

Prima dello screenshot a pagina intera, scorri il documento per attivare immagini caricate in differita e sezioni mostrate all'ingresso nella viewport:

async function triggerLazyLoading(page) {
  await page.evaluate(async () => {
    const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
    const viewportHeight = window.innerHeight;
    const scrollHeight = document.body.scrollHeight;

    for (let y = 0; y < scrollHeight; y += viewportHeight) {
      window.scrollTo(0, y);
      await delay(600);
    }

    window.scrollTo(0, 0);
    await delay(300);
  });
}

Usalo prima di page.screenshot():

await page.goto(url, { waitUntil: "load", timeout: 45_000 });
await triggerLazyLoading(page);
await page
  .waitForFunction(
    () =>
      Array.from(document.images).every(
        (image) => image.complete && image.naturalWidth > 0,
      ),
    null,
    { timeout: 15_000 },
  )
  .catch(() => {});
await page.screenshot({ path: outputPath, fullPage: true });

Per alcune pagine serve altro. Contenitori di scorrimento personalizzati, animazioni, chiamate API ritardate e contenuti virtualizzati possono richiedere un selettore o uno stato specifico.

Scegli la strategia di attesa adatta

Gran parte dei problemi di affidabilità degli screenshot dipende dai tempi.

La documentazione sulla navigazione di Playwright spiega che, per impostazione predefinita, page.goto() attende l'evento load; le pagine moderne, però, possono continuare a recuperare dati o eseguire il rendering. Per gli screenshot, in genere conviene:

  • partire da waitUntil: "load" per le normali pagine pubbliche;
  • attendere un locator preciso che dimostri il rendering del contenuto importante;
  • usare page.waitForFunction() quando la prontezza dipende dallo stato dell'app;
  • scorrere prima dell'acquisizione se le immagini vengono caricate in differita;
  • evitare pause fisse come regola principale di attesa.

Per esempio:

await page.goto(url, { waitUntil: "load", timeout: 45_000 });
await page
  .getByRole("heading", { name: "Shotomatic Features" })
  .waitFor({ state: "visible", timeout: 15_000 });
await page.screenshot({ path: outputPath, fullPage: true });

Non dare per scontato che una sola regola funzioni ovunque. Se gestisci il sito di destinazione, un locator o un'attesa legata allo stato dell'app è di solito più affidabile di una condizione generica della rete.

Aggiungi nuovi tentativi

Le richieste di rete falliscono, le pagine superano il timeout e i widget di terze parti possono bloccarsi. Uno script batch reale deve riprovare l'URL non riuscito prima di abbandonare la sessione.

async function withRetries(task, retries = 2) {
  let lastError;

  for (let attempt = 0; attempt <= retries; attempt += 1) {
    try {
      return await task();
    } catch (error) {
      lastError = error;
      console.warn(`Attempt ${attempt + 1} failed: ${error.message}`);
    }
  }

  throw lastError;
}

Avvolgi il lavoro sulla pagina:

await withRetries(async () => {
  await page.goto(url, { waitUntil: "load", timeout: 45_000 });
  await page.screenshot({ path: outputPath, fullPage: true });
});

In produzione, scrivi anche un rapporto JSON con URL, percorso del risultato, stato, messaggio di errore e data e ora. In seguito farà risparmiare tempo quando qualcuno chiederà quali pagine non sono riuscite e perché.

Una struttura pratica per le cartelle

Per i lavori da ripetere, mantieni separati input, output e script:

website-screenshots-playwright/
  scripts/
    capture-batch.js
  urls/
    launch-pages.txt
    competitor-pages.txt
  screenshots/
    2026-06-23-launch/
    2026-06-23-competitors/
  reports/
    2026-06-23-launch.json

La struttura rende il flusso più facile da rieseguire e mantiene il gruppo di screenshot associato all'elenco di URL che lo ha prodotto.

Errori comuni

  • Acquisizione troppo anticipata: il browser può raggiungere load prima che il contenuto importante sia visibile. Attendi un selettore o aggiungi un controllo specifico per la pagina.
  • Troppe pagine aperte insieme: un'elevata concorrenza può bloccare il browser, esaurire la memoria o attivare limiti. Parti con un numero ridotto.
  • Un unico schema di nomi per ogni sito: gli URL possono contenere query string, barre finali, duplicati e caratteri insoliti. Normalizza sempre i nomi e includi un indice.
  • Pulizia del browser dimenticata: chiudi pagine, contesti e browser. I contesti non chiusi diventano un problema concreto nei processi lunghi.
  • Screenshot a pagina intera trattati come test visivi: il file è una prova. Un test di regressione visiva richiede anche baseline, soglie di confronto e un processo di revisione.

Alternative a uno script Playwright

Playwright non è l'unico modo per automatizzare gli screenshot dei siti. La scelta dipende da chi sarà responsabile del lavoro dopo l'esistenza del primo script.

Se ti basta uno script mirato per automatizzare il browser, Puppeteer può essere sufficiente. Playwright è più adatto quando gli screenshot affiancano una suite di test più ampia, servono Chromium, Firefox e WebKit oppure il runner, le tracce e i rapporti di Playwright Test fanno già parte del lavoro.

Se stai integrando l'acquisizione in un prodotto, un'API per screenshot può ridurre il lavoro rispetto alla gestione autonoma dei browser.

Se non vuoi occuparti di scrivere e mantenere questo script, leggi come automatizzare gli screenshot dei siti web senza codice, oppure usa Website Capture in Shotomatic per acquisire schermate da un elenco di URL, impostare opzioni per ogni pagina, rivedere i risultati ed esportarli senza mantenere codice Playwright.

FAQ

Playwright può automatizzare gli screenshot dei siti web?

Sì. Playwright può avviare un browser, aprire un URL, impostare viewport o dispositivo e salvare uno screenshot della pagina con page.screenshot().

Playwright può acquisire screenshot a pagina intera?

Sì. Passa fullPage: true a page.screenshot() per acquisire l'intera pagina scorrevole anziché la sola viewport visibile.

Posso acquisire più URL con Playwright?

Sì. Inserisci gli URL in un array, scorrili con un ciclo e crea un nuovo contesto del browser per ogni acquisizione. Per i batch più grandi, aggiungi un limite di concorrenza e la gestione degli errori.

Devo usare Playwright o Puppeteer per gli screenshot dei siti web?

Usa Playwright se vuoi coprire più browser, emulare dispositivi o inserire gli screenshot in un flusso di test più ampio. Puppeteer è pratico anche per script incentrati su Chrome.

Quando è preferibile un flusso senza codice rispetto a Playwright?

È preferibile quando utenti non tecnici devono avviare, controllare o esportare screenshot da elenchi di URL senza mantenere script, dipendenze dei browser o processi CI.

Riferimenti

Gli esempi qui sopra sono stati controllati sulla documentazione ufficiale attuale:

Articoli correlati

Altri articoli

Automatizza gli screenshot dei siti web senza scrivere codice

Con Website Capture puoi acquisire elenchi di URL, impostare ogni pagina, controllare i risultati ed esportarli senza mantenere uno script.

Come automatizzare gli screenshot dei siti web con Playwright | Blog | Shotomatic