cheerio ha impiegato 2,9 secondi su una pagina da 10 MB in Node

Ultimo aggiornamento il August 17, 2026
cheerio ha impiegato 2,9 secondi su una pagina da 10 MB in Node
Riepilogo AI
Su una macchina, cheerio in Node ha analizzato ed estratto titoli e href da un documento HTML sintetico da 10 MB in 2.927,89 ms. selectolax, eseguito tramite CPython con un parser supportato in C, ha completato la stessa estrazione dei campi in 158 ms. I valori hash dei titoli ordinati e degli href coincidevano. Si tratta di un confronto end-to-end tra stack e runtime, non di una sentenza isolata sull’algoritmo del parser. Su una pagina da 10 KB la differenza è 2×, e nessuno se ne accorgerebbe. La vera domanda è dove si collocano le tue pagine su quella curva. Usa cheerio se lavori in Node, l’API in stile jQuery ti è utile e le pagine rappresentative assomigliano alle dimensioni testate fino a 1 MB.

Su una macchina, cheerio in Node ha analizzato ed estratto titoli e href da un documento HTML sintetico da 10 MB in 2.927,89 ms. selectolax, eseguito tramite CPython con un parser supportato in C, ha completato la stessa estrazione dei campi in 158 ms. I valori hash dei titoli ordinati e degli href coincidevano. Si tratta di un confronto end-to-end tra stack e runtime, non di una sentenza isolata sull’algoritmo del parser.

Su una pagina da 10 KB la differenza è 2×, e nessuno se ne accorgerebbe. La vera domanda è dove si collocano le tue pagine su quella curva.

Cos’è cheerio

cheerio è il parser HTML per Node con sintassi jQuery, ed è la risposta predefinita in quell’ecosistema per un motivo preciso: 30.449 stelle su GitHub, licenza MIT e un aggiornamento al repository il giorno prima dei miei test. Versione testata: 1.2.0.

Riferimento ufficiale: introduzione ufficiale a Cheerio.

import * as cheerio from "cheerio";
const $ = cheerio.load(html);
const titles = $("h3.title").map((_, e) => $(e).text()).get();
const hrefs = $("a").map((_, e) => $(e).attr("href")).get();

Se hai già usato jQuery, conosci già l’API. Proprio questa familiarità è una delle ragioni principali del suo successo nel suo ecosistema.

Sotto il cofano non c’è un singolo parser, ma uno stack: htmlparser2 e parse5 per il parsing, domhandler e domutils per l’albero DOM, cheerio-select per i selettori, oltre a undici, encoding-sniffer e altri ancora — undici dipendenze dirette, che diventano 22 pacchetti di primo livello e 9,0 MiB su disco. Questi pacchetti forniscono capacità di parsing e gestione dell’encoding, ma contribuiscono anche al peso della dipendenza. Questa analisi ha testato l’output con input malformati, ma non ha verificato la correttezza dell’encoding né isolato i singoli backend del parser.

La misurazione, e perché è affidabile

La base di ricerca aveva già un benchmark per parser: cinque dimensioni di pagina da 1 KB a 10 MB, 50 iterazioni, tre esecuzioni indipendenti e — la parte che conta — una soglia di parità che confronta l’hash del contenuto estratto, titoli ordinati e href ordinati, con un parser di riferimento. Un parser che salta silenziosamente del lavoro non può ottenere un tempo veloce.

Per aggiungere cheerio, servivano due verifiche prima di prendere qualsiasi numero per buono.

Il riferimento era finito nello stesso punto di prima? selectolax è stato rieseguito nella stessa sessione sugli stessi fixture. Il suo hash del contenuto è risultato identico su 5 su 5 dimensioni, e il suo p50 è rientrato tra 0,989× e 1,079× rispetto al valore pubblicato. Quindi questa è la macchina che ha prodotto la tabella originale.

