Tutorial
Shotomatic Team
13 min di lettura

Come automatizzare gli screenshot dei siti web con Puppeteer

Usa Puppeteer e JavaScript per automatizzare gli screenshot di una pagina, pagine intere, batch di URL e viewport mobile con uno script riutilizzabile.

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

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

Puppeteer si presta bene perché il primo script può restare piccolo: avvia un browser, apre un URL e chiama page.screenshot(). La panoramica ufficiale di Puppeteer lo descrive come un'API JavaScript per controllare Chrome o Firefox, per impostazione predefinita in modalità headless.

Gli esempi usano la pagina degli strumenti di Shotomatic, https://www.shotomatic.com/tools, così puoi vedere come cambia la stessa pagina nelle acquisizioni per viewport, pagina intera, batch e mobile.

In breve: usa Puppeteer quando vuoi automatizzare gli screenshot nel codice. Imposta la viewport, attendi lo stato che ti interessa, chiama page.screenshot(), quindi aggiungi pagine intere, batch, nuovi tentativi e rapporti. Se ti serve soltanto acquisire un elenco di URL pubblici senza mantenere uno script, un flusso senza codice come Website Capture di Shotomatic può essere più semplice.

Nota: siamo gli sviluppatori di Shotomatic, quindi gli esempi usano come destinazione la nostra pagina pubblica degli strumenti. Puoi applicare gli stessi schemi Puppeteer ad altri siti pubblici per cui hai il permesso. Se invece ti serve un elenco di URL senza codice, usa Website Capture in Shotomatic.

Quando Puppeteer è lo strumento adatto

Puppeteer è indicato quando il flusso degli screenshot appartiene al codice e il lavoro consiste soprattutto nel controllare il browser.

Usalo per:

  • acquisire pagine pubbliche da uno script Node.js;
  • riutilizzare il comportamento di rendering di Chrome;
  • eseguire l'accesso o chiudere un popup prima dell'acquisizione;
  • salvare screenshot della viewport, a pagina intera, PNG, JPEG o WebP.

Lo screenshot proviene da un vero browser headless, non da una libreria di immagini. È il motivo principale per cui Puppeteer è utile in questo caso.

Installa Puppeteer per uno script di screenshot

Per un semplice progetto Node.js, installa Puppeteer:

$ mkdir website-screenshots-puppeteer
$ cd website-screenshots-puppeteer
$ npm init -y
$ npm i puppeteer
$ mkdir scripts screenshots

Il pacchetto standard puppeteer scarica una versione compatibile di Chrome for Testing durante l'installazione. La documentazione ufficiale sull'installazione segnala anche il download di un eseguibile chrome-headless-shell. Se gestisci già il browser o ti colleghi a un browser remoto, usa invece puppeteer-core.

I gestori di pacchetti moderni a volte bloccano gli script di installazione. Se Puppeteer è presente ma manca il browser, la documentazione sulla configurazione mostra come installarlo separatamente:

$ npx puppeteer browsers install

Acquisisci lo screenshot di un sito

Crea scripts/capture-one.mjs:

import puppeteer from "puppeteer";
import { mkdir } from "node:fs/promises";

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

const browser = await puppeteer.launch();
const page = await browser.newPage();

try {
  await mkdir("screenshots", { recursive: true });

  await page.setViewport({
    width: 1440,
    height: 1000,
    deviceScaleFactor: 1,
  });

  await page.goto(url, {
    waitUntil: "networkidle2",
    timeout: 45_000,
  });

  await page.screenshot({ path: outputPath });
  console.log(`Saved ${outputPath}`);
} finally {
  await browser.close();
}

Eseguilo:

$ node scripts/capture-one.mjs

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

Risultato dell'esempio Puppeteer capture-one con la pagina degli strumenti di Shotomatic
Pagina degli strumenti acquisita dallo script qui sopra con una viewport di 1440 × 1000.

La guida ufficiale agli screenshot di Puppeteer usa lo stesso flusso di base: avvio del browser, apertura della pagina, navigazione verso un URL e chiamata a page.screenshot(). L'estensione del path determina il formato, a meno che tu non passi direttamente un'opzione type. La documentazione di ScreenshotOptions e ImageFormat tratta fullPage, path, quality e i formati supportati png, jpeg e webp.

Imposta la viewport prima della navigazione. La documentazione di page.setViewport() lo sottolinea perché alcuni siti si comportano diversamente quando la pagina viene ridimensionata dopo il caricamento, soprattutto nelle impaginazioni mobile.

Acquisisci uno screenshot a pagina intera

Per uno screenshot a pagina intera, passa fullPage: true. ScreenshotOptions di Puppeteer definisce fullPage come l'opzione per acquisire l'intera pagina:

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

