Das programmgesteuerte Erfassen von Screenshots von Webseiten kann unglaublich nützlich sein, um Vorschauen zu erstellen, bildbasierte Berichte zu erstellen und mehr. In dieser Anleitung erstellen wir eine Next.js-API-Route, die eine URL verwendet und einen PNG-Screenshot generiert. Unser Setup nutzt Puppeteer und chrome-aws-lambda, um einen Headless-Chrome-Browser zu nutzen und ihn so vielseitig und produktionsbereit zu machen.
Wir beginnen mit der Einrichtung eines neuen Next.js-Projekts und gehen den Code Schritt für Schritt durch, um zu verstehen, wie die API Screenshots erfasst.
Voraussetzungen
Erste Schritte mit einem neuen Next.js-Projekt
npx create-next-app@latest capture-image-app cd capture-image-app
npm install puppeteer puppeteer-core chrome-aws-lambda busboy
Schritt 2: Erstellen Sie die API-Route zum Generieren von Screenshots
Jetzt richten wir einen API-Endpunkt ein, um Screenshots basierend auf einer bereitgestellten URL zu erfassen und zurückzugeben.
Erstellen Sie im Ordner „pages/api“ eine neue Datei mit dem Namen „generate-png.ts“ und fügen Sie diesen Code hinzu:
import { NextApiRequest, NextApiResponse } from "next"; import busboy, { Busboy } from "busboy"; // Use busboy for multipart parsing import chromium from "chrome-aws-lambda"; import puppeteerCore from "puppeteer-core"; // Import puppeteer-core directly import puppeteer from "puppeteer"; // Import puppeteer directly // Conditional import for Puppeteer based on the environment const puppeteerModule = process.env.NODE_ENV === "production" ? puppeteerCore : puppeteer; export const config = { api: { bodyParser: false, // Disable default body parsing to handle raw binary data (Blob) }, }; const delay = (ms: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, ms)); export default async function handler( req: NextApiRequest, res: NextApiResponse ): Promise<void> { try { if (req.method === "POST") { const bb: Busboy = busboy({ headers: req.headers }); let width: number = 1920; // Default width let height: number = 0; // Default height let delayTime: number = 6000; const buffers: Buffer[] = []; bb.on("file", (_name: string, file: NodeJS.ReadableStream) => { file.on("data", (data: Buffer) => buffers.push(data)); }); bb.on("field", (name: string, value: string) => { if (name === "width") width = parseInt(value, 10) || 1920; if (name === "height") height = parseInt(value, 10) || 0; if (name === "delay") delayTime = parseInt(value, 10) || 6000; }); bb.on("finish", async () => { const blobBuffer: Buffer = Buffer.concat(buffers); const htmlContent: string = blobBuffer.toString("utf-8"); const browser = await puppeteerModule.launch({ args: ["--start-maximized"], executablePath: process.env.NODE_ENV === "production" ? await chromium.executablePath || "/usr/bin/chromium-browser" : undefined, // No custom executable path needed for local headless: true, }); const page = await browser.newPage(); // Load the HTML content directly await page.setContent(htmlContent, { waitUntil: "networkidle0" }); //@ts-expect-error todo const bodyHeight = await page.evaluate(() => { return document.body.scrollHeight; // Get the full scrollable height of the body }); await page.setViewport({ width: Number(width), height: height || bodyHeight, // Use the provided height or fallback to the full body height deviceScaleFactor: 2, }); await delay(delayTime); const screenshotBuffer = await page.screenshot({ fullPage: !height, type: "png", omitBackground: false, }); await browser.close(); res.setHeader("Content-Type", "image/png"); res.setHeader( "Content-Disposition", "attachment; filename=screenshot.png" ); res.status(200).end(screenshotBuffer); }); req.pipe(bb); // Pipe the request stream to busboy } else { res.setHeader("Allow", ["POST"]); res.status(405).end(`Method ${req.method} Not Allowed`); } } catch (error) { console.error("ERROR", error); res.status(500).end("Internal Server Error"); } }
*Erläuterung: Auswahl von Puppeteer für lokale oder Produktionsumgebungen
*
In diesem Code haben wir einen dynamischen Import für Puppenspieler eingerichtet:
Lokale Entwicklung: Wenn NODE_ENV keine Produktion ist, wird Puppeteer verwendet, was einfacher einzurichten ist und kein Chrome-AWS-Lambda erfordert.
Produktion: Bei serverlosen Bereitstellungen erkennt die Umgebung NODE_ENV als Produktion und lädt puppeteer-core zusammen mit chrome-aws-lambda, was die Arbeit in AWS Lambda und anderen ähnlichen Umgebungen ermöglicht. In diesem Setup stellt chrome-aws-lambda den richtigen Chromium-Pfad bereit und gewährleistet so die Kompatibilität mit serverlosen Anbietern.
Schritt 3: Erstellen Sie eine einfache Reaktionskomponente für die Benutzeroberfläche
Hier erstellen wir ein einfaches Formular, mit dem Benutzer Werte für die Webseitenerfassung eingeben können. Dieses Formular löst die Generierungsfunktion aus, um den Screenshot im PDF-Format zu erfassen und herunterzuladen.
import { useState } from "react"; export default function ScreenCaptureComponent() { const [isProcessing, setProcessing] = useState(false); const [width, setWidth] = useState<string>("1920"); const [height, setHeight] = useState<string>("1000"); const [delay, setDelay] = useState<string>("6000"); // Function to clone HTML and prepare for capture function takeScreenshot() { const clonedElement = document.body.cloneNode(true) as HTMLElement; const blob = new Blob([clonedElement.outerHTML], { type: "text/html" }); return blob; } // Function to capture screenshot by sending cloned HTML to API async function generateCapture() { setProcessing(true); const htmlBlob = takeScreenshot(); if (!htmlBlob) { setProcessing(false); return; } try { const formData = new FormData(); formData.append("file", htmlBlob); formData.append("width", width); formData.append("height", height); formData.append("delay", delay); const response = await fetch("/api/generate-png", { method: "POST", body: formData, }); if (!response.ok) throw new Error("Capture failed"); const blob = await response.blob(); const downloadUrl = URL.createObjectURL(blob); const link = document.createElement("a"); link.href = downloadUrl; link.download = "capture.png"; link.click(); URL.revokeObjectURL(downloadUrl); } catch (error) { console.error("Failed to capture screenshot", error); } finally { setProcessing(false); } } return ( <div style={{ maxWidth: "400px", margin: "50px auto", padding: "24px", backgroundColor: "white", borderRadius: "8px", width: "100%", boxShadow: "0 4px 6px rgba(0, 0, 0, 0.1)", }} > <h2 style={{ fontSize: "24px", fontWeight: "600", textAlign: "center", marginBottom: "16px", }} > Webpage Screenshot Capture </h2> <form onSubmit={(e) => { e.preventDefault(); generateCapture(); }} style={{ display: "flex", flexDirection: "column", alignItems: "center", marginBottom: "16px", }} > <label style={{ marginBottom: "8px", fontWeight: "500" }} htmlFor="width" > Width (px) </label> <select id="width" value={width} onChange={(e) => setWidth(e.target.value)} style={{ width: "100%", padding: "8px", marginBottom: "16px", borderRadius: "4px", border: "1px solid #ccc", outline: "none", }} > <option value="1920">1920 (Full HD)</option> <option value="1366">1366 (Laptop)</option> <option value="1280">1280 (Desktop)</option> <option value="1024">1024 (Tablet Landscape)</option> <option value="768">768 (Tablet Portrait)</option> <option value="375">375 (Mobile)</option> </select> <label style={{ marginBottom: "8px", fontWeight: "500" }} htmlFor="height" > Height (px) </label> <input type="number" id="height" value={height} onChange={(e) => setHeight(e.target.value)} required style={{ width: "100%", padding: "8px", marginBottom: "16px", borderRadius: "4px", border: "1px solid #ccc", outline: "none", }} /> <label style={{ marginBottom: "8px", fontWeight: "500" }} htmlFor="delay" > Delay (ms) </label> <input type="number" id="delay" value={delay} onChange={(e) => setDelay(e.target.value)} required style={{ width: "100%", padding: "8px", marginBottom: "16px", borderRadius: "4px", border: "1px solid #ccc", outline: "none", }} /> <button type="submit" disabled={isProcessing} style={{ padding: "8px 16px", color: "white", borderRadius: "4px", transition: "background-color 0.3s", backgroundColor: isProcessing ? "#b0bec5" : "#2196F3", cursor: isProcessing ? "not-allowed" : "pointer", }} > {isProcessing ? "Capturing..." : "Capture Screenshot"} </button> </form> {/* Example HTML Element to Capture */} <div id="capture-area" style={{ display: "none" }}> <h3 style={{ fontSize: "20px", fontWeight: "600", }} > Content to Capture </h3> <p>This is an example of the HTML content that will be captured.</p> </div> </div> ); }
Fazit
Dieses Tutorial behandelt die Einrichtung eines Webseiten-Erfassungstools in Next.js, die Handhabung von Screenshots mit Puppeteer und die Erstellung einer interaktiven Frontend-Komponente. Denken Sie daran, Puppeteer lokal zu verwenden und in der Produktion auf Puppeteer-Core umzusteigen, um die Bundle-Größe zu reduzieren und für serverlose Umgebungen zu optimieren. Viel Spaß beim Codieren!
Das obige ist der detaillierte Inhalt vonSo erfassen Sie Webseiten-Screenshots mit Next.js und Puppeteer. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!