Website-Screenshots mit Puppeteer automatisieren
Automatisiere Website-Screenshots mit Puppeteer und JavaScript. Erfasse einzelne Seiten, Ganzseiten, URL-Stapel und mobile Ansichten mit einem wiederverwendbaren Skript.

In dieser Anleitung bauen wir schrittweise ein Screenshot-Skript mit Puppeteer: zuerst eine einzelne Ansichtsgröße, danach eine Ganzseiten-Aufnahme, einen wiederverwendbaren URL-Runner und eine mobile Ansicht.
Puppeteer passt gut dazu, weil schon das erste Skript klein bleiben kann: Browser starten, URL öffnen und page.screenshot() aufrufen. Die offizielle Puppeteer-Übersicht beschreibt Puppeteer als JavaScript-API zur Steuerung von Chrome oder Firefox, standardmäßig im Headless-Modus.
Die Beispiele verwenden die Werkzeugseite von Shotomatic unter https://www.shotomatic.com/tools. So kannst du vergleichen, wie dieselbe Seite als Ansichtsgröße, Ganzseite, stapelfähige Aufnahme und mobile Ansicht aussieht.
Kurz gesagt: Nutze Puppeteer, wenn Screenshot-Automatisierung in Code gehört. Lege die Ansichtsgröße fest, warte auf den wichtigen Seitenzustand, rufe
page.screenshot()auf und ergänze danach Ganzseiten-Aufnahmen, Stapel, Wiederholungen und Berichte. Wenn du nur eine Liste öffentlicher URLs ohne eigenes Skript aufnehmen möchtest, ist ein No-Code-Ablauf wie Website Capture von Shotomatic möglicherweise einfacher.
Wann Puppeteer das richtige Werkzeug ist
Puppeteer passt, wenn der Screenshot-Ablauf in Code gehört und es hauptsächlich um die Steuerung des Browsers geht.
Nutze Puppeteer, wenn du:
- öffentliche Seiten mit einem Node.js-Skript aufnehmen möchtest
- das Rendering-Verhalten von Chrome für Screenshots nutzen möchtest
- dich vor der Aufnahme anmelden oder ein Pop-up schließen musst
- Ansichtsgrößen-, Ganzseiten-, PNG-, JPEG- oder WebP-Screenshots speichern möchtest
Der Screenshot stammt aus einem echten Headless-Browser, nicht aus einer Bildbibliothek. Genau deshalb ist Puppeteer hier so nützlich.
Puppeteer für ein Screenshot-Skript installieren
Installiere Puppeteer für ein gewöhnliches Node.js-Projekt:
$ mkdir website-screenshots-puppeteer
$ cd website-screenshots-puppeteer
$ npm init -y
$ npm i puppeteer
$ mkdir scripts screenshots
Das Standardpaket puppeteer lädt bei der Installation eine kompatible Version von Chrome for Testing herunter. Laut der offiziellen Installationsdokumentation von Puppeteer wird außerdem eine chrome-headless-shell-Binärdatei heruntergeladen. Wenn du den Browser bereits selbst verwaltest oder eine Verbindung zu einem Remote-Browser herstellst, nutze stattdessen puppeteer-core.
Moderne Paketmanager blockieren gelegentlich Installationsskripte. Falls Puppeteer installiert ist, aber der Browser fehlt, zeigt die offizielle Konfigurationsdokumentation, wie du Browser separat installierst:
$ npx puppeteer browsers install
Einen Website-Screenshot aufnehmen
Erstelle 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();
}
Führe das Skript aus:
$ node scripts/capture-one.mjs
Das Skript erzeugt screenshots/shotomatic-tools.png:
Die offizielle Screenshot-Anleitung von Puppeteer verwendet denselben grundlegenden Ablauf: Browser starten, Seite öffnen, zu einer URL navigieren und page.screenshot() aufrufen. Die Dateiendung in path bestimmt das Ausgabeformat, sofern du nicht ausdrücklich die Option type übergibst. In der Dokumentation zu ScreenshotOptions und ImageFormat findest du Einzelheiten zu fullPage, path, quality und den unterstützten Formaten png, jpeg und webp.
Lege die Ansichtsgröße vor der Navigation fest. Darauf weist auch die Dokumentation zu page.setViewport() hin, weil manche Websites anders reagieren, wenn ihre Größe erst nach dem Laden geändert wird. Das gilt besonders für mobile Layouts.
Einen Ganzseiten-Screenshot aufnehmen
Übergib für einen Ganzseiten-Screenshot fullPage: true. In Puppeteers ScreenshotOptions ist fullPage als Option für die Aufnahme der gesamten Seite definiert:
await page.screenshot({
path: "screenshots/shotomatic-tools-full-page.png",
fullPage: true,
});
Nutze die Ganzseiten-Aufnahme, wenn du das gesamte scrollbare Dokument brauchst. Ein Ansichtsgrößen-Screenshot ist die bessere Wahl, wenn es darum geht, wie die Seite bei einer bestimmten Bildschirmgröße aussieht.
Auch eine Ganzseiten-Aufnahme hängt vom Zustand der Seite ab. fullPage: true ändert den Aufnahmebereich, zwingt aber weder Lazy-Loading-Bilder noch Animationen oder beim Scrollen eingeblendete Abschnitte zum Rendern. Der Abschnitt zu Lazy Loading weiter unten zeigt, warum der Seitenzustand wichtig ist.
Einen wiederverwendbaren URL-Runner erstellen
Sobald ein Screenshot funktioniert, verschiebst du die Ziel-URL in ein Skript, das eine Liste verarbeiten kann. Das Beispiel verwendet weiterhin nur /tools, damit die Ausgabe übersichtlich bleibt. Später kannst du demselben Array weitere URLs hinzufügen.
Erstelle 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();
}
Damit bleibt ein Browser geöffnet, während für jede URL ein eigener Browserkontext entsteht. Puppeteers Dokumentation zu BrowserContext beschreibt diese Kontexte als isolierte Benutzersitzungen mit getrennten Cookies und Speichern. Das hilft, wenn eine Seite während der Aufnahme Cookies oder Daten im lokalen Speicher ablegt.
Die Schleife läuft bewusst sequenziell. Das ist langsamer, lässt sich zu Beginn aber einfacher debuggen.
Parallelität für größere Stapel ergänzen
Bei wenigen URLs reicht die sequenzielle Aufnahme aus. Für größere Listen solltest du eine niedrige Obergrenze für parallele Aufnahmen einführen.
Beginne mit zwei bis vier gleichzeitigen Aufnahmen. Auch ein Headless-Browser braucht Prozessorleistung und Arbeitsspeicher. Zu aggressive Stapel können außerdem Zugriffsbeschränkungen auslösen oder zu instabilen Seitenaufrufen führen.
Ergänze diese Hilfsfunktion:
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;
}
Ersetze anschließend die sequenzielle Schleife:
const results = await runWithConcurrency(urls, 3, (url, index) =>
captureUrl(browser, url, index),
);
So verarbeitet der Stapel mehr Seiten, ohne dass das Skript eine unbegrenzte Zahl von Seiten gleichzeitig öffnet.
Mobile Screenshots aufnehmen
Lege die Ansichtsgröße vor page.goto() fest, um einen Screenshot in Mobilgröße aufzunehmen:
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",
});
Das reicht, wenn du nur eine ungefähre mobile Ansichtsgröße brauchst. Hier siehst du dieselbe Werkzeugseite mit diesen Einstellungen:
Wenn du ein umfangreicheres Geräteprofil brauchst, stellt Puppeteer außerdem bekannte Geräte bereit:
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,
});
Geräteemulation ist sinnvoller, wenn Ansichtsgröße, User-Agent, Touch-Verhalten und Pixeldichte gemeinsam wichtig sind. Laut der Dokumentation zu page.emulate() setzt die Methode sowohl den User-Agent als auch die Ansichtsgröße. Rufe sie deshalb vor der Navigation auf.
Inhalte mit Lazy Loading berücksichtigen
Viele Seiten laden Bilder oder blenden Abschnitte erst ein, wenn sie in den sichtbaren Bereich gelangen. Nimmst du die gesamte Seite zu früh auf, können dort leere Flächen erscheinen, wo Inhalte unterhalb des sichtbaren Bereichs noch nicht gerendert wurden.
Scrolle vor einem Ganzseiten-Screenshot durch das Dokument, um verbreitete Lazy-Loading-Mechanismen auszulösen:
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);
});
}
Rufe die Funktion vor page.screenshot() auf:
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,
});
Manche Seiten brauchen mehr als das. Eigene Scroll-Container, Animationen, verzögerte API-Aufrufe und virtualisierte Inhalte können einen seitenspezifischen Selektor oder Zustand erfordern.
Die passende Wartestrategie wählen
Die meisten Zuverlässigkeitsprobleme bei Screenshots hängen mit dem Zeitpunkt der Aufnahme zusammen.
networkidle2 ist für viele Screenshot-Skripte ein vernünftiger Ausgangspunkt und wird auch in Puppeteers eigener Screenshot-Anleitung im Grundbeispiel verwendet. Es ist jedoch keine Garantie dafür, dass die Seite fertig ist. Manche Websites halten Hintergrundanfragen offen, andere rendern den Hauptinhalt erst nach Ende der Netzwerkaktivität.
Übliche Wartestrategien:
- Nutze
waitUntil: "networkidle2"für gewöhnliche öffentliche Seiten. - Nutze
page.waitForSelector(), wenn ein bestimmtes Element zeigt, dass die Seite bereit ist. - Nutze
page.waitForFunction(), wenn die Bereitschaft von einem App-Zustand abhängt. - Scrolle vor der Aufnahme, wenn Bilder erst bei Bedarf geladen werden.
- Verlasse dich nicht hauptsächlich auf feste Wartezeiten.
Beispiel:
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,
});
Wenn du die Ziel-Website selbst betreust, sind Selektor-Wartebedingungen normalerweise zuverlässiger als eine allgemeine Netzwerkbedingung.
Wiederholungsversuche und einen Bericht ergänzen
Echte Screenshot-Stapel schlagen gelegentlich aus ganz gewöhnlichen Gründen fehl: vorübergehende Netzwerkfehler, langsame Seiten, Weiterleitungen, Zugriffsbeschränkungen, Cookie-Banner und Skripte von Drittanbietern.
Ergänze Wiederholungsversuche für jede 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;
}
Umschließe damit die eigentliche Aufnahme:
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,
});
});
Schreibe für wiederkehrende Jobs einen JSON-Bericht mit URL, Ausgabepfad, Erfolgsstatus, Fehlermeldung und Zeitstempel. Der Screenshot zeigt die Seite, der Bericht erklärt den Lauf.
Eine praktische Ordnerstruktur
Trenne bei wiederkehrenden Jobs Eingaben, Ausgaben und Berichte:
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
So lässt sich der Ablauf leichter wiederholen und debuggen. Außerdem bleibt jeder Screenshot-Satz mit der URL-Liste verbunden, aus der er entstanden ist.
Häufige Fehler
- Aufnahme, bevor die Seite bereit ist: Ein abgeschlossenes
page.goto()bedeutet nicht immer, dass der wichtige Inhalt gerendert wurde. Warte auf einen Selektor oder App-Zustand. - Zu spät gesetzte Ansichtsgröße: Lege sie vor der Navigation fest, damit die Seite von Anfang an mit dem richtigen Layout lädt.
fullPagemit Lazy-Loading-Behandlung verwechseln: Die Ganzseiten-Option ändert den Aufnahmebereich. Sie garantiert nicht, dass jedes verzögert geladene Bild verfügbar ist.- Zu viele Seiten gleichzeitig öffnen: Beginne mit niedriger Parallelität. Browserautomatisierung kann deinen Rechner oder die Ziel-Website überfordern.
- Dateinamen wiederverwenden: URLs können sich nur durch einen abschließenden Schrägstrich, die Abfragezeichenfolge oder die Groß- und Kleinschreibung im Pfad unterscheiden. Ergänze einen Index und bereinige die Namen.
- Aufräumen überspringen: Schließe Kontexte und Browser. Bei länger laufenden Jobs führen offen gebliebene Seiten und Sitzungen schnell zu Problemen.
Alternativen zu einem Puppeteer-Screenshot-Skript
Puppeteer ist nur eine Möglichkeit, Website-Screenshots zu automatisieren.
Wenn der Job in Code bleibt, ist Playwright die naheliegendste Alternative. Die Umsetzung beschreiben wir in der Anleitung Website-Screenshots mit Playwright automatisieren. Playwright passt oft besser, wenn Screenshots Teil einer größeren Testsuite sind, du Chromium, Firefox und WebKit abdecken möchtest oder den Test-Runner sowie Traces und Berichte von Playwright Test für die Aufnahmen brauchst.
Wenn du Screenshot-Aufnahmen in ein Produkt einbaust, kann eine Screenshot-API weniger Arbeit verursachen, als Browser selbst zu verwalten. Sobald das Skript für andere zuverlässig laufen muss, liegen Browser-Binärdateien, CI-Arbeitsspeicher, Wiederholungen, Aufräumen und Anmeldestatus bei dir.
Wenn du dieses Skript nicht selbst schreiben und pflegen möchtest, lies die Anleitung Website-Screenshots ohne Code automatisieren. Oder nutze Website Capture in Shotomatic, wenn du URL-Listen, Optionen pro Seite, prüfbare Ergebnisse und Exporte ohne eigenes Puppeteer-Skript brauchst.
FAQ
Kann Puppeteer Website-Screenshots automatisieren?
Ja. Puppeteer kann einen Browser starten, eine URL öffnen, die Ansichtsgröße festlegen und mit page.screenshot() einen Screenshot speichern.
Kann Puppeteer Ganzseiten-Screenshots aufnehmen?
Ja. Übergib fullPage: true an page.screenshot(), um die gesamte scrollbare Seite statt nur der sichtbaren Ansichtsgröße aufzunehmen.
Kann ich mit Puppeteer mehrere URLs aufnehmen?
Ja. Lass einen Browser geöffnet, erstelle für jede URL eine Seite oder einen Browserkontext und begrenze bei größeren Stapeln die Parallelität.
Ist Puppeteer für Screenshots besser als Playwright?
Puppeteer ist für auf Chrome ausgerichtete Screenshot-Skripte oft einfacher. Playwright ist meist sinnvoller, wenn du Chromium, Firefox und WebKit abdecken oder die Aufnahmen in einen größeren Testablauf einbinden möchtest.
Wann sollte ich Shotomatic statt Puppeteer verwenden?
Nutze Shotomatic für einen No-Code-Ablauf mit URL-Listen, den jemand ohne Pflege eines JavaScript-Skripts ausführen, prüfen und exportieren soll.
Quellen
Die Beispiele oben wurden anhand der offiziellen Dokumentation geprüft:
- Puppeteer-Übersicht, Installation und Konfiguration
- Puppeteers Screenshot-Anleitung,
Page.screenshot()und ScreenshotOptions - Puppeteers
BrowserContext,page.setViewport(),KnownDevicesundpage.emulate() - Puppeteers
page.waitForSelector()undpage.waitForFunction() - Browser-Dokumentation und Bibliotheksdokumentation von Playwright
Ähnliche Artikel
Weitere ArtikelWebsite-Screenshots mit Playwright automatisieren
Automatisiere Website-Screenshots mit Playwright und JavaScript. Nimm einzelne Seiten, Ganzseiten, mobile Ansichten und URL-Stapel mit Wiederholungen auf.

Website-Screenshots ohne Code automatisieren
Finde den passenden No-Code-Weg für automatische Website-Screenshots: URL-Listen, Automatisierungsplattformen mit Screenshot-API, Monitoring oder Browser-Helfer.

Website-Screenshots auf dem Mac gesammelt erstellen
Füge eine URL-Liste ein, wähle Desktop, Tablet oder Mobil, erfasse Seiten parallel und exportiere die Ergebnisse für Audits und Berichte.

Vor dem Kauf eines digitalen Lehrbuchs fürs Offlinelernen prüfen
Prüfe vor dem Kauf Offlinegeräte, portable Dateien, Drucklimits, Zugriffsdauer, Notizenexport, Barrierefreiheit und Rückgaberegeln.

Website-Screenshots ohne Code automatisieren
Mit Website Capture nimmst du URL-Listen mit eigenen Einstellungen pro Seite auf, prüfst die Ergebnisse und exportierst sie – ohne ein Skript zu pflegen.