Usa la pagina intera quando ti serve tutto il documento scorrevole. Usa uno screenshot della viewport quando conta l'aspetto della pagina su uno schermo di dimensioni precise.

Screenshot Puppeteer a pagina intera della pagina degli strumenti gratuiti di Shotomatic
Risultato a pagina intera della stessa pagina `/tools`, 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.

Crea un esecutore riutilizzabile per gli URL

Quando lo screenshot singolo funziona, sposta l'URL in uno script che accetta un elenco. L'esempio continua a usare soltanto /tools, così il risultato resta facile da seguire. In seguito potrai aggiungere altri URL allo stesso array.

Crea scripts/capture-batch.mjs:

import puppeteer from "puppeteer";
import { mkdir } from "node:fs/promises";

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

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.createBrowserContext();
  const page = await context.newPage();
  const outputPath = `${outputDir}/${filenameForUrl(url, index)}`;

  try {
    await page.setViewport({
      width: 1440,
      height: 1000,
      deviceScaleFactor: 1,
    });

    await page.goto(url, {
      waitUntil: "networkidle2",
      timeout: 45_000,
    });

    await page.waitForSelector("body", {
      visible: true,
      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();
  }
}

await mkdir(outputDir, { recursive: true });

const browser = await puppeteer.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();
}

Il codice mantiene aperto un browser e crea un contesto separato per ogni URL. La documentazione di BrowserContext descrive questi contesti come sessioni isolate, con cookie e archiviazione distinti. È utile quando una pagina imposta cookie o dati locali durante l'acquisizione.

Il ciclo è volutamente sequenziale. È più lento, ma inizialmente più facile da correggere.

Aggiungi un limite di concorrenza ai batch grandi

Per pochi URL, l'acquisizione sequenziale va bene. Per gli elenchi più lunghi, aggiungi un piccolo limite di concorrenza.

Parti da 2-4 acquisizioni simultanee. Un browser headless usa comunque CPU e memoria; batch aggressivi possono attivare limiti o produrre caricamenti instabili.

Aggiungi questo helper:

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),
);

Il batch guadagna velocità senza permettere allo script di aprire un numero illimitato di pagine.

Acquisisci screenshot mobile

Per uno screenshot in formato mobile, imposta la viewport prima di page.goto():

await page.setViewport({
  width: 390,
  height: 844,
  deviceScaleFactor: 2,
  isMobile: true,
  hasTouch: true,
});

await page.goto("https://www.shotomatic.com/tools", {
  waitUntil: "networkidle2",
});

await page.screenshot({
  path: "screenshots/shotomatic-tools-mobile-viewport.png",
});

Usalo quando basta una viewport mobile approssimativa. Ecco la stessa pagina degli strumenti acquisita con queste dimensioni:

Screenshot Puppeteer della pagina degli strumenti di Shotomatic in una viewport mobile
Viewport mobile acquisita con Puppeteer. Il file misura 780 × 1688 perché lo script usa deviceScaleFactor 2.

Se ti serve un profilo più completo, Puppeteer espone anche i dispositivi noti:

import puppeteer, { KnownDevices } from "puppeteer";

const iPhone = KnownDevices["iPhone 15"];

await page.emulate(iPhone);
await page.goto("https://www.shotomatic.com/tools", {
  waitUntil: "networkidle2",
});
await page.screenshot({
  path: "screenshots/shotomatic-tools-iphone-15.png",
  fullPage: true,
});

L'emulazione del dispositivo è più utile quando contano viewport, user agent, comportamento del tocco e fattore di scala. La documentazione di page.emulate() segnala che imposta sia user agent sia viewport, quindi usala prima della navigazione.

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 Puppeteer 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 Puppeteer 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 i comuni comportamenti di caricamento differito:

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

    window.scrollTo(0, 0);
  });
}

Usalo prima di page.screenshot():

await page.goto(url, {
  waitUntil: "networkidle2",
  timeout: 45_000,
});

await triggerLazyLoading(page);

