cheerio levou 2,9 segundos em uma página de 10 MB no Node

Última atualização em August 17, 2026
cheerio levou 2,9 segundos em uma página de 10 MB no Node
Resumo com IA
Em uma máquina, o cheerio no Node analisou e extraiu títulos e hrefs de um documento HTML sintético de 10 MB em 2.927,89 ms. O selectolax, executado no CPython com um parser em C, concluiu a mesma extração de campos em 158 ms. Os textos dos títulos ordenados e os hashes dos hrefs coincidiram. Esta é uma comparação de stack ponta a ponta entre runtimes, não um veredicto isolado sobre o algoritmo do parser. Em uma página de 10 KB, a diferença é de 2×, e ninguém perceberia. A questão toda é onde suas páginas se encaixam nessa curva. Use cheerio se você está no Node, a API no formato de jQuery é valiosa e as páginas representativas se parecem com os tamanhos testados até 1 MB.

Em uma máquina, o cheerio no Node analisou e extraiu títulos e hrefs de um documento HTML sintético de 10 MB em 2.927,89 ms. O selectolax, rodando no CPython com um parser em C, concluiu a mesma extração de campos em 158 ms. Os textos dos títulos ordenados e os hashes dos hrefs bateram exatamente. Esta é uma comparação de stack ponta a ponta entre runtimes, não um veredito isolado sobre o algoritmo do parser.

Em uma página de 10 KB, a diferença é de 2×, e ninguém perceberia. A questão mesmo é onde suas páginas se encaixam nessa curva.

O que é o cheerio

O cheerio é o parser HTML com sintaxe de jQuery para Node, e por um bom motivo costuma ser a resposta padrão nesse ecossistema: 30.449 estrelas no GitHub, licença MIT, e um push no repositório no dia anterior ao teste. Versão testada: 1.2.0.

Referência oficial: introdução oficial do 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 você já usou jQuery, já conhece a API. Essa familiaridade é uma das principais razões pelas quais ele ganhou espaço no seu ecossistema.

Por baixo dos panos, não é um único parser, mas uma pilha: htmlparser2 e parse5 para parsing, domhandler e domutils para a árvore, cheerio-select para seletores, além de undici, encoding-sniffer e outros — onze dependências diretas, virando 22 pacotes de primeiro nível e 9,0 MiB em disco. Esses pacotes entregam capacidade de parsing e codificação, mas também aumentam o tamanho da árvore de dependências. Esta análise testou a saída em entradas malformadas, mas não verificou a correção de codificação nem isolou qualquer um dos backends de parser.

A medição, e por que ela é confiável

A base de pesquisa já tinha um benchmark de parser: cinco tamanhos de página de 1 KB a 10 MB, 50 iterações, três execuções independentes e — o ponto importante — uma validação de paridade que faz hash do conteúdo extraído, títulos ordenados mais hrefs ordenados, contra um parser de referência. Um parser que silenciosamente deixa trabalho de fora não pode fingir que é rápido.

Adicionar o cheerio a esse conjunto exigiu duas checagens antes que qualquer número valesse.

O resultado de referência continuou no mesmo lugar? O selectolax foi executado de novo na mesma sessão e com os mesmos fixtures. O hash do conteúdo se repetiu em 5 de 5 tamanhos, e o p50 ficou entre 0,989× e 1,079× do valor publicado. Ou seja, esta é a mesma máquina que gerou a tabela original.

O cheerio produziu os mesmos campos avaliados? O hash do conteúdo — calculado no Node com a mesma regra, SHA-256 sobre o texto dos títulos ordenados e os hrefs ordenados — bateu com a referência em 5 de 5 tamanhos. Isso comprova paridade para esses campos ordenados nesses fixtures, mas não para formato do DOM, ordem do documento, atributos, normalização de texto ou recuperação de erros.

Só então os tempos passam a significar alguma coisa.

