# Cheerio: Schnelles HTML Parsing ohne Browser **Meta-Description:** Cheerio für effizientes Web Scraping. jQuery-Syntax für Server-Side HTML Parsing ohne Browser-Overhead. **Keywords:** Cheerio, HTML Parsing, Web Scraping, jQuery, Node.js, DOM Manipulation, Data Extraction --- ## Einführung Cheerio ist eine **ultraschnelle HTML Parsing Library** für Node.js. Mit jQuery-ähnlicher Syntax parst es HTML serverseitig – ohne Browser, ohne JavaScript-Rendering, nur pure Geschwindigkeit. --- ## Cheerio vs Browser-basierte Tools ``` ┌─────────────────────────────────────────────────────────────┐ │ CHEERIO CHARAKTERISTIKEN │ ├─────────────────────────────────────────────────────────────┤ │ │ │ Vorteile: │ │ ├── Extrem schnell (kein Browser-Overhead) │ │ ├── Geringer Memory-Footprint │ │ ├── Vertraute jQuery-Syntax │ │ ├── Serverless-freundlich │ │ └── Ideal für Static HTML │ │ │ │ Einschränkungen: │ │ ├── Kein JavaScript-Rendering │ │ ├── Keine dynamischen Inhalte │ │ ├── Keine Browser-APIs │ │ └── Kein Visual Rendering │ │ │ │ Use Cases: │ │ ├── Static Website Scraping │ │ ├── HTML Email Parsing │ │ ├── RSS/XML Feed Processing │ │ ├── HTML Sanitization │ │ └── Content Extraction │ │ │ └─────────────────────────────────────────────────────────────┘ ``` --- ## Setup ```bash npm install cheerio axios ``` ```typescript // scraper.ts import * as cheerio from 'cheerio'; import axios from 'axios'; async function scrape(url: string) { // HTML fetchen const response = await axios.get(url, { headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' } }); // Cheerio laden const $ = cheerio.load(response.data); // jQuery-Syntax für Selektion const title = $('h1').text(); const description = $('meta[name="description"]').attr('content'); return { title, description }; } ``` --- ## Selektoren & Navigation ```typescript import * as cheerio from 'cheerio'; const html = `

Title

Introduction text

Link 1 Link 2
`; const $ = cheerio.load(html); // Basis Selektoren const title = $('h1').text(); // "Title" const intro = $('.intro').text(); // "Introduction text" const firstItem = $('#items li').first().text(); // "Item 1" // Attribute const href = $('a').attr('href'); // "/page1" const dataId = $('li').first().data('id'); // 1 // Traversierung const items = $('li').map((i, el) => $(el).text()).get(); // ["Item 1", "Item 2", "Item 3"] // Parent/Child Navigation const parent = $('li').first().parent().attr('id'); // "items" const children = $('#items').children().length; // 3 const siblings = $('li').first().siblings().length; // 2 // Filtering const externalLinks = $('a.external').map((i, el) => $(el).attr('href')).get(); // Contains const itemWith2 = $('li:contains("2")').text(); // "Item 2" // Multiple Selectors const headings = $('h1, h2, h3').map((i, el) => $(el).text()).get(); ``` --- ## Datenextraktion ```typescript import * as cheerio from 'cheerio'; import axios from 'axios'; interface Product { name: string; price: number; description: string; image: string; url: string; } async function scrapeProducts(url: string): Promise { const { data: html } = await axios.get(url); const $ = cheerio.load(html); const products: Product[] = []; $('.product-card').each((index, element) => { const $el = $(element); products.push({ name: $el.find('.product-name').text().trim(), price: parseFloat( $el.find('.price') .text() .replace('€', '') .replace(',', '.') .trim() ), description: $el.find('.description').text().trim(), image: $el.find('img').attr('src') || '', url: $el.find('a.product-link').attr('href') || '' }); }); return products; } // Tabellen extrahieren function extractTable($: cheerio.CheerioAPI, selector: string) { const headers: string[] = []; const rows: Record[] = []; // Header extrahieren $(`${selector} thead th`).each((i, el) => { headers.push($(el).text().trim()); }); // Rows extrahieren $(`${selector} tbody tr`).each((i, row) => { const rowData: Record = {}; $(row).find('td').each((j, cell) => { const header = headers[j] || `col${j}`; rowData[header] = $(cell).text().trim(); }); rows.push(rowData); }); return rows; } // Strukturierte Daten (JSON-LD) function extractJsonLd($: cheerio.CheerioAPI) { const scripts = $('script[type="application/ld+json"]'); const data: any[] = []; scripts.each((i, el) => { try { const content = $(el).html(); if (content) { data.push(JSON.parse(content)); } } catch {} }); return data; } ``` --- ## HTML Manipulation ```typescript import * as cheerio from 'cheerio'; const $ = cheerio.load('