await page
  .waitForFunction(
    () => Array.from(document.images).every((image) => image.complete),
    { 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.

networkidle2 è un punto di partenza ragionevole per molti script e la stessa guida di Puppeteer lo usa nell'esempio di base. Consideralo un inizio, non la garanzia che la pagina sia pronta. Alcuni siti mantengono richieste in background; altri mostrano il contenuto principale dopo che la rete è diventata inattiva.

Strategie comuni:

  • usa waitUntil: "networkidle2" per le normali pagine pubbliche;
  • usa page.waitForSelector() quando un elemento preciso dimostra che la pagina è pronta;
  • usa page.waitForFunction() quando la prontezza dipende dallo stato dell'app;
  • scorri prima dell'acquisizione se le immagini vengono caricate in differita;
  • evita pause fisse come regola principale di attesa.

Per esempio:

await page.goto(url, {
  waitUntil: "networkidle2",
  timeout: 45_000,
});

await page.waitForSelector("main", {
  visible: true,
  timeout: 15_000,
});

await page.screenshot({
  path: outputPath,
  fullPage: true,
});

Se gestisci il sito di destinazione, le attese legate a un selettore sono di solito più affidabili di una condizione generica della rete.

Aggiungi nuovi tentativi e un rapporto

I batch reali falliscono per motivi normali: errori di rete temporanei, pagine lente, reindirizzamenti, limiti di frequenza, banner dei cookie e script di terze parti.

Aggiungi nuovi tentativi intorno a ogni URL:

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 di acquisizione:

await withRetries(async () => {
  await page.goto(url, {
    waitUntil: "networkidle2",
    timeout: 45_000,
  });

  await page.waitForSelector("body", {
    visible: true,
    timeout: 15_000,
  });

  await page.screenshot({
    path: outputPath,
    fullPage: true,
  });
});

Per i lavori da ripetere, scrivi un rapporto JSON con URL, percorso del risultato, stato di successo, messaggio di errore e data e ora. Lo screenshot mostra la pagina; il rapporto spiega la sessione.

Una struttura pratica per le cartelle

Per i lavori da rieseguire, separa input, output e rapporti:

website-screenshots-puppeteer/
  scripts/
    capture-batch.mjs
  urls/
    product-pages.txt
    competitor-pages.txt
  screenshots/
    2026-06-23-product-pages/
    2026-06-23-competitors/
  reports/
    2026-06-23-product-pages.json

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

Errori comuni

  • Acquisizione prima che la pagina sia pronta: il completamento di page.goto() non garantisce che il contenuto importante sia stato mostrato. Attendi un selettore o uno stato dell'app.
  • Viewport impostata troppo tardi: impostala prima della navigazione, così la pagina usa l'impaginazione corretta fin dall'inizio.
  • fullPage usato come gestione del caricamento differito: l'opzione cambia l'area dello screenshot, ma non garantisce che ogni immagine differita sia stata caricata.
  • Troppe pagine aperte insieme: parti da una bassa concorrenza. L'automazione del browser può sovraccaricare il computer o il sito di destinazione.
  • Nomi file riutilizzati: gli URL possono differire soltanto per barra finale, query string o maiuscole nel percorso. Aggiungi un indice e normalizza i nomi.
  • Pulizia saltata: chiudi contesti e browser. I processi lunghi diventano instabili quando pagine e sessioni restano aperte.

Alternative a uno script Puppeteer

Puppeteer è soltanto uno dei modi per automatizzare gli screenshot dei siti.

Se il lavoro resta nel codice, Playwright è l'alternativa più vicina. Spieghiamo questo approccio in come automatizzare gli screenshot dei siti web con Playwright. Spesso è la scelta più adatta quando gli screenshot fanno parte di una suite di test più ampia, quando serve coprire Chromium, Firefox e WebKit oppure quando si vogliono usare il test runner, le tracce e i report di Playwright Test per le acquisizioni.

Se stai integrando l'acquisizione in un prodotto, un'API per screenshot può ridurre il lavoro rispetto alla gestione autonoma dei browser. Quando lo script deve funzionare in modo affidabile per altre persone, binari del browser, memoria della CI, nuovi tentativi, pulizia e stato di accesso diventano responsabilità tua.

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 uno script Puppeteer.

FAQ

Puppeteer può automatizzare gli screenshot dei siti web?

Sì. Puppeteer può avviare un browser, aprire un URL, impostare la viewport e salvare screenshot con page.screenshot().

Puppeteer 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 Puppeteer?

Sì. Mantieni aperto un browser, crea una pagina o un contesto per ogni URL e aggiungi un limite di concorrenza per i batch più grandi.

Puppeteer è migliore di Playwright per gli screenshot?

Puppeteer è spesso più semplice per script incentrati su Chrome. Playwright è di solito più adatto quando servono Chromium, Firefox e WebKit oppure un flusso di test più ampio.

Quando conviene usare Shotomatic invece di Puppeteer?

Usa Shotomatic quando serve acquisire un elenco di URL senza codice e una persona deve avviare, controllare ed esportare il lavoro senza mantenere uno script JavaScript.

Riferimenti

Gli esempi qui sopra sono stati controllati sulla documentazione ufficiale:

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 Puppeteer | Blog | Shotomatic