Tamanho da páginaselectolaxlxmlPyQuerycheerio (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 em milissegundos, mediana de três execuções. parser-bench.json. Os três parsers Python rodaram em um único processo; o cheerio rodou no Node 22, o que também é uma fronteira de runtime e não só de biblioteca — veja abaixo.

Lendo a tabela com honestidade

Measured results chart: Parser time across page sizes

A linha de 1 KB é ruído. Entre os três parsers Python, a variação nesse tamanho foi de 77,6%, e as execuções individuais se sobrepõem bastante — o selectolax variou de 0,0267 a 0,0404 ms nas três rodadas. Em 28 microssegundos, a resolução do timer e o agendamento do sistema dominam. Eu não colocaria nada no topo em 1 KB, inclusive o cheerio.

O meio da tabela é pouco impressionante. Entre 10 KB e 1 MB, a diferença fica entre 2× e 4×. Para um scraper que processa algumas centenas de páginas, isso significa 45 milissegundos por página em vez de 15, algo que você praticamente não vai sentir.

A linha de 10 MB não é ruído. As três execuções do cheerio ficaram em 2.839, 2.928 e 2.954 ms — consistentes e bem distantes das demais linhas. O resultado ponta a ponta em 10 MB se afasta claramente do padrão dos tamanhos menores. Cinco pontos de tamanho não bastam para cravar complexidade assintótica nem para dizer se o salto vem do runtime, do parser, do seletor, da alocação ou da coleta de lixo.

Ele cai na faixa do BeautifulSoup. O benchmark publicado mediu mais quatro parsers no mesmo fixture de 10 MB, e colocar os 2.927,89 ms do cheerio ao lado deles é a parte mais útil deste artigo:

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

As quatro linhas Python são os valores publicados de bench_parse.json; a do cheerio é desta execução. O parser de referência se reproduziu entre 0,989× e 1,079× nas duas rodadas, então trate diferenças menores que cerca de 8% como dentro dessa incerteza — cheerio contra o backend html.parser do BeautifulSoup (5% de diferença) está dentro disso; cheerio contra selectolax (18×) não está.

BeautifulSoup é a biblioteca que o pessoal escolhe quando quer praticidade e aceita, de propósito, que ela é lenta — é aquela que toda discussão de performance em Python manda substituir. Em um documento de 10 MB, o cheerio fica no fim dessa mesma faixa, e não na faixa dos parsers com backend em C aos quais costuma ser comparado.

A questão da substituição no lado do Node continua em aberto neste artigo. Alternativas mais novas para Node não foram testadas, então este resultado não pode afirmar que trocar de biblioteca é impossível nem descartá-las como menos consolidadas. Ele só mostra o caminho medido do cheerio em comparação com as stacks Python listadas.

Também é uma comparação de runtime, não só de biblioteca. Os milissegundos do cheerio vêm do JIT e do coletor de lixo do Node; os demais vêm do CPython chamando parsers com backend em C. O hash do conteúdo prova que o mesmo trabalho foi executado, e ambos os números são o que um desenvolvedor realmente sente ao escolher uma stack — mas ninguém deve ler isso como “o algoritmo do cheerio é 18× pior que o do selectolax”. Foi isso que aconteceu, nesta máquina, no runtime nativo de cada biblioteca.

Realidade da configuração

BibliotecaPacotesDiscoLicençaEstrelasÚltimo push
cheerio22 (npm)9,0 MiBMIT30.4492026-08-11
PyQuery3 (pip)20,1 MiBBSD2.3802026-07-27

Referência oficial: documentação de configuração do Cheerio.

metadata-snapshot.json, obtido no dia da escrita.

npm install cheerio levou menos de dois segundos e baixou 9,0 MiB. A importação a frio levou 0,056 s em uma execução separada do conversor na mesma máquina.

Onze dependências diretas é coisa demais para um parser, e vale a pena saber disso se você audita sua árvore: htmlparser2, parse5, parse5-htmlparser2-tree-adapter, parse5-parser-stream, domhandler, domutils, dom-serializer, cheerio-select, encoding-sniffer, undici e whatwg-mimetype. Dois parsers completos vêm embutidos, porque o cheerio pode usar qualquer um deles dependendo do que você pedir.

Trinta mil estrelas e um push no dia anterior ao teste são sinais de manutenção tão saudáveis quanto se pode esperar nessa categoria.

Memória e o que HTML quebrado faz com ela

O uso de memória e o comportamento diante de HTML malformado afetam implantação e tratamento de falhas, então ambos são medidos separadamente aqui.

O contexto mais amplo desse stress test está na comparação de memória e HTML malformado entre dez bibliotecas.

Pico de memória residente, via /usr/bin/time -l, um processo novo por célula — o piso de importação é o custo da biblioteca carregada e ociosa; os picos incluem o documento.

BibliotecaRuntimePiso de importaçãoPico em 226 KBPico em 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. As baselines de Python e Node não são comparáveis entre si; o interpretador faz parte dos dois.

cheerio tem o maior piso de importação nesta tabela mista, com 66,8 MiB, incluindo o runtime Node e suas dependências. O processo bateu 398,5 MiB no fixture de 10 MB. As demais linhas incluem parsers, conversores e extratores de artigos fazendo trabalhos principais diferentes, então use-as como contexto de footprint do processo, não como ranking direto de desempenho entre pares. A linha do turndown, no mesmo runtime, atingiu um pico muito maior, mas ela faz conversão em vez do contrato de seleção de títulos e hrefs medido para o cheerio.

HTML quebrado. Doze documentos quebrando exatamente uma coisa — tags não fechadas, elementos inline mal aninhados, atributos sem aspas com espaços, fechamentos soltos, ausência total de <html>, atributos duplicados, um documento truncado no meio de uma tag, entidades inválidas, um <script> não fechado, uma declaração de charset mentirosa, um comentário contendo marcação e 600 níveis de aninhamento — mais dois controles bem-formados em tamanhos correspondentes, porque “não retornou nada” só diz algo sobre malformação se a biblioteca também ficar em silêncio em um documento limpo do mesmo tamanho.

O cheerio lançou erro em 0 de 14 e não retornou nada em 0, recuperando 11/22 sentinelas avaliadas entre os fixtures malformados (malformed-results.json). Para parsers, o scorer verifica sentinelas de heading e link em onze documentos malformados pontuáveis; ele não pontua a sentinela de parágrafo, e o fixture com <script> não fechado é excluído. Sem um baseline com o mesmo contrato nesta seção, 11/22 não é um ranking de qualidade. A conclusão suportada é que o cheerio retornou saída não vazia sem lançar erro em todas as quatorze entradas malformadas+controle, enquanto recuperou metade dos marcadores pontuados.

Prós e contras

A favor. Sintaxe jQuery familiar. MIT. Um sinal de manutenção com data: 30.449 estrelas e atividade no repositório no dia anterior ao teste. Dois backends de parser e pacotes relacionados a codificação estão presentes, embora a recuperação do backend e a precisão da codificação não tenham sido isoladas aqui. O hash ordenado de títulos + hrefs coincidiu com o de referência em todos os tamanhos de fixture.

Contra. 18,5× mais lento que o selectolax em um documento de 10 MB, e 4× em 1 MB. Onze dependências diretas, incluindo duas implementações completas de parser. Apenas Node. E nada na documentação sugere um tamanho em que ele deixe de ser a escolha óbvia.

Quem deve usar e quem não deve

Use cheerio se você está no Node, a API no formato de jQuery é valiosa e as páginas representativas se parecem com os tamanhos testados até 1 MB. Um megabyte é o maior ponto testado antes do salto brusco em 10 MB; este artigo não define o ponto de corte entre eles nem diz qual parte da web fica abaixo dele.

Faça benchmark antes de usar em documentos HTML muito grandes, como relatórios gerados, dumps de catálogo ou páginas longas de listagem. O comportamento com sitemap XML não foi testado. No fixture HTML de 10 MB, 2,9 segundos por documento é um custo relevante que vai se acumulando.

Se você está em Python, esta comparação diz outra coisa: selectolax, lxml e PyQuery ficam praticamente empatados de 10 KB para cima (dentro de 0,5% a 5,4%, com intervalos de execução sobrepostos), então escolha pela API, não pela velocidade. A diferença do cheerio para os três é o número que importa, não as diferenças entre eles.

Onde uma API gerenciada se encaixa

O cheerio faz parsing do HTML que você já tem. Ele não busca páginas, não renderiza JavaScript nem lida com camada anti-bot — e, em muitos alvos reais, essa é a metade mais difícil do trabalho.

Um serviço gerenciado de fetch/render/extraction, incluindo o nosso Thunderbit, fica em outra fronteira de responsabilidade. O Thunderbit não foi benchmarkado aqui. A diferença relevante é: analisar HTML fornecido com seletores versus terceirizar aquisição, renderização e extração; este artigo não traz comparação de qualidade, latência ou custo na mesma métrica.

O enquadramento justo é este: se você já tem o HTML e conhece seus seletores, o cheerio é grátis e agradável de usar. Se você está buscando páginas em escala, ou prefere descrever os dados em vez do DOM, isso é uma compra diferente.

Para o panorama mais amplo, nosso resumo de APIs de web scraping cobre opções hospedadas e o guia de scrapers open source cobre as auto-hospedadas. Se a saída parseada for para um modelo, converter HTML em Markdown em Python mostra onde a fidelidade se perde.

Experimente o Thunderbit para extração de dados da web

Vale a pena usar cheerio?

Sim, no Node, quando a compatibilidade da API importa e os documentos representativos ficam perto da faixa pequena até 1 MB testada.

A familiaridade da API e os sinais atuais de manutenção são critérios legítimos de escolha. O benchmark não prova que exista uma resposta de suporte específica nem que a distribuição de tamanhos das páginas testadas corresponda a um corpus de produção.

O número que você precisa guardar é o de 10 MB. Em algum ponto entre 1 MB e 10 MB, o custo do cheerio deixa de acompanhar os demais e passa a multiplicar — 4× vira 18,5×. Se o seu corpus contém documentos desse tamanho, faça benchmark antes de decidir, porque nada na biblioteca vai te avisar.

Experimente o Thunderbit para extração de dados da web Get Started Free

Perguntas frequentes

Comparar cheerio com parsers Python é justo? É uma comparação de stack, não de algoritmo. Os quatro produziram o mesmo texto de título ordenado e os mesmos hrefs sob a regra de hash em 5 de 5 tamanhos de página. Isso não prova equivalência total de parser. Os tempos do cheerio incluem o comportamento do runtime Node, enquanto os demais incluem o CPython chamando parsers com backend em C; a comparação descreve essas escolhas de ponta a ponta.

Por que a linha de 1 KB não é ranqueada? Porque, em 28 microssegundos, a medição é dominada por ruído. Nas três execuções, os parsers Python tiveram uma dispersão de 77,6%, com execuções individuais se sobrepondo entre si. Qualquer ordenação nesse tamanho seria um artefato. A partir de 10 KB, a leitura já fica estável.

O que causa o salto em 10 MB? Este teste não diz. O que ele mostra é que o salto é real e não ruído: as três execuções do cheerio ficaram em 2.839, 2.928 e 2.954 ms, bem separadas do resto, enquanto a diferença em 1 MB foi de 4×. Isolar a causa exigiria perfilar separadamente os backends do parser do cheerio, o que ficou fora do escopo aqui.

Quantas dependências ele realmente tem? Onze diretas, 22 pacotes de primeiro nível após a resolução, 9,0 MiB em disco. Duas delas são implementações completas de parser — htmlparser2 e parse5 — porque o cheerio pode usar qualquer uma. Esse é o preço de lidar tanto com parsing permissivo quanto com parsing aderente à especificação, e vale a pena saber disso ao auditar árvores de dependência.

O que não foi testado aqui? O rascunho atual testou memória de pico do processo em um documento de 226 KB e um de 10 MB, além de um conjunto de 14 entradas malformadas+controle no qual o cheerio não lançou erro em nenhuma, retornou saída não vazia em todas e recuperou 11/22 sentinelas pontuadas. Não foram testados streaming via parse5-parser-stream, correção de codificação, recuperação específica de backend, a localização do salto de performance entre 1 e 10 MB, parsing de XML ou alternativas mais novas para Node. Os links brutos dos artefatos também exigem a mesma estrutura pública de diretório no momento da publicação; caso contrário, precisam de URLs públicas duráveis ou de uma referência de commit do repositório.

Ke
Ke
CTO na Thunderbit | Cientista de Dados Sênior e Especialista em ML Com quase uma década de experiência em machine learning e data science, Ke Shen é ex-aluno da Columbia University e foi Cientista de Dados Sênior na Walmart Labs. Com profunda experiência, reconhecida pelos pares, em Python, R, Java e Estatística, ele compartilha insights testados em batalha sobre como levar algoritmos complexos de IA da teoria à arquitetura pronta para produção.
Sumário
Thunderbit · Agente de dados web com IA

Extraia dados de qualquer página em 1 clique

Aprovado por mais de 250.000 usuários
plano gratuito disponível
Da página web para a planilha
Descreva o que você precisa — o Agente de IA da Thunderbit extrai e exporta para Excel, Google Sheets, Airtable ou Notion. Comece grátis.
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week