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