Skip to content

Latest commit

 

History

History
557 lines (438 loc) · 30.1 KB

File metadata and controls

557 lines (438 loc) · 30.1 KB

🇮🇹 Italiano | 🇬🇧 English

Web Clipper CLI

Trasforma qualsiasi URL in Markdown pulito, deduplicato e pronto per Obsidian — rendering con Chrome headless + estrazione Defuddle, poi una passata opzionale di recupero via LLM. Nessuna estensione del browser richiesta.

node puppeteer-core defuddle license

Un toolkit Node.js che riproduce — e migliora — l'Obsidian Web Clipper ufficiale, da riga di comando. Gli dai un URL; lui rende la pagina col Chrome che hai già installato, toglie il rumore e scrive un .md con frontmatter in una cartella a tua scelta — pronto da mettere in un vault Obsidian o da dare in pasto a un LLM.

È deliberatamente agnostico rispetto al sito: nessun selettore o scraper specifico per sito. Ogni regola si aggancia a pattern web universali (ruoli ARIA, marcatori di classe delle librerie di carousel, stili calcolati, dati strutturati), così lo stesso codice funziona su uno store Shopify, un blog o una scheda prodotto senza casi speciali.

È anche portabile: copia la cartella clipper/ in qualsiasi progetto, puntala alla tua cartella di output (una variabile d'ambiente, un flag o un file di config) e parte — Windows, macOS o Linux, senza modifiche al codice.


Indice


I due modi per usarlo

Ci sono due livelli. Puoi fermarti al livello 1 — è completamente deterministico, gratuito e non richiede alcun LLM.

Livello Cosa gira Cosa ottieni Costo
1. Deterministico node clip.mjs <url> Un .md pulito + un sidecar di "candidati" (testo visibile che l'estrattore ha scartato) Gratis, offline, adatto a run non presidiati
2. Ibrido (consigliato) La skill /clip: livello 1 + un giudice LLM che recupera i contenuti davvero in-topic scartati per errore, poi un controllo qualità Lo stesso .md, ma più completo — con righe di specifiche, didascalie di feature, ecc. recuperate Qualche chiamata LLM (in cache)

Il livello 2 esiste perché nessuno scraper deterministico può decidere "contenuto vero vs clutter" su ogni sito — serve giudizio. Così il livello 1 va sul sicuro (non inventa mai), raccoglie in un sidecar tutto ciò di cui non era certo, e il livello 2 lascia che un LLM si pronunci su quegli scarti. Il .md di base è sempre valido di per sé; il giudice si limita ad aggiungere.


Come funziona (la pipeline ibrida)

          ┌──────────────────────── LIVELLO 1 (deterministico, clip.mjs) ────────────────────────┐
  URL ──▶ │ Chrome headless → settle/idratazione → chiusura cookie → autoscroll → espansione      │
          │ collassati → estrazione Defuddle (strategia A/C + prune R1–R5) → scrive .md pulito +   │
          │ livello commerciale JSON-LD                                                            │
          └───────────────┬──────────────────────────────────────────────────────┬───────────────┘
                          │                                                        │
                   raw/<nome>.md (fonte di verità)                 clipper/.candidates/<nome>.json
                          │                                       (blocchi visibili scartati da Defuddle)
                          │                                                        │
          ┌───────────────┴──────────────── LIVELLO 2 (ibrido, skill /clip) ───────┴───────────────┐
          │  clip-judge (LLM) tiene/scarta ogni candidato per rilevanza rispetto all'argomento      │
          │        → merge-candidates.mjs ricuce i keep nel .md (deterministico)                     │
          │        → lint-clip.mjs (check gratuiti) → clip-inspector (LLM) solo se il lint segnala    │
          │        → verdetti in cache per hash del contenuto, così i re-clip costano quasi nulla     │
          └────────────────────────────────────────────────────────────────────────────────────────┘
                          │
                   raw/<nome>.md (ora completo)

Livello 1 — l'estrattore deterministico (clip.mjs)

  1. Chrome headless. puppeteer-core avvia il Chrome che hai installato (nessun download incorporato) a 1366×900.
  2. Naviga + settle. Carica su domcontentloaded (molti store tengono aperte socket di analytics, quindi networkidle non scatta mai), aspetta un'ancora di contenuto, poi una attesa anti-skeleton: campiona la pagina finché non resta alcun elemento skeleton/shimmer/aria-busy e il testo del body è stabile, con un tetto duro di 8s.
  3. Classifica la pagina. Gli interstitial anti-bot (Cloudflare "Just a moment…", DataDome, "Access Denied") vengono rifiutati con un errore chiaro — non si scrive nulla, nessuna evasione. I soft-paywall si clippano ma vengono segnalati (meta.paywall: true).
  4. Chiude cookie/consenso. CMP noti (OneTrust, Cookiebot, Didomi) più un fallback testuale rigoroso che clicca solo veri pulsanti di consenso. Un guard-rail ripristina la pagina se un click porta altrove.
  5. Autoscroll + cattura liste virtualizzate. Un MutationObserver installato per primo registra il testo dei nodi aggiunti durante lo scroll, così le righe che le liste virtualizzate (react-window & co.) smontano poi non vanno perse. Poi scorre fino in fondo per caricare i contenuti lazy e torna su.
  6. Espande i contenuti collassati. Apre i <details> chiusi, clicca i toggle accordion aria-expanded="false" e i pulsanti multilingua "mostra di più" / "carica altro" (it/en/de/fr/es) — fuori da nav/header/footer, mai link, selettori di tab o consenso. È ciò che rende estraibili i valori di specifica nascosti negli accordion. Le tab inattive non vengono cliccate (nasconderebbero quella attiva); i loro pannelli si leggono direttamente dal DOM.
  7. Estrae con Defuddle (A/C + prune R1–R5). Il DOM viene ripulito in modo agnostico — cloni di carousel (R2), contatori a odometro (R3), nodi nascosti a basso testo (R1, preservando i veri contenuti collassati), heading duplicati (R4) — poi Defuddle gira sia sul documento intero (A) sia su una regione a densità massima isolata (C); un punteggio di qualità della prosa sceglie il vincitore. R5 ricuce il testo hero animato parola per parola. Ogni passo di prune è a prova di mutazione (rimozioni idempotenti, isolate) così i re-render SPA non fanno crashare la passata.
  8. Livello commerciale. Prezzo, disponibilità, valutazione e recensioni si leggono dal JSON-LD schema.org (con fallback OpenGraph e microdata nel DOM) in una sezione ## Dati commerciali etichettata con la fonte reale.
  9. Scrive + misura. Scrive <outDir>/<nome>.md con frontmatter identico al Web Clipper, e un sidecar clipper/.candidates/<nome>.json con i blocchi visibili scartati da Defuddle (per il livello 2) più una metrica di copertura testo (meta.coverage).

Livello 2 — recupero LLM + QA (la skill /clip)

Gira solo in modalità ibrida; vedi .claude/skills/clip/SKILL.md.

  1. Giudizio (agente clip-judge). Legge il sidecar dei candidati e l'argomento della pagina, e ritorna keep/discard per candidato in base alla rilevanza — tiene titoli/ didascalie/frasi di specifica davvero in-topic, scarta nav, promo, cookie, cross-sell, boilerplate. Nessuna conoscenza specifica del sito.
  2. Merge (merge-candidates.mjs, deterministico). Ricuce i keep nel .md: sotto un heading corrispondente quando esiste, altrimenti ricostruiti per tag sotto un'unica sezione ## Approfondimenti. Deduplica rispetto al testo esistente, marca la provenienza, è idempotente (sicuro da rieseguire).
  3. QA — lint poi inspect. lint-clip.mjs esegue 11 check deterministici gratuiti (heading orfani, residui "leggi tutto", troncamenti, etichetta-senza-valore, copertura…). Solo se segnala qualcosa entra l'agente clip-inspector, che ragiona sul risultato e suggerisce recuperi. Lint pulito → zero token extra.
  4. Cache. Ogni verdetto è messo in cache tramite lo SHA-256 del testo del candidato, così ri-clippare la stessa pagina (o una che condivide blocchi) è deterministico e quasi gratuito.

Ciclo di automiglioramento (self-improve)

Ogni run del livello 1 emette — best-effort, a costo zero sul .md — un record diagnostico strutturato in clipper/.diagnostics/ (sidecar per-clip <nome>.diag.json + registro append-only runs.jsonl): stadi in timeout/errore, copertura, percorso di estrazione, verdetto boilerplate, paywall/anti-bot, esiti fatali. A fine run — solo in modalità ibrida e solo se il record segnala errori — la skill /clip invoca l'agente clip-doctor, che pesa l'errore sullo storico recente (ricorrente vs una-tantum, per non gridare al lupo su una pagina rotta isolata) e propone fix agnostici ranked (codice o prompt di un agente), mappati sulla tassonomia di SELF-IMPROVE.md. Nulla viene applicato senza ok esplicito dell'utente: l'implementazione passa dal workflow verify+review (nessuna riscrittura silenziosa, nessun auto-commit). In --deterministic-only la diagnostica si scrive comunque, ma l'agente non gira. Il toggle è CLIP_DIAGNOSTICS (attivo di default).


Avvio rapido

cd clipper
npm install
node clip.mjs "https://example.com/un-articolo"
# → scrive ../raw/<Titolo Articolo>.md  (e clipper/.candidates/<Titolo Articolo>.json)

Questo è il livello deterministico. Per il livello ibrido, esegui la skill /clip dalla tua sessione Claude Code invece di chiamare clip.mjs direttamente (orchestra il giudice, il merge e la QA per te).


Uso

URL singolo (deterministico)

node clip.mjs <url> ["nome file esplicito senza .md"] [--out <dir>]
  • Con un nome esplicito, il clip viene scritto in <outDir>/<nome>.md.
  • Altrimenti il titolo della pagina viene sanitizzato nel nome file, esattamente come fa il Web Clipper.
  • --out <dir> sovrascrive la cartella di output per questo run (vedi Configurazione).

Tramite lo script npm:

npm run clip -- <url> ["nome file"]

Il tool stampa esattamente dove ha scritto, così gli script (e la skill /clip) possono catturare i path invece di indovinarli:

Scritto: <path assoluto del .md>
  candidati emessi: <N> -> <path assoluto del sidecar dei candidati>

Ibrido (consigliato)

In una sessione Claude Code:

/clip <url> [nome]                      # estrai + giudica + merge + QA
/clip <url> [nome] --deterministic-only # solo .md di base, nessun LLM (costo zero)

Batch

PowerShell — un URL per riga in urls.txt:

Get-Content urls.txt | Where-Object { $_ -and -not $_.StartsWith('#') } | ForEach-Object {
    node clip.mjs $_
}

bash:

grep -v '^#' urls.txt | while read -r url; do [ -n "$url" ] && node clip.mjs "$url"; done

Ogni URL viene clippato in un proprio avvio di Chrome; il browser viene sempre chiuso in un blocco finally, così una pagina che fallisce non lascia mai un processo appeso né blocca il resto del batch.


Configurazione

Nulla è obbligatorio — i default funzionano. Tutto ciò che segue è opzionale e a livelli: flag CLI > variabile d'ambiente > clipper.config.json > default interno.

Cartella di output (dove finisce il .md)

Metodo Esempio Path risolto rispetto a
Flag CLI node clip.mjs <url> --out "D:/MioVault/raw" cartella di lavoro corrente
Variabile d'ambiente CLIP_OUT_DIR="D:/MioVault/raw" cartella di lavoro corrente
File di config "outDir": "../raw" in clipper.config.json la cartella clipper/
Default (nessuno impostato)../raw la cartella clipper/

Il default ../raw significa: la cartella raw/ accanto a clipper/. Quindi se tieni clipper/ come sottocartella del tuo vault, i clip finiscono in <vault>/raw/ senza alcuna configurazione.

Eseguibile Chrome

Metodo Esempio
Variabile d'ambiente CHROME_PATH="/usr/bin/google-chrome" (oppure PUPPETEER_EXECUTABLE_PATH)
File di config "chromePath": "/usr/bin/google-chrome"
Auto-discovery (nessuno impostato) → path noti per il tuo OS (Windows/macOS/Linux), poi channel:'chrome' di puppeteer

Serve impostarlo solo se Chrome è installato in un percorso non standard. Su un'installazione normale viene trovato automaticamente su tutti e tre gli OS.

Sicurezza

Il clipper renderizza pagine web non fidate, quindi adotta alcune difese di default:

  • Sandbox di Chrome ATTIVO. Il processo renderer (che esegue il codice della pagina) gira dentro il sandbox del sistema operativo. Se un ambiente particolare (es. container/CI eseguito come root) impedisce l'avvio del sandbox, lo si può disattivare in modo esplicito: CLIP_NO_SANDBOX=1. Su un normale desktop non va disattivato.
  • Solo URL http/https. Schemi come file:, data:, chrome:, view-source: sono rifiutati, così il clipper non può leggere file locali o risorse non-web. localhost e gli IP interni restano ammessi (uso locale legittimo).
Variabile d'ambiente Effetto
CLIP_NO_SANDBOX=1 Disattiva il sandbox di Chrome (opt-in per container/CI root). Default: sandbox attivo.
CLIP_STAGE_TIMEOUT_MS=<ms> Timeout hard per ogni stadio della pipeline (chiusura cookie, scroll, espansione, guard-rail). Impedisce gli hang quando un iframe/promise non si risolve mai (tipico con URL carichi di parametri ads: gclid/utm_*/gad_*). È una rete di sicurezza contro gli hang da minuti, non una soglia stretta: sta sopra lo stadio legittimo più lento (l'autoscroll su pagine molto lunghe). Default: 180000.
CLIP_DIAGNOSTICS=0 Disattiva la scrittura della diagnostica per-run (registro di automiglioramento). Default: attiva (best-effort, non altera l'output .md né rallenta il clip).
CLIP_DIAG_DIR=<dir> Cartella del registro diagnostica. Default: clipper/.diagnostics.

Il file di config (opzionale)

Copia clipper.config.example.json in clipper.config.json e modificalo. Tutti i campi sono opzionali:

{
  "outDir": "../raw",     // dove vanno i file .md (relativo a clipper/, o assoluto)
  "chromePath": null,     // null = auto-discovery; oppure un path esplicito
  "vault": null,          // riservato per futura integrazione con la CLI ufficiale (vedi sotto)
  "attachmentsDir": null  // riservato per quando sarà cablato il download immagini/OCR
}

clipper.config.json è ignorato da git (è specifico dell'utente/macchina); versiona solo il file .example.


Uso responsabile

Il clipper scarica e converte contenuti pubblicati da terzi. Sei tu responsabile del rispetto dei Termini di Servizio, del robots.txt e del copyright dei siti che clippi. Il tool è pensato per uso locale e interattivo: non esporlo come servizio di rete — gli URL verso localhost e gli IP interni sono ammessi by design proprio perché il modello d'uso è la tua macchina, avviato da te. Le pagine protette da anti-bot vengono rifiutate con un errore, mai aggirate.


Portarlo in un altro progetto / vault

Il clipper è pensato per essere copiato. Ecco tutto ciò che serve sapere — basato su come funziona davvero Obsidian, non su supposizioni.

1. Cos'è "un vault Obsidian". Un vault è semplicemente una cartella su disco. Obsidian la osserva: nel momento in cui un file .md compare al suo interno, Obsidian lo indicizza automaticamente — link, backlink, tag e properties del frontmatter inclusi. "Obsidian aggiorna automaticamente il vault per stare al passo con qualsiasi modifica esterna."Non c'è alcun comando di "ingest" o "aggiornamento" da lanciare. Scrivere il file nella cartella del vault È l'intera procedura.

2. Quindi portarlo è una sola impostazione. Punta outDir a una cartella dentro il vault di destinazione (es. <vault>/raw o <vault>/Clippings). Tre modi equivalenti:

# a) tieni clipper/ come sottocartella del vault — zero config, usa il default ../raw
# b) variabile d'ambiente:
CLIP_OUT_DIR="/percorso/al/Vault/Clippings" node clipper/clip.mjs <url>
# c) file di config: imposta "outDir" in clipper.config.json

3. Passi per inserirlo in un nuovo progetto:

  1. Copia la cartella clipper/. (Per il livello ibrido, copia anche .claude/skills/clip/ e .claude/agents/clip-judge.md + clip-inspector.md.)
  2. cd clipper && npm install.
  3. Imposta outDir (o affidati al default ../raw). Imposta CHROME_PATH solo se serve.
  4. Esegui node clip.mjs <url> — fatto.

4. Cosa non è incluso, di proposito. Il compito del clipper finisce con "un .md pulito nella tua cartella". Trasformare quei clip in pagine wiki strutturate (riassumere, creare cross-link, aggiornare un indice) è logica specifica del tuo progetto — cambia da vault a vault ed è tenuta deliberatamente fuori da questo pacchetto, così il clipper resta agnostico e riutilizzabile.

5. Opzionale: la CLI ufficiale di Obsidian. Obsidian offre un comando ufficiale obsidian (Obsidian 1.12.7+, abilitalo in Impostazioni → Generali). Può aprire una nota, impostare properties, cercare, ecc. — mirando a un vault per nome (obsidian vault="Mio Vault" open file="…") o per la cartella di lavoro del terminale. Richiede che Obsidian sia in esecuzione e non è necessario per far comparire un clip (vedi punto 1). Il campo vault nel config è riservato a una futura integrazione opzionale; oggi il clipper si limita a scrivere il file.


Formato di output

Ogni file si apre con un blocco YAML il cui ordine dei campi e le regole di rendering combaciano esattamente col template di default dell'Obsidian Web Clipper:

---
title: "<titolo>"
source: "<URL completo compresa la query string>"
author:
  - "[[<autore>]]"          # wikilink; oppure `author:` nudo quando Defuddle non ne trova
published: 2026-06-20      # data, senza virgolette; `published:` nudo se vuoto
created: 2026-06-30        # data odierna al momento del clip
description: "<descrizione>"
tags:
  - "clippings"
---
<corpo Markdown estratto>

Regole di rendering (riprodotte dal Web Clipper):

  • testo (title, source, description): key: "value", virgolette interne con escape; key: nudo quando vuoto.
  • wikilink (author): lista a blocco con un solo elemento - "[[value]]" (nodo collegabile nel grafo Obsidian anziché testo semplice); author: nudo quando vuoto. Autori multipli non vengono separati euristicamente: l'intera stringa diventa un unico target del wikilink.
  • data (published, created): YYYY-MM-DD senza virgolette; key: nudo se vuoto.
  • multitesto (tags): lista a blocco, ogni voce - "value". tags è letteralmente clippings.
  • source = URL completo con query string (viene rimosso solo il frammento #:~:text=…).
  • created = oggi al runtime. published = il primo segmento del valore di Defuddle.

Sezioni del corpo

Il corpo è il Markdown Defuddle della strategia vincente, ripulito da R1–R5, con le immagini preservate come ![](url-assoluto). La struttura editoriale (heading, feature, tabelle di specifiche, recensioni) è mantenuta; navigazione, chrome dei cookie, cloni di carousel e testo frammentato sono rimossi.

  • ## Approfondimenti (solo ibrido) — blocchi in-topic che l'estrattore aveva scartato e il giudice ha promosso, ricostruiti in ordine di lettura. Presente solo quando il giudice recupera contenuto orfano.
  • ## Dati commerciali — aggiunta solo quando esistono dati strutturati: prezzo, prezzo di listino, valuta, disponibilità, valutazione aggregata, numero di recensioni e una tabella per recensione — da JSON-LD (con fallback OpenGraph / microdata), con una nota di provenienza che indica la fonte.

I componenti (file per file)

File Ruolo Esegue
clip.mjs L'estrattore deterministico. URL → .md pulito + sidecar candidati. Node CLI
merge-candidates.mjs Merge deterministico dei keep del giudice nel .md. Idempotente. Node CLI
lint-clip.mjs 11 check gratuiti di completezza/qualità sul .md finale. Output --json. Node CLI
score-clip.mjs Valuta un .md rispetto a una checklist di stringhe attese (benchmark). Node CLI
ocr.mjs Scaffold per l'OCR del testo incorporato nelle immagini. Non ancora cablato in clip.mjs. Node (WIP)
.claude/agents/clip-judge.md Agente LLM: tiene/scarta i candidati per rilevanza. Agnostico. Claude Code
.claude/agents/clip-inspector.md Agente LLM: ragiona sul clip finale per lacune di completezza. Claude Code
.claude/agents/clip-doctor.md Agente LLM: diagnostica gli errori di un run e propone fix agnostici ranked. Non applica. Claude Code
.claude/skills/clip/SKILL.md Orchestra la pipeline ibrida (estrai → giudica → merge → QA → cache → self-improve). Claude Code
SELF-IMPROVE.md Tassonomia agnostica dei failure-mode → classe di rimedio ammesso. Doc

Generati a runtime (ignorati da git): .candidates/ (sidecar + verdetti), .judge-cache/ (verdetti content-addressed), .ocr-cache/, .diagnostics/ (record per-run + registro runs.jsonl).

Esempi CLI standalone:

node lint-clip.mjs "../raw/Mio Clip.md" "clipper/.candidates/Mio Clip.json" --json
node merge-candidates.mjs "../raw/Mio Clip.md" ".candidates/Mio Clip.json" ".candidates/Mio Clip.verdicts.json" "../raw/Mio Clip.md"

Confronto con le alternative

Confronto illustrativo su un singolo caso reale (scheda prodotto, luglio 2026): non è un benchmark sistematico.

Basato su un audit a 3 vie della stessa scheda prodotto (Dreame L50). Questo clipper ha prodotto l'unico output strutturato e deduplicato (~535 righe utili) contro le ~1925 righe di Apify e le ~2678 di Firecrawl, che includono anche navigazione, script e markup non editoriale.

Capacità Web Clipper CLI (questo) Obsidian Web Clipper (ufficiale) Apify Firecrawl
Motore Chrome headless + Defuddle + recupero LLM Estensione in-browser + Defuddle Crawler cloud Crawler cloud + LLM
Render JS / idratazione Sì (autoscroll + settle) Sì (la tua tab live)
Recupera il contenuto scartato dall'estrattore (giudice sui candidati) No No No
Rumore / dedup Forte (R1–R5, scorer A/C) Moderato Non prevista (output integrale) Non prevista (output integrale)
Frontmatter , identico al Web Clipper No No
Livello commerciale (prezzo/valutazione/recensioni) (JSON-LD + microdata) No Solo nell'output integrale, non strutturato Solo nell'output integrale, non strutturato
QA di completezza (lint + inspector) No No No
Gira headless / non presidiato No (serve la UI del browser) Sì (cloud) Sì (cloud)
OCR (testo nelle immagini) Pianificato No No Parziale
Costo Gratis / locale (+ LLM se ibrido) Gratis SaaS a pagamento SaaS a pagamento

Firecrawl e Apify includono i dati commerciali perché restituiscono la pagina integrale. Questo progetto recupera gli stessi dati in forma strutturata (JSON-LD) e usa un LLM per recuperare il contenuto editoriale che una passata deterministica scarta — mantenendo l'output essenziale.


Risoluzione problemi

Sintomo Causa Soluzione
Could not find Chrome / avvio fallito Chrome non in un path standard Imposta CHROME_PATH (o chromePath nel config) sull'eseguibile Chrome/Chromium.
PAGINA BLOCCATA da anti-bot e nulla scritto La pagina è dietro una sfida anti-bot (Cloudflare, DataDome…) Per scelta — nessuna evasione. Clippala a mano da una sessione browser reale.
Clip scritto ma scarno; warning sul paywall Soft-paywall rilevato (meta.paywall: true) Il testo visibile può essere solo l'anteprima gratuita. Nulla di più è recuperabile senza login.
copertura testo: NN% bassa Molta pagina è media, virtualizzata o dietro interazioni Usa la modalità ibrida: il giudice recupera i candidati scartati. Controlla il sidecar per vedere cosa è stato raccolto.
Clip degenerato in boilerplate cookie/consenso Il muro di consenso non è stato chiuso Lo script rifiuta di scriverlo (esce con codice non-zero). Riprova, o clippa a mano.
Valori di specifica mancanti su una scheda prodotto Erano dentro un accordion/tab collassato Già gestito da expandCollapsed + harvest tab nascoste; se ancora manca, il valore è probabilmente reso solo dopo un click che il tool evita — controlla il sidecar dei candidati.
clipper.config.json ignorato (JSON non valido) JSON del config malformato Correggi il JSON; nel frattempo il tool usa i default.
Cartella di output sbagliata C'è un override attivo Ricorda la precedenza: --out > CLIP_OUT_DIR > clipper.config.json > ../raw.
Schema non consentito Hai passato un URL non http/https (file:, data:, …) Per sicurezza sono ammessi solo http/https. Per leggere un file locale, servilo via un server http.
Chrome non parte in un container/CI (root) Il sandbox non si avvia come root Imposta CLIP_NO_SANDBOX=1 (opt-in). Su desktop lascia il sandbox attivo.
Il clip si blocca a lungo su "Chiusura banner cookie…" (o un altro stadio) Un iframe/promise di quello stadio non si risolve mai — tipico con URL pieni di parametri di tracking (gclid/gbraid/utm_*/gad_*) che caricano iframe pubblicitari Ogni stadio ha ora un timeout hard: regola CLIP_STAGE_TIMEOUT_MS (default 180000 ms). In genere basta ri-clippare con l'URL pulito, senza parametri di tracking.

Requisiti

  • Node.js ≥ 22.12 (sviluppato su Node v24, npm 11).
  • Google Chrome o Chromium installato. Trovato automaticamente su Windows/macOS/Linux; sovrascrivi con CHROME_PATH se è in un percorso non standard.
  • Qualsiasi OS — Windows, macOS, Linux.

Dipendenze pinnate: puppeteer-core@25.1.0, defuddle@0.19.0, jsdom@^25. puppeteer-core (non puppeteer) è una scelta deliberata: pilota il Chrome che hai già installato e non scarica alcun binario del browser.


Limitazioni

  • Il livello commerciale ha bisogno di dati strutturati. Prezzo/valutazione/recensioni vengono da JSON-LD, OpenGraph o microdata itemprop; pagine che non ne espongono nessuno non producono ## Dati commerciali.
  • Niente OCR. Il testo incorporato nelle immagini non viene ancora letto (scaffold in ocr.mjs).
  • Niente trascrizione video. I video sono tenuti solo come link/thumbnail.
  • Le pagine molto commerciali mantengono un po' di chrome d'acquisto. La base deterministica è la fonte di verità e non viene mai potata a posteriori; alcune sezioni promo/permuta possono restare. Il giudice si limita ad aggiungere i contenuti scartati; la potatura della base è in roadmap.
  • Le pagine anti-bot vengono rifiutate, non aggirate (per scelta).
  • I metadati dipendono dalla pagina. Su siti molto dinamici author/published possono risultare vuoti — comportamento atteso, identico al Web Clipper.
  • Le SPA con DOM molto instabile restano imprevedibili. Su pagine il cui contenuto cambia struttura da una sessione all'altra (feed raccomandati, varianti A/B, ordine dei commenti), l'euristica generica di isolamento del contenuto principale può occasionalmente agganciare la sezione sbagliata. Non esiste un rimedio agnostico affidabile al 100% per questi casi — selettori specifici del sito violerebbero la filosofia del progetto.

Roadmap

Vedi ROADMAP.md. Priorità a breve:

  1. OCR per il testo incorporato nelle immagini.
  2. Specifiche come tabelle GFM (da JSON-LD o tabelle HTML).
  3. Promozione opzionale dei campi commerciali nel frontmatter (Dataview).

Già consegnato: pipeline di recupero ibrida (giudice + merge + QA), livello commerciale (JSON-LD + microdata), espansione contenuti collassati + "mostra di più", harvest tab nascoste e shadow-DOM, discovery Chrome cross-platform, cartella di output configurabile, portabilità.


Contribuire

Issue e PR sono benvenute. Mantieni intatto il principio fondante: le regole devono essere agnostiche rispetto al sito — agganciarsi a pattern web universali (ARIA, marcatori di classe delle librerie, stili calcolati, dati strutturati), mai selettori specifici per sito. Quando aggiungi una regola di pulizia, documenta quale failure mode affronta e perché non può mangiare contenuto legittimo, nello stile di R1–R5, con un esempio prima/dopo su una pagina reale.


Licenza

MIT — vedi LICENSE.


Obsidian, Defuddle, Puppeteer, Apify, Firecrawl e i prodotti citati come casi di test appartengono ai rispettivi titolari. Progetto indipendente, non affiliato ad alcuno di essi.