Hello

'); // Text ändern $('p').text('Hello World'); // HTML ändern $('div').html('New Content'); // Elemente hinzufügen $('div').append('

Appended

'); $('div').prepend('

Prepended

'); $('p').after('After'); $('p').before('Before'); // Attribute setzen $('div').attr('id', 'container'); $('div').addClass('active'); $('div').removeClass('hidden'); // Elemente entfernen $('.ads').remove(); $('.tracking').empty(); // Wrap $('p').wrap('
'); // Clone const cloned = $('p').clone(); $('div').append(cloned); // Output const finalHtml = $.html(); const bodyOnly = $('body').html(); ``` --- ## HTML Sanitization ```typescript import * as cheerio from 'cheerio'; function sanitizeHtml(dirtyHtml: string): string { const $ = cheerio.load(dirtyHtml); // Scripts entfernen $('script').remove(); // Event Handler entfernen $('*').each((i, el) => { const element = $(el); const attrs = (el as any).attribs || {}; Object.keys(attrs).forEach(attr => { if (attr.startsWith('on')) { element.removeAttr(attr); } }); }); // Gefährliche Attribute entfernen $('a[href^="javascript:"]').removeAttr('href'); $('*[style*="expression"]').removeAttr('style'); // Iframe entfernen $('iframe').remove(); // Erlaubte Tags whitelist const allowedTags = ['p', 'a', 'b', 'i', 'u', 'ul', 'ol', 'li', 'br', 'h1', 'h2', 'h3']; $('*').each((i, el) => { const tagName = (el as any).tagName?.toLowerCase(); if (tagName && !allowedTags.includes(tagName) && tagName !== 'html' && tagName !== 'body') { $(el).replaceWith($(el).html() || ''); } }); return $('body').html() || ''; } // Email-safe HTML function makeEmailSafe(html: string): string { const $ = cheerio.load(html); // Relative URLs zu Absoluten $('img[src^="/"]').each((i, el) => { const src = $(el).attr('src'); $(el).attr('src', `https://example.com${src}`); }); // CSS Inlinen (vereinfacht) $('*[class]').each((i, el) => { const element = $(el); const classes = element.attr('class'); // Hier würde man CSS Styles inlinen element.removeAttr('class'); }); return $.html(); } ``` --- ## Pagination & Crawling ```typescript import * as cheerio from 'cheerio'; import axios from 'axios'; interface CrawlResult { url: string; title: string; links: string[]; } async function crawlWithPagination(startUrl: string, maxPages: number = 10) { const visited = new Set(); const results: CrawlResult[] = []; let currentUrl: string | null = startUrl; let pageCount = 0; while (currentUrl && pageCount < maxPages) { if (visited.has(currentUrl)) break; visited.add(currentUrl); try { const { data: html } = await axios.get(currentUrl, { headers: { 'User-Agent': 'Mozilla/5.0' }, timeout: 10000 }); const $ = cheerio.load(html); results.push({ url: currentUrl, title: $('title').text(), links: $('a[href]') .map((i, el) => $(el).attr('href')) .get() .filter(href => href && !href.startsWith('#')) }); // Nächste Seite finden const nextLink = $('a.next, a[rel="next"], .pagination a:last-child') .attr('href'); currentUrl = nextLink ? new URL(nextLink, currentUrl).toString() : null; pageCount++; // Rate Limiting await new Promise(r => setTimeout(r, 1000)); } catch (error) { console.error(`Error crawling ${currentUrl}:`, error); break; } } return results; } // Sitemap Parsing async function parseSitemap(sitemapUrl: string): Promise { const { data: xml } = await axios.get(sitemapUrl); const $ = cheerio.load(xml, { xmlMode: true }); const urls: string[] = []; // Standard Sitemap $('url loc').each((i, el) => { urls.push($(el).text()); }); // Sitemap Index $('sitemap loc').each((i, el) => { urls.push($(el).text()); }); return urls; } ``` --- ## RSS/Atom Feed Parsing ```typescript import * as cheerio from 'cheerio'; import axios from 'axios'; interface FeedItem { title: string; link: string; description: string; pubDate: Date; author?: string; } async function parseRssFeed(feedUrl: string): Promise { const { data: xml } = await axios.get(feedUrl); const $ = cheerio.load(xml, { xmlMode: true }); const items: FeedItem[] = []; // RSS 2.0 $('item').each((i, el) => { const $item = $(el); items.push({ title: $item.find('title').text(), link: $item.find('link').text(), description: $item.find('description').text(), pubDate: new Date($item.find('pubDate').text()), author: $item.find('author, dc\\:creator').text() || undefined }); }); // Atom $('entry').each((i, el) => { const $entry = $(el); items.push({ title: $entry.find('title').text(), link: $entry.find('link').attr('href') || '', description: $entry.find('summary, content').text(), pubDate: new Date($entry.find('updated, published').text()), author: $entry.find('author name').text() || undefined }); }); return items; } ``` --- ## Performance-Optimierung ```typescript import * as cheerio from 'cheerio'; // Lazy Loading für große Dokumente function processLargeHtml(html: string) { const $ = cheerio.load(html, { // Nur parsen was nötig ist xml: false, decodeEntities: true }); // Selektiv arbeiten const relevantSection = $('#main-content').html(); if (relevantSection) { const $section = cheerio.load(relevantSection); // Weiterverarbeiten... } } // Batch Processing async function batchScrape(urls: string[], concurrency: number = 5) { const results: any[] = []; for (let i = 0; i < urls.length; i += concurrency) { const batch = urls.slice(i, i + concurrency); const batchResults = await Promise.all( batch.map(async (url) => { const { data } = await axios.get(url); const $ = cheerio.load(data); return { url, title: $('title').text() }; }) ); results.push(...batchResults); // Rate Limiting zwischen Batches await new Promise(r => setTimeout(r, 1000)); } return results; } ``` --- ## Fazit Cheerio ist ideal für: 1. **Static Content**: HTML ohne JavaScript-Rendering 2. **Speed**: Kein Browser-Overhead 3. **Serverless**: Geringer Memory-Footprint 4. **Vertrautheit**: jQuery-Syntax Für dynamische Seiten kombiniere mit Playwright/Puppeteer. --- ## Bildprompts 1. "HTML document being parsed into structured data, document processing" 2. "jQuery selector finding elements in page structure, DOM navigation" 3. "Fast processing of multiple HTML pages, batch extraction" --- ## Quellen - [Cheerio Documentation](https://cheerio.js.org/) - [Cheerio npm](https://www.npmjs.com/package/cheerio) - [Web Scraping with Cheerio 2026](https://www.capsolver.com/blog/The%20other%20captcha/web-scraping-with-cheerio) - [Cheerio Tutorial](https://zetcode.com/javascript/cheerio/)