# Playwright Web Scraping: Stealth & Performance Guide **Meta-Description:** Playwright für Web Scraping 2026. Stealth-Techniken, Anti-Bot Detection, Browser Fingerprinting und Performance-Optimierung. **Keywords:** Playwright, Web Scraping, Stealth, Browser Automation, Anti-Bot, Headless Browser, Data Extraction --- ## Einführung Playwright ist das **moderne Standard-Tool für Web Scraping**. Mit WebSocket-basierter Kommunikation, Native Network Interception und Multi-Browser-Support bietet es alles für skalierbare Datenextraktion. --- ## Playwright vs Alternativen ``` ┌─────────────────────────────────────────────────────────────┐ │ SCRAPING TOOLS 2026 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ PLAYWRIGHT │ │ ├── WebSocket-First (schneller als HTTP) │ │ ├── Multi-Browser (Chrome, Firefox, WebKit) │ │ ├── Native Request Interception │ │ ├── Auto-Waiting │ │ └── Best for: Modern JS Sites, Stealth │ │ │ │ PUPPETEER │ │ ├── Chrome DevTools Protocol │ │ ├── Chrome/Firefox Support │ │ ├── Größere Community │ │ └── Best for: Chrome-specific Features │ │ │ │ CHEERIO │ │ ├── Kein Browser (nur HTML Parsing) │ │ ├── Extrem schnell │ │ ├── Niedrige Ressourcen │ │ └── Best for: Static Sites │ │ │ └─────────────────────────────────────────────────────────────┘ ``` --- ## Basic Setup ```bash npm install playwright npx playwright install # Browser installieren ``` ```typescript // scraper.ts import { chromium, Browser, Page } from 'playwright'; async function scrape() { const browser = await chromium.launch({ headless: true // 'new' ist jetzt default }); const context = await browser.newContext({ userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', viewport: { width: 1920, height: 1080 }, locale: 'de-DE', timezoneId: 'Europe/Berlin' }); const page = await context.newPage(); try { await page.goto('https://example.com', { waitUntil: 'networkidle', timeout: 30000 }); // Scraping Logic const data = await page.evaluate(() => { return { title: document.title, headings: Array.from(document.querySelectorAll('h1, h2')) .map(h => h.textContent) }; }); return data; } finally { await browser.close(); } } ``` --- ## Stealth Mode ```typescript // playwright-extra für Stealth Plugins import { chromium } from 'playwright-extra'; import stealth from 'puppeteer-extra-plugin-stealth'; chromium.use(stealth()); async function stealthScrape(url: string) { const browser = await chromium.launch({ headless: true }); const context = await browser.newContext({ // Realistische Browser-Einstellungen userAgent: getRandomUserAgent(), viewport: getRandomViewport(), locale: 'de-DE', timezoneId: 'Europe/Berlin', geolocation: { latitude: 52.52, longitude: 13.405 }, permissions: ['geolocation'], // WebGL Fingerprint deviceScaleFactor: 1, hasTouch: false, isMobile: false }); // WebDriver Flag entfernen await context.addInitScript(() => { Object.defineProperty(navigator, 'webdriver', { get: () => undefined }); // Chrome-spezifische Properties Object.defineProperty(navigator, 'plugins', { get: () => [1, 2, 3, 4, 5] }); Object.defineProperty(navigator, 'languages', { get: () => ['de-DE', 'de', 'en-US', 'en'] }); // Automation Detection Override delete (window as any).cdc_adoQpoasnfa76pfcZLmcfl_Array; delete (window as any).cdc_adoQpoasnfa76pfcZLmcfl_Promise; delete (window as any).cdc_adoQpoasnfa76pfcZLmcfl_Symbol; }); const page = await context.newPage(); await page.goto(url); return { page, browser, context }; } // Random User Agents function getRandomUserAgent(): string { const userAgents = [ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0' ]; return userAgents[Math.floor(Math.random() * userAgents.length)]; } function getRandomViewport() { const viewports = [ { width: 1920, height: 1080 }, { width: 1366, height: 768 }, { width: 1536, height: 864 }, { width: 1440, height: 900 } ]; return viewports[Math.floor(Math.random() * viewports.length)]; } ``` --- ## Network Interception ```typescript // Ressourcen blockieren für Speed async function fastScrape(url: string) { const browser = await chromium.launch(); const context = await browser.newContext(); const page = await context.newPage(); // Unnötige Ressourcen blockieren await page.route('**/*', (route) => { const resourceType = route.request().resourceType(); if (['image', 'stylesheet', 'font', 'media'].includes(resourceType)) { return route.abort(); } // Tracking Scripts blockieren const url = route.request().url(); if ( url.includes('analytics') || url.includes('tracking') || url.includes('ads') ) { return route.abort(); } return route.continue(); }); await page.goto(url, { waitUntil: 'domcontentloaded' }); return page; } // API Responses abfangen async function interceptApi(page: Page) { const apiResponses: any[] = []; page.on('response', async (response) => { const url = response.url(); if (url.includes('/api/') && response.ok()) { try { const json = await response.json(); apiResponses.push({ url, data: json }); } catch {} } }); return apiResponses; } // Request modifizieren await page.route('**/api/**', (route) => { const headers = { ...route.request().headers(), 'Authorization': 'Bearer token123', 'X-Custom-Header': 'value' }; route.continue({ headers }); }); ``` --- ## Selektoren & Datenextraktion ```typescript // Moderne Selektoren async function extractData(page: Page) { // CSS Selektoren const title = await page.locator('h1').textContent(); // Text-basierte Selektoren const loginButton = page.getByRole('button', { name: 'Login' }); const emailInput = page.getByLabel('E-Mail'); const link = page.getByText('Mehr erfahren'); // Multiple Elemente const prices = await page.locator('.price').allTextContents(); // Attribute extrahieren const links = await page.locator('a').evaluateAll( (elements) => elements.map(el => ({ href: el.getAttribute('href'), text: el.textContent?.trim() })) ); // Tabellen scrapen const tableData = await page.evaluate(() => { const rows = document.querySelectorAll('table tbody tr'); return Array.from(rows).map(row => { const cells = row.querySelectorAll('td'); return Array.from(cells).map(cell => cell.textContent?.trim()); }); }); // Strukturierte Daten (JSON-LD) const jsonLd = await page.evaluate(() => { const script = document.querySelector('script[type="application/ld+json"]'); return script ? JSON.parse(script.textContent || '{}') : null; }); return { title, prices, links, tableData, jsonLd }; } // Warten auf dynamischen Content async function waitForContent(page: Page) { // Auf Element warten await page.waitForSelector('.product-list', { state: 'visible' }); // Auf Network Idle await page.waitForLoadState('networkidle'); // Auf bestimmte Anzahl Elemente await page.locator('.product-card').first().waitFor(); // Custom Condition await page.waitForFunction(() => { return document.querySelectorAll('.product-card').length >= 10; }); } ``` --- ## Pagination & Infinite Scroll ```typescript // Pagination async function scrapePaginated(baseUrl: string, maxPages: number = 10) { const browser = await chromium.launch(); const page = await browser.newPage(); const allData: any[] = []; for (let i = 1; i <= maxPages; i++) { await page.goto(`${baseUrl}?page=${i}`); const pageData = await page.evaluate(() => { return Array.from(document.querySelectorAll('.item')).map(el => ({ title: el.querySelector('.title')?.textContent, price: el.querySelector('.price')?.textContent })); }); if (pageData.length === 0) break; // Keine Daten mehr allData.push(...pageData); // Rate Limiting await page.waitForTimeout(1000 + Math.random() * 2000); } await browser.close(); return allData; } // Infinite Scroll async function scrapeInfiniteScroll(url: string, maxScrolls: number = 20) { const browser = await chromium.launch(); const page = await browser.newPage(); await page.goto(url); let previousHeight = 0; let scrollCount = 0; while (scrollCount < maxScrolls) { // Scroll to bottom await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight)); // Auf neue Inhalte warten await page.waitForTimeout(2000); const currentHeight = await page.evaluate(() => document.body.scrollHeight); if (currentHeight === previousHeight) { break; // Keine neuen Inhalte } previousHeight = currentHeight; scrollCount++; } // Alle Daten extrahieren const data = await page.evaluate(() => { return Array.from(document.querySelectorAll('.item')).map(/* ... */); }); await browser.close(); return data; } ``` --- ## Proxy & Session Management ```typescript // Proxy Setup const browser = await chromium.launch({ proxy: { server: 'http://proxy.example.com:8080', username: 'user', password: 'pass' } }); // Rotating Proxies async function withRotatingProxy(urls: string[]) { const proxies = [ 'http://proxy1.example.com:8080', 'http://proxy2.example.com:8080', 'http://proxy3.example.com:8080' ]; for (const url of urls) { const proxy = proxies[Math.floor(Math.random() * proxies.length)]; const browser = await chromium.launch({ proxy: { server: proxy } }); try { const page = await browser.newPage(); await page.goto(url); // Scrape... } finally { await browser.close(); } } } // Session/Cookie Persistence async function persistSession() { // Session speichern const context = await browser.newContext(); const page = await context.newPage(); await page.goto('https://example.com/login'); // Login durchführen... // Cookies speichern const cookies = await context.cookies(); await fs.writeFile('cookies.json', JSON.stringify(cookies)); // Session wiederherstellen const newContext = await browser.newContext(); const savedCookies = JSON.parse(await fs.readFile('cookies.json', 'utf8')); await newContext.addCookies(savedCookies); } ``` --- ## Parallel Scraping ```typescript import { chromium, Browser } from 'playwright'; import pLimit from 'p-limit'; async function parallelScrape(urls: string[], concurrency: number = 5) { const browser = await chromium.launch(); const limit = pLimit(concurrency); const results = await Promise.all( urls.map(url => limit(async () => { const context = await browser.newContext(); const page = await context.newPage(); try { await page.goto(url, { timeout: 30000 }); const data = await extractData(page); return { url, data, success: true }; } catch (error) { return { url, error: (error as Error).message, success: false }; } finally { await context.close(); } }) ) ); await browser.close(); return results; } ``` --- ## Fazit Playwright Web Scraping 2026: 1. **Stealth First**: Anti-Detection von Anfang an 2. **Performance**: Resource Blocking, Parallel Scraping 3. **Robustheit**: Auto-Waiting, Retries, Error Handling 4. **Compliance**: Robots.txt respektieren, Rate Limiting Immer rechtliche Aspekte und Terms of Service beachten. --- ## Bildprompts 1. "Spider crawling through web pages extracting data, web scraping concept" 2. "Browser automation with invisible robot, stealth scraping" 3. "Multiple parallel processes scraping different websites, concurrent extraction" --- ## Quellen - [Playwright Documentation](https://playwright.dev/) - [Playwright Web Scraping 2026](https://brightdata.com/blog/how-tos/playwright-web-scraping) - [Playwright Stealth](https://www.zenrows.com/blog/playwright-stealth) - [Browserless Scraping Guide](https://www.browserless.io/blog/scraping-with-playwright-a-developer-s-guide-to-scalable-undetectable-data-extraction)