Passa al contenuto principale

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​

  1. Ricezione sitemap URL
  2. Parse XML sitemap
  3. Identificazione tipo entry:
    • <sitemap>: Ricorsione → torna al step 2
    • <url>: Aggiungi a lista finale
  4. Ritorno lista completa URL

Crawling Distribuito​

  1. sitemapCrawlWorkflow riceve sitemap URL
  2. prepareCrawlTask:
    • Parse sitemap ricorsivamente → ottiene tutti URL
    • Divide in batch da 10
    • Spawna N crawlBatchTask
  3. crawlBatchTask (eseguito in parallelo):
    • Crawla 10 URL con Playwright
    • Converte HTML → Markdown
    • Salva su MinIO
    • Ritorna LoaderResult[]
  4. 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​

FeatureCrawl4AICrawlee
Sitemap ricorsiva✅ (singolo livello)✅ (multi-livello)
Batching❌✅ (10 URL/batch)
HTML → MDRaw markdownPipeline unified
BrowserPlaywright/ChromiumPlaywright
Rate limitingManualeIntegrato
Retry logicManualeIntegrato
PerformanceBuonaOttima (parallelo)
Memory usageAltaOttimizzata

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​

  1. ✅ HTML to Markdown - Verifica conversione e pulizia
  2. ✅ Single URL Crawl - Test crawling singolo URL
  3. ✅ Sitemap Parsing - Test parser sitemap XML
  4. ✅ 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