cheerio produceva gli stessi campi valutati? Il suo hash del contenuto — calcolato in Node con la stessa regola, SHA-256 su titoli ordinati e href ordinati — coincideva con il riferimento su 5 su 5 dimensioni. Questo dimostra la parità per quei campi ordinati su questi fixture, non la forma del DOM, l’ordine del documento, gli attributi, la normalizzazione del testo o il recupero dagli errori.

Solo a quel punto i tempi hanno davvero senso.

Dimensione paginaselectolaxlxmlPyQuerycheerio (Node)cheerio vs selectolax
1 KB0,0286 ms0,05080,04560,1147 ms4,0×
10 KB0,1725 ms0,18020,17280,3490 ms2,0×
100 KB1,4855 ms1,41451,40933,8399 ms2,6×
1 MB14,97 ms15,0314,9659,37 ms4,0×
10 MB158,10 ms165,25162,862.927,89 ms18,5×

p50 in millisecondi, mediana di tre esecuzioni. parser-bench.json. I tre parser Python sono stati eseguiti in un solo processo; cheerio è stato eseguito in Node 22, quindi c’è anche un confine di runtime, non solo di libreria — vedi sotto.

Leggere la tabella con onestà

Measured results chart: Parser time across page sizes

La riga da 1 KB è rumore. Tra i tre parser Python, su quella dimensione la differenza è del 77,6% e le singole esecuzioni si sovrappongono in modo ampio — selectolax è andato da 0,0267 a 0,0404 ms nelle tre prove. A 28 microsecondi dominano risoluzione del timer e scheduling. Non classificherei nulla a 1 KB, cheerio compreso.

La parte centrale della tabella non sorprende. Tra 10 KB e 1 MB siamo tra 2× e 4×. Per uno scraper che gestisce poche centinaia di pagine, parliamo di 45 millisecondi a pagina invece di 15: nella pratica non te ne accorgerai.

La riga da 10 MB non è rumore. Le tre esecuzioni di cheerio sono risultate in 2.839, 2.928 e 2.954 ms — strette tra loro e nettamente separate dal resto. Il risultato end-to-end a 10 MB si discosta chiaramente dal pattern delle dimensioni più piccole. Cinque punti di misura non bastano per stabilire la complessità asintotica o per dire con precisione quale livello — runtime, parser, selettore, allocazione o garbage collection — provochi il salto.

Si colloca nella fascia di BeautifulSoup. Il benchmark pubblicato ha misurato altri quattro parser sullo stesso fixture da 10 MB, e mettere i 2.927,89 ms di cheerio accanto a loro è la cosa più utile di questo articolo:

Parser (10 MB)p50
selectolax (lexbor)159,93 ms
lxml172,93 ms
parsel231,85 ms
selectolax (modest)247,95 ms
BeautifulSoup + lxml2.261,56 ms
BeautifulSoup + html.parser2.788,75 ms
cheerio2.927,89 ms

Le quattro righe Python riportano i valori pubblicati da bench_parse.json; quello di cheerio proviene da questa esecuzione. Il parser di riferimento è stato riprodotto entro 0,989×–1,079× tra le due esecuzioni, quindi considera differenze inferiori a circa l’8% come interne a quell’incertezza — cheerio contro il backend html.parser di BeautifulSoup (5% di distanza) rientra in questa soglia, cheerio contro selectolax (18×) no.

BeautifulSoup è la libreria che le persone scelgono quando vogliono comodità e accettano consapevolmente che sia lenta — è quella che in ogni discussione sulle prestazioni in Python viene sempre consigliato di sostituire. Su un documento da 10 MB, cheerio si trova nella parte bassa di quella stessa fascia, non in quella dei parser supportati da C con cui viene spesso accostato.

La questione di un sostituto lato Node resta aperta in questo articolo. Non sono state testate alternative Node più recenti, quindi questo risultato non può dire che cambiare libreria sia impossibile né liquidarle come meno consolidate. Mostra solo il percorso misurato di cheerio rispetto agli stack Python elencati.

