Crawler Interno con Crawlee + Playwright
Il sistema di crawling è stato completamente rinnovato con Crawlee e Playwright, sostituendo il precedente sistema basato su Crawl4AI. Questa implementazione offre performance superiori, maggiore affidabilità e funzionalità avanzate per il crawling di siti web moderni.
🎯 Caratteristiche Principali
✅ Sitemap Ricorsive
- Gestione automatica di sitemap nidificate multi-livello
- Parsing intelligente di sitemap index e urlset
- Protezione anti-loop per evitare crawling infiniti
- Estrazione di metadati (lastmod, changefreq, priority)
✅ Batching Automatico
- Divisione intelligente degli URL in batch da 10
- Esecuzione parallela dei batch tramite Hatchet
- Gestione ottimizzata della memoria e delle risorse
- Tracking del progresso per ogni batch
✅ Pipeline HTML → Markdown
- Conversione avanzata con framework
unified - Rimozione automatica di elementi non utili (script, style, nav, footer)
- Estrazione selettiva del contenuto principale
- Normalizzazione degli heading e semplificazione delle tabelle
✅ Supporto SPA e Framework
- Rilevamento automatico di React, Vue, Angular, Next.js
- Gestione intelligente del contenuto JavaScript
- Attesa dinamica del caricamento del contenuto
- Hook personalizzati per framework specifici
🏗️ Architettura del Sistema
Componenti Principali
File Struttura
hatchet/
├── src/
│ ├── services/
│ │ └── crawleeService.ts # Servizio principale crawler
│ ├── utilities/
│ │ ├── htmlToMarkdown.ts # Pipeline conversione HTML
│ │ ├── detectors.ts # Rilevamento framework
│ │ ├── extractContent.ts # Estrazione contenuto
│ │ └── spaHooks.ts # Hook per SPA
│ └── types/
│ └── crawl4aiTypes.ts # Tipi unificati
├── workflows/
│ └── crawling.ts # Workflow Hatchet distribuito
└── tests/
└── crawler-test.ts # Suite di test
🚀 Utilizzo
1. Crawling Sincrono (URL singoli/piccoli set)
import { resourceEmbeddingWorkflow } from './workflows/embetting'
await resourceEmbeddingWorkflow.runNoWait({
resourceUuid: 'uuid-123',
collectionName: 'my-collection',
resourceType: 'url',
urls: ['https://example.com'],
urlType: 'single',
userId: 1
})
2. Crawling Distribuito (Sitemap grandi)
import { sitemapCrawlWorkflow } from './workflows/crawling'
await sitemapCrawlWorkflow.runNoWait({
sitemapUrl: 'https://example.com/sitemap.xml',
resourceUuid: 'uuid-123',
collectionName: 'my-collection',
userId: 1
})
3. Conversione HTML → Markdown Standalone
import { htmlToMarkdown } from '#utilities/htmlToMarkdown'
const markdown = await htmlToMarkdown(htmlString, {
removeElements: ['script', 'style', 'nav', 'footer'],
contentSelectors: ['article', 'main'],
normalizeHeadings: true,
simplifyTables: true,
removeEmptyLists: true
})
⚙️ Configurazione
Variabili Ambiente
# Abilita/disabilita Crawlee (default: true)
USE_CRAWLEE=true
# Configurazione MinIO
MINIO_ENDPOINT=http://minio:9000
MINIO_ACCESS_KEY=minioadmin
MINIO_SECRET_KEY=minioadmin
MINIO_BUCKET_NAME=crawler-results
Parametri Tuning
CrawleeService
private maxRequestRetries = 5 // Retry per URL
private requestTimeout = 120000 // 120 sec per pagina
private maxConcurrency = 5 // URL concorrenti
Workflow Crawling
const BATCH_SIZE = 10 // URL per batch
Pipeline HTML → Markdown
// Personalizza selettori di contenuto
contentSelectors: ['article', 'main', '[role="main"]', '.content']
// Personalizza elementi da rimuovere
removeElements: ['script', 'style', 'nav', 'footer', 'aside', 'form']
🔄 Flusso di Lavoro
Sitemap Ricorsiva
- Ricezione sitemap URL
- Parse XML sitemap
- Identificazione tipo entry:
<sitemap>: Ricorsione → torna al step 2<url>: Aggiungi a lista finale
- Ritorno lista completa URL
Crawling Distribuito
- sitemapCrawlWorkflow riceve sitemap URL
- prepareCrawlTask:
- Parse sitemap ricorsivamente → ottiene tutti URL
- Divide in batch da 10
- Spawna N crawlBatchTask
- crawlBatchTask (eseguito in parallelo):
- Crawla 10 URL con Playwright
- Converte HTML → Markdown
- Salva su MinIO
- Ritorna LoaderResult[]
- I risultati vengono processati dal workflow embedding
Pipeline HTML → Markdown
HTML input
↓
rehype-parse → AST HTML
↓
rehype-remove → Rimuove script, style, nav, footer, aside, form
↓
rehype-extract → Estrae article, main, section
↓
rehype-remark → AST Markdown
↓
remark-normalize-headings → Normalizza h1, h2, h3...
↓
remark-simplify-tables → Tabelle → testo semplice
↓
remark-remove-empty → Rimuove liste vuote
↓
remark-stringify → Markdown string
↓
Clean output (max 2 newlines consecutive, trim)
📊 Performance e Confronti
Crawl4AI vs Crawlee
| Feature | Crawl4AI | Crawlee |
|---|---|---|
| Sitemap ricorsiva | ✅ (singolo livello) | ✅ (multi-livello) |
| Batching | ❌ | ✅ (10 URL/batch) |
| HTML → MD | Raw markdown | Pipeline unified |
| Browser | Playwright/Chromium | Playwright |
| Rate limiting | Manuale | Integrato |
| Retry logic | Manuale | Integrato |
| Performance | Buona | Ottima (parallelo) |
| Memory usage | Alta | Ottimizzata |
Stima Tempi
- Single URL: ~5-10 secondi
- 10 URLs (batch): ~10-20 secondi (parallelo)
- 100 URLs: ~100-200 secondi (10 batch in parallelo)
- 1000 URLs: ~1000-2000 secondi (~16-33 minuti, 100 batch)
🧪 Testing
Test Suite Completa
# Esegui test suite
pnpm run test:crawler
Test Implementati
- ✅ HTML to Markdown - Verifica conversione e pulizia
- ✅ Single URL Crawl - Test crawling singolo URL
- ✅ Sitemap Parsing - Test parser sitemap XML
- ✅ Nested Sitemap - Test sitemap index
Risultati Attesi
🚀 Starting Crawler Tests
============================================================
🧪 Test 1: HTML to Markdown conversion
✅ Markdown Output:
✓ Has title heading: true
✓ Has features section: true
✓ Scripts removed: true
✓ Navigation removed: true
🧪 Test 2: Single URL crawling
✅ Crawl completed
- Success: true
- Results count: 1
- Title: Example Domain
- Markdown length: 532 chars
🧪 Test 3: Sitemap parsing (mock)
✅ Parsed 3 URLs from sitemap
🧪 Test 4: Nested sitemap parsing (mock)
✅ Detected sitemap index: true
Found 2 nested sitemaps
============================================================
📊 Test Results Summary:
HTML to Markdown: ✅ PASS
Single URL Crawl: ✅ PASS
Sitemap Parsing: ✅ PASS
Nested Sitemap: ✅ PASS
Total: 4/4 tests passed
🎉 All tests passed!
🐛 Troubleshooting
Playwright non si avvia
# Installa browser Playwright
pnpm exec playwright install chromium
Errori di memoria con sitemap grandi
Usa il workflow distribuito sitemapCrawlWorkflow invece di resourceEmbeddingWorkflow.
Markdown non pulito
Modifica le opzioni in htmlToMarkdown():
await htmlToMarkdown(html, {
contentSelectors: ['article', 'main', '.content'], // Aggiungi selettori custom
removeElements: ['script', 'style', 'nav', 'footer', 'aside', 'form', '.ads']
})
MinIO non raggiungibile
Verifica la configurazione:
# Controlla endpoint MinIO
echo $MINIO_ENDPOINT
# Test connessione
curl -f http://minio:9000/minio/health/live
📚 Documentazione Aggiuntiva
🔮 Roadmap Futura
- Supporto per sitemap video/immagini
- Cache DNS per sitemap ricorsive
- Metrics e monitoring per batch
- Rate limiting personalizzabile per dominio
- Screenshot delle pagine (opzionale)
- Supporto per sitemap.xml.gz
- Dashboard Grafana per monitoring