È anche un confronto tra runtime, non solo tra librerie. I millisecondi di cheerio dipendono dal JIT e dal garbage collector di Node; gli altri arrivano da CPython che chiama parser supportati da C. L’hash del contenuto prova che il lavoro svolto è lo stesso, e entrambi i numeri sono ciò che un developer sperimenta davvero quando sceglie uno stack — ma nessuno dovrebbe leggere questo dato come “l’algoritmo di cheerio è 18× peggiore di quello di selectolax”. È semplicemente ciò che è successo, su questa macchina, nel runtime nativo di ciascuna libreria.

La realtà dell’installazione

LibreriaPacchettiDiscoLicenzaStelleUltimo push
cheerio22 (npm)9,0 MiBMIT30.4492026-08-11
PyQuery3 (pip)20,1 MiBBSD2.3802026-07-27

Riferimento ufficiale: documentazione di configurazione di Cheerio.

metadata-snapshot.json, recuperato il giorno della stesura.

npm install cheerio ha richiesto meno di due secondi e ha scaricato 9,0 MiB. L’importazione a freddo ha impiegato 0,056 s in una prova separata di conversione sulla stessa macchina.

Undici dipendenze dirette sono tante per un parser, ed è utile saperlo se fai auditing del tuo albero: htmlparser2, parse5, parse5-htmlparser2-tree-adapter, parse5-parser-stream, domhandler, domutils, dom-serializer, cheerio-select, encoding-sniffer, undici e whatwg-mimetype. Dentro ci sono due implementazioni complete di parser, perché cheerio può usare l’una o l’altra a seconda di ciò che gli chiedi.

Trentamila stelle e un push il giorno prima dei test sono segnali di manutenzione più che sani in questa categoria.

Memoria e impatto dell’HTML rotto

L’impronta di memoria e il comportamento con HTML malformato incidono su deployment e gestione dei guasti, quindi qui vengono misurati separatamente.

Il contesto più ampio del test di stress è nel confronto su memoria e HTML malformato di dieci librerie.

Memoria resident peak, tramite /usr/bin/time -l, un processo nuovo per cella — il floor di import è ciò che la libreria consuma da caricata e inattiva, i picchi includono il documento.

LibreriaRuntimeImport floorPicco 226 KBPicco 10 MB
html2textpython3.1418,719,971,2
pyquerypython3.1430,333,9172,5
resiliparsepython3.1420,525,1225,1
markdownifypython3.1423,928,9278,5
goose3python3.1444,152,4398,5
cheerionode2266,876,5398,5
justextpython3.1430,336,6431,2
newspaper4kpython3.1452,661,8668,5
trafilaturapython3.1452,564,8927,1
turndownnode2247,868,42947,1

memory-results.json. I baseline di Python e Node non sono direttamente confrontabili tra loro; il runtime dell’interprete è presente in entrambi.

cheerio ha il più alto import floor in questa tabella mista con 66,8 MiB, includendo runtime Node e dipendenze. Il suo processo ha raggiunto un picco di 398,5 MiB sul fixture da 10 MB. Le altre righe includono parser, convertitori ed estrattori di articoli che svolgono compiti principali diversi, quindi usale come contesto sull’impronta del processo e non come classifica diretta delle prestazioni tra pari. La riga di turndown nello stesso runtime è salita molto di più, ma esegue conversione, non il contratto di estrazione di titoli e href misurato per cheerio.

HTML rotto. Dodici documenti che rompono esattamente una sola cosa — tag non chiusi, elementi inline annidati male, attributi senza virgolette con spazi, tag di chiusura solitari, assenza totale di <html>, attributi duplicati, un documento troncato a metà tag, entità errate, uno <script> non chiuso, una dichiarazione di charset falsa, un commento che contiene markup e 600 livelli di annidamento — più due controlli ben formati di dimensioni corrispondenti, perché “non ha restituito nulla” dice qualcosa sulla malformazione solo se la libreria non resta muta anche su un documento pulito della stessa dimensione.

cheerio ha generato un’eccezione in 0 su 14 casi e non ha restituito nulla in 0 casi, recuperando 11/22 sentinelle valutate nei fixture malformati (malformed-results.json). Per i parser, il sistema di scoring controlla le sentinelle di heading e link su undici documenti malformati valutabili; non assegna punteggio alla sentinella del paragrafo ed esclude il fixture con <script> non chiuso. Senza un baseline con lo stesso contratto in questa sezione, 11/22 non è una classifica qualitativa. La conclusione supportata è che cheerio ha restituito un output non vuoto senza errori su tutti e quattordici gli input malformati+di controllo, recuperando però solo metà dei marker valutati.

Pro e contro

A favore. Sintassi jQuery familiare. MIT. Un segnale di manutenzione con timestamp: 30.449 stelle e attività nel repository il giorno prima dei test. Sono presenti due backend di parsing e pacchetti legati all’encoding, anche se il recupero del backend e l’accuratezza dell’encoding non sono stati isolati qui. L’hash ordinato di titoli + href ha coinciso con il riferimento in ogni dimensione testata.

Contro. 18,5× più lento di selectolax su un documento da 10 MB, e 4× su 1 MB. Undici dipendenze dirette, comprese due implementazioni complete del parser. Solo Node. E nella documentazione non c’è alcun indizio su una dimensione oltre la quale smette di essere la scelta ovvia.

Chi dovrebbe usarlo e chi no

Usa cheerio se lavori in Node, l’API in stile jQuery ti è utile e le pagine rappresentative assomigliano alle dimensioni testate fino a 1 MB. Un megabyte è l’ultimo punto testato prima del salto netto a 10 MB; questo articolo non stabilisce la soglia tra i due né afferma quanta parte del web si trovi sotto di essa.

Fai benchmark prima di usarlo per documenti HTML molto grandi, come report generati, dump di cataloghi o pagine con liste molto lunghe. Il comportamento sugli sitemap XML non è stato testato. Sul fixture HTML da 10 MB, 2,9 secondi per documento sono un costo concreto che si moltiplica rapidamente.

Se lavori in Python, questo confronto dice altro: selectolax, lxml e PyQuery sono praticamente alla pari da 10 KB in su (entro lo 0,5%–5,4%, con intervalli di esecuzione sovrapposti), quindi la scelta va fatta sull’API, non sulla velocità. La distanza di cheerio da tutti e tre è il numero interessante, non le differenze tra loro.

Dove si inserisce un’API gestita

cheerio analizza HTML che hai già in mano. Non scarica, non renderizza JavaScript e non gestisce un livello anti-bot — e su molti target reali quella è la parte più difficile del lavoro.

Un servizio gestito di fetch/render/estrazione, incluso il nostro Thunderbit, si colloca su un piano di responsabilità diverso. Thunderbit non è stato benchmarkato qui. La distinzione importante è tra analizzare HTML fornito con selettori e delegare acquisizione, rendering ed estrazione; questo articolo non offre un confronto omogeneo in termini di qualità, latenza o costo.

Per dirla in modo equo: se hai già l’HTML e conosci i tuoi selettori, cheerio è gratuito e piacevole da usare. Se devi raccogliere pagine su larga scala, o preferisci descrivere i dati invece del DOM, stai facendo un acquisto diverso.

Per il panorama più ampio, il nostro roundup delle API per web scraping copre le opzioni hosted, mentre la guida ai scraper open source riguarda quelle self-hosted. Se l’output parsato deve finire in un modello, convertire HTML in Markdown in Python spiega dove si perde fedeltà.

Prova Thunderbit per l’estrazione di dati dal web

Conviene usare cheerio?

Sì, in Node, quando conta la compatibilità dell’API e i documenti rappresentativi restano vicini all’intervallo testato da piccolo fino a 1 MB.

La familiarità dell’API e i segnali attuali di manutenzione sono elementi legittimi per la scelta. Il benchmark non dimostra che esista un certo supporto tecnico né che la distribuzione delle dimensioni testate corrisponda a un corpus di produzione.

Il numero da ricordare è quello dei 10 MB. Da qualche parte tra 1 MB e 10 MB, il costo di cheerio smette di seguire gli altri e inizia a moltiplicarsi — da 4× a 18,5×. Se il tuo corpus contiene documenti di quella dimensione, fai benchmark prima di impegnarti, perché nella libreria non c’è nulla che ti avvisi.

Prova Thunderbit per l’estrazione di dati dal web Get Started Free

FAQ

Confrontare cheerio con parser Python è corretto? È un confronto tra stack, non tra algoritmi. Tutti e quattro hanno prodotto titoli ordinati e href identici secondo la regola di hashing su 5 su 5 dimensioni di pagina. Questo non prova l’equivalenza totale dei parser. I tempi di cheerio includono il comportamento runtime di Node, mentre gli altri includono CPython che chiama parser supportati da C; il confronto descrive queste scelte end-to-end.

Perché la riga da 1 KB non è classificata? Perché a 28 microsecondi la misurazione è dominata dal rumore. Su tre esecuzioni i parser Python hanno mostrato una dispersione del 77,6%, con singole prove che si sovrappongono. Qualsiasi ordine a quella dimensione sarebbe un artefatto. Dalla riga da 10 KB in su la lettura è stabile.

Cosa provoca il salto a 10 MB? Questo test non lo dice. Quello che stabilisce è che il salto è reale e non rumore: le tre esecuzioni di cheerio sono finite a 2.839, 2.928 e 2.954 ms, ben distanti da tutto il resto, mentre la distanza a 1 MB era di 4×. Isolare la causa significherebbe profilare separatamente i backend del parser di cheerio, cosa fuori dallo scopo di questa analisi.

Quante dipendenze ha davvero? Undici dirette, 22 pacchetti di primo livello dopo la risoluzione, 9,0 MiB su disco. Due di queste sono implementazioni complete del parser — htmlparser2 e parse5 — perché cheerio può usare entrambe. È il prezzo da pagare per gestire sia parsing permissivo sia conforme allo standard, ed è un dettaglio utile se fai auditing degli alberi di dipendenze.

Cosa non è stato testato qui? La bozza attuale ha testato la memoria di picco del processo su un documento da 226 KB e uno da 10 MB, oltre a un insieme di quattordici input malformati+di controllo in cui cheerio non ha generato errori, ha restituito sempre un output non vuoto e ha recuperato 11/22 sentinelle valutate. Non sono stati testati lo streaming via parse5-parser-stream, la correttezza dell’encoding, il recupero specifico del backend, la posizione esatta del salto prestazionale tra 1 e 10 MB, il parsing XML o alternative Node più recenti. I link grezzi agli artifact relativi richiedono inoltre la stessa struttura di directory pubblica al momento della pubblicazione; altrimenti servono URL pubblici stabili o un riferimento al commit del repository.

Ke
Ke
CTO di Thunderbit | Senior Data Scientist ed esperto di ML Con quasi un decennio di esperienza nel machine learning e nella data science, Ke Shen è un ex studente della Columbia University ed ex Senior Data Scientist presso Walmart Labs. Grazie a una profonda competenza, riconosciuta dai suoi pari, in Python, R, Java e statistica, condivide insight collaudati sul passaggio di algoritmi AI complessi dalla teoria a un'architettura pronta per la produzione.
Indice
Thunderbit · Agente AI per dati web

Estrai dati da qualsiasi pagina in 1 clic

Scelto da oltre 250.000 utenti
piano gratuito disponibile
Dalla pagina web al foglio di calcolo
Descrivi ciò che ti serve — l'agente AI di Thunderbit lo estrae ed esporta in Excel, Google Sheets, Airtable o Notion. Puoi iniziare gratis.
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week