Node에서 10MB 페이지를 2.9초 만에 처리한 cheerio

최종 업데이트: August 19, 2026
Node에서 10MB 페이지를 2.9초 만에 처리한 cheerio
AI 요약
한 머신에서 Node의 cheerio는 10MB 가상 HTML 문서에서 제목과 href를 추출하는 작업을 2,927.89 ms에 끝냈다. C 기반 파서를 사용하는 CPython 환경의 selectolax는 같은 필드 추출을 158 ms 만에 완료했다. 정렬된 제목 텍스트와 href 해시값은 일치했다. 이는 단일 파서 알고리즘만 떼어 놓은 평가가 아니라, 런타임까지 포함한 전체 스택 비교다. 10KB 페이지에서는 차이가 2배 수준이라 누구도 체감하지 못한다. 핵심은 당신의 페이지가 이 곡선의 어디쯤에 있느냐는 점이다. Node 환경이고 jQuery처럼 생긴 API가 중요하며, 실제 문서가 테스트된 1 MB 이하 범위에 가깝다면 cheerio를 쓰는 것이 좋다.

한 머신에서 Node의 cheerio는 10MB짜리 합성 HTML 문서에서 제목과 href를 파싱·추출하는 데 2,927.89 ms가 걸렸습니다. 반면 C 기반 파서를 사용하는 CPython 환경의 selectolax는 동일한 필드 추출을 158 ms에 끝냈습니다. 정렬된 제목 텍스트와 href 해시는 일치했습니다. 이는 단일 파서 알고리즘만 떼어 놓은 판정이 아니라, 런타임까지 포함한 엔드투엔드 스택 비교입니다.

10KB 페이지에서는 격차가 2배 수준이라 누구도 체감하지 못할 것입니다. 핵심은 여러분의 페이지가 그 곡선의 어디쯤에 있는가입니다.

cheerio란 무엇인가

cheerio는 Node에서 사용하는 jQuery 문법의 HTML 파서이며, 그 생태계에서 기본 선택지로 자리 잡은 데에는 이유가 있습니다. GitHub 스타 30,449개, MIT 라이선스, 그리고 제가 이 테스트를 실행한 전날에도 저장소에 커밋이 있었습니다. 테스트한 버전은 1.2.0입니다.

공식 문서: 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();

jQuery를 써 본 적이 있다면 API는 이미 익숙할 것입니다. 이 친숙함이 cheerio가 해당 생태계에서 승자가 된 가장 큰 이유입니다.

내부적으로는 단일 파서가 아니라 여러 구성요소가 겹친 스택입니다. 파싱에는 htmlparser2parse5, 트리에는 domhandlerdomutils, 셀렉터에는 cheerio-select, 그 밖에 undici, encoding-sniffer 등 — 직접 의존성만 11개이며, 해석 결과로는 최상위 패키지 22개와 디스크 사용량 9.0 MiB로 이어집니다. 이 패키지들은 파싱과 인코딩 기능을 제공하는 동시에 의존성 부담도 늘립니다. 이번 리뷰는 잘못된 입력에 대한 출력은 테스트했지만, 인코딩의 정확성이나 두 파서 백엔드 중 어느 쪽인지의 분리는 검증하지 않았습니다.

측정 방식과 신뢰할 수 있는 이유

이 연구 베이스에는 이미 파서 벤치마크가 있었습니다. 1KB부터 10MB까지 5가지 페이지 크기, 각 50회 반복, 3개의 독립 실행, 그리고 중요한 부분인 — 추출된 콘텐츠를 해시하는 일치성 검증 게이트가 있어 정렬된 제목과 정렬된 href를 기준 파서와 비교합니다. 조용히 일을 건너뛴 파서는 빠른 시간을 내더라도 통과할 수 없습니다.

여기에 cheerio를 추가하려면, 수치 하나를 믿기 전에 두 가지를 먼저 확인해야 했습니다.

기준값이 이전과 같은 위치에 도착했는가? selectolax를 동일한 세션과 동일한 fixture에서 다시 실행했습니다. 콘텐츠 해시는 5개 크기 모두에서 재현되었고, p50은 공개된 수치의 0.989배~1.079배 사이였습니다. 즉, 원래 표를 만들어 낸 바로 그 머신입니다.

cheerio가 같은 채점 필드를 만들어 냈는가? Node에서 동일한 규칙, 즉 정렬된 제목 텍스트와 정렬된 href에 대해 SHA-256을 적용한 콘텐츠 해시는 5개 크기 모두에서 기준값과 일치했습니다. 이는 이 fixture들에서 해당 정렬 필드의 일치성을 증명할 뿐이며, DOM 형태, 문서 순서, 속성, 텍스트 정규화, 오류 복구까지 포함하는 것은 아닙니다.

이제서야 타이밍을 의미 있게 볼 수 있습니다.

페이지 크기selectolaxlxmlPyQuerycheerio (Node)cheerio 대비 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 밀리초, 3회 실행의 중앙값입니다. parser-bench.json. 세 개의 Python 파서는 하나의 프로세스에서 실행되었고, cheerio는 Node 22에서 실행되었으므로 런타임 경계이자 라이브러리 경계이기도 합니다 — 아래 참고.

이 표를 제대로 읽는 법

Measured results chart: Parser time across page sizes

1 KB 행은 노이즈입니다. 세 개 Python 파서 간의 그 크기에서의 편차는 77.6%였고, 개별 실행값도 크게 겹쳤습니다. selectolax는 3번 실행에서 0.0267~0.0404 ms를 오갔습니다. 28마이크로초 수준에서는 타이머 해상도와 스케줄링이 지배적입니다. cheerio를 포함해 1KB에서 무엇이 더 빠르다고 단정하지 않겠습니다.

표의 중간 구간은 특별할 게 없습니다. 10KB~1MB 페이지에서는 2배에서 4배 정도입니다. 몇백 페이지를 긁는 스크래퍼라면 페이지당 45밀리초냐 15밀리초냐의 차이이고, 체감하기 어렵습니다.

10 MB 행은 노이즈가 아닙니다. cheerio의 3회 실행값은 2,839ms, 2,928ms, 2,954ms였고, 서로도 가깝고 다른 행들과는 확실히 분리됩니다. 10MB의 엔드투엔드 결과는 더 작은 크기에서 보이던 패턴에서 뚜렷하게 벗어납니다. 다만 5개 포인트만으로는 점근적 복잡성을 증명할 수도 없고, 어떤 계층 — 런타임, 파서, 셀렉터, 할당, 가비지 컬렉션 — 이 점프를 일으켰는지도 특정할 수 없습니다.

BeautifulSoup 계열과 같은 구간에 있습니다. 같은 10MB fixture에서 공개 벤치마크는 4개의 파서를 더 측정했습니다. cheerio의 2,927.89 ms를 그들과 나란히 놓는 것이 이 글에서 가장 유용한 부분입니다.

파서 (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

위의 네 Python 행은 bench_parse.json에 있는 공개 수치이고, cheerio는 이번 실행 결과입니다. 기준 파서는 두 실행 사이에서 0.989배~1.079배로 재현되었으므로 약 8% 이내 차이는 오차 범위로 보아야 합니다. 따라서 cheerio와 BeautifulSoup의 html.parser 백엔드 간 5% 차이는 그 범위 안이고, cheerio와 selectolax 간 18배 차이는 아닙니다.

BeautifulSoup는 편의성을 위해 쓰되 느리다는 걸 알면서도 감수하는 도구입니다. Python 성능 관련 글에서 늘 대체하라고 언급되는 바로 그 라이브러리죠. 10MB 문서에서 cheerio는 C 기반 파서들과 어울리는 구간이 아니라 그와 같은 밴드의 맨 아래에 놓입니다.

이 글에서는 Node 쪽 대체재에 대한 질문은 아직 열려 있습니다. 더 최신의 Node 대안들은 테스트하지 않았으므로, 이번 결과만으로 라이브러리 교체가 불가능하다고 말할 수는 없고, 덜 검증되었다고 치부할 수도 없습니다. 단지 측정된 cheerio 경로를 나열된 Python 스택들과 비교했을 뿐입니다.

이 비교는 라이브러리 비교이면서 동시에 런타임 비교입니다. cheerio의 밀리초는 Node의 JIT와 가비지 컬렉터에서 나오고, 다른 라이브러리들은 CPython이 C 코드를 호출한 결과입니다. 콘텐츠 해시는 같은 작업이 수행되었음을 증명하고, 숫자 둘 다 스택을 선택하는 개발자가 실제로 마주하는 경험입니다. 하지만 이를 "cheerio의 알고리즘이 selectolax보다 18배 나쁘다"고 읽어서는 안 됩니다. 이 머신에서, 각 라이브러리의 네이티브 런타임 안에서 실제로 벌어진 일일 뿐입니다.

설정의 현실

라이브러리패키지 수디스크 사용량라이선스스타 수마지막 push
cheerio22 (npm)9.0 MiBMIT30,4492026-08-11
PyQuery3 (pip)20.1 MiBBSD2,3802026-07-27

공식 문서: Cheerio 설정 문서.

metadata-snapshot.json, 작성 당일에 가져온 스냅샷입니다.

npm install cheerio는 2초도 안 걸렸고 9.0 MiB를 내려받았습니다. 콜드 임포트는 같은 머신에서 별도 변환 실행 기준 0.056초였습니다.

직접 의존성 11개는 파서치고는 많은 편이며, 트리를 감사할 때 알아둘 만합니다. htmlparser2, parse5, parse5-htmlparser2-tree-adapter, parse5-parser-stream, domhandler, domutils, dom-serializer, cheerio-select, encoding-sniffer, undici, whatwg-mimetype가 포함됩니다. cheerio는 필요에 따라 둘 중 하나를 사용할 수 있기 때문에 완전한 파서 구현체가 두 개나 들어 있습니다.

스타 3만 개와 테스트 전날의 push는 이 카테고리에서 유지보수 신호로는 꽤 건강한 편입니다.

메모리와 깨진 HTML이 미치는 영향

메모리 사용량과 잘못된 HTML에 대한 대응은 배포와 장애 처리에 영향을 주므로, 여기서는 둘을 따로 측정했습니다.

더 넓은 스트레스 테스트 맥락은 10개 라이브러리의 메모리 및 잘못된 HTML 비교에 있습니다.

최대 상주 메모리/usr/bin/time -l로 측정했고, 각 셀마다 새 프로세스를 사용했습니다. import floor는 라이브러리를 로드하고 대기 상태로 둘 때 드는 비용이고, peak에는 문서 처리 비용이 포함됩니다.

라이브러리런타임Import floor226 KB peak10 MB peak
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. Python과 Node의 기준값은 서로 직접 비교할 수 없습니다. 둘 다 인터프리터가 포함되기 때문입니다.

이 혼합 맥락 표에서 cheerio는 Node 런타임과 의존성을 포함한 가장 높은 import floor인 66.8 MiB를 기록했습니다. 10MB fixture에서 프로세스 peak는 398.5 MiB였습니다. 다른 행들은 기사 추출기, 변환기, 파서 등 서로 다른 주된 작업을 수행하므로, 동급 성능 비교가 아니라 프로세스 메모리 맥락으로 보아야 합니다. 같은 런타임의 turndown은 peak가 훨씬 높지만, cheerio가 측정한 제목·href 선택 계약이 아니라 변환 작업을 수행합니다.

깨진 HTML. 정확히 하나씩만 고장 난 12개의 문서 — 닫히지 않은 태그, 잘못 중첩된 인라인 요소, 공백이 포함된 따옴표 없는 속성, 불필요한 닫는 태그, <html> 자체가 없음, 중복 속성, 태그 중간에서 끊긴 문서, 잘못된 엔티티, 닫히지 않은 <script>, 거짓 charset 선언, 마크업을 담은 주석, 600단계 중첩 — 에 더해, 크기가 맞는 정상 문서 2개를 추가했습니다. "아무것도 돌려주지 않았다"는 말은 같은 크기의 깨끗한 문서에도 침묵하는 라이브러리라면 malformedness를 판단할 근거가 되지 않기 때문입니다.

cheerio는 14개 중 0개에서 예외를 던졌고, 0개에서 빈 결과를 반환하지 않았으며, malformed fixture 전반에서 점수 대상 sentinel 22개 중 11개를 복구했습니다(malformed-results.json). 파서용 scorer는 점수 가능한 11개의 malformed 문서에서 heading과 link sentinel을 검사하며, paragraph sentinel은 점수화하지 않고, 닫히지 않은 <script> fixture는 제외합니다. 이 섹션에는 같은 계약의 기준선이 없으므로 11/22를 품질 순위로 해석하면 안 됩니다. 다만 지원 가능한 결론은, cheerio가 14개의 malformed+control 입력 모두에서 예외 없이 비어 있지 않은 출력을 반환했고, 점수 대상 마커의 절반을 복구했다는 점입니다.

장단점

장점. 익숙한 jQuery 문법. MIT. 테스트 전날의 저장소 활동과 30,449 스타라는 시점이 찍힌 유지보수 신호. 두 개의 파서 백엔드와 인코딩 관련 패키지가 들어 있지만, 이번 글에서는 백엔드 복구나 인코딩 정확도는 분리 검증하지 않았습니다. 정렬된 제목+href 해시는 모든 fixture 크기에서 기준값과 일치했습니다.

단점. 10MB 문서에서 selectolax보다 18.5배 느리고, 1MB에서도 4배 느립니다. 직접 의존성 11개, 그중 완전한 파서 구현 두 개. Node 전용. 그리고 문서 어디에도 "이 크기부터는 당연한 선택이 아니다"라는 경계가 적혀 있지 않습니다.

누가 써야 하고, 누가 쓰지 말아야 하는가

cheerio를 쓰면 좋은 경우: Node 환경이고, jQuery처럼 생긴 API가 가치가 있으며, 대표 문서가 테스트한 1MB 이하 범위와 비슷할 때입니다. 1MB는 급격한 10MB 점프 직전의 가장 큰 테스트 지점입니다. 이 글은 그 사이의 정확한 경계나 웹의 얼마나 많은 부분이 그 아래에 있는지는 밝히지 않습니다.

매우 큰 HTML 문서라면 사용 전 벤치마크가 필요합니다. 생성 리포트, 카탈로그 덤프, 긴 목록 페이지 같은 경우입니다. XML sitemap의 동작은 테스트하지 않았습니다. 10MB HTML fixture에서는 문서당 2.9초가 드는 셈이라 누적 비용이 큽니다.

Python 환경이라면 이야기가 조금 다릅니다. selectolax, lxml, PyQuery는 10KB 이상에서 사실상 동률입니다(0.5%~5.4% 범위, 실행 범위도 겹침). 그러니 속도보다 API 기준으로 고르세요. 그 셋 사이보다 중요한 숫자는 cheerio와 그들 사이의 격차입니다.

관리형 API는 어디에 들어맞는가

cheerio는 이미 가지고 있는 HTML을 파싱합니다. 페이지를 가져오거나, JavaScript를 렌더링하거나, 안티봇 계층을 처리하지는 않습니다. 실제 목표 사이트에서는 종종 그 부분이 더 어려운 절반입니다.

우리의 Thunderbit를 포함한 관리형 fetch/render/extraction 서비스는 책임 경계가 다릅니다. Thunderbit는 이번 벤치마크에 포함되지 않았습니다. 중요한 차이는, 제공된 HTML을 셀렉터로 파싱하는 것과 수집, 렌더링, 추출을 외주화하는 것 사이에 있습니다. 이 글은 동일 지표 기준의 품질, 지연시간, 비용 비교를 제공하지 않습니다.

공정하게 말하면, HTML을 이미 갖고 있고 셀렉터를 알고 있다면 cheerio는 무료에 가깝고 사용도 편합니다. 반대로 페이지를 대규모로 가져와야 하거나, DOM이 아니라 데이터 자체를 설명하고 싶다면 그건 다른 선택입니다.

더 넓은 시장 관점에서는 웹 스크래핑 API 총정리에서 호스팅 옵션을, 오픈소스 스크래퍼 종합 가이드에서 셀프 호스팅 도구를 다룹니다. 파싱 결과를 모델로 보낼 예정이라면, Python에서 HTML을 Markdown으로 변환하기에서 fidelity가 어디서 손실되는지도 확인할 수 있습니다.

웹 데이터 추출을 위해 Thunderbit 체험하기

cheerio를 써야 할까?

네, Node 환경에서 API 적합성이 중요하고 대표 문서가 테스트한 소형~1MB 범위에 머문다면 그렇습니다.

API의 익숙함과 현재의 유지보수 신호는 실제 선택 기준입니다. 다만 이번 벤치마크가 특정한 지원 체계가 존재함을 증명하거나, 테스트한 페이지 크기 분포가 실제 운영 코퍼스와 같다고 말해 주지는 않습니다.

꼭 기억할 숫자는 10MB입니다. 1MB와 10MB 사이 어딘가에서 cheerio의 비용은 다른 도구들을 따라가지 못하고 곱절로 커지기 시작합니다. 4배가 18.5배가 됩니다. 코퍼스에 그런 크기의 문서가 있다면, 본격 도입 전에 벤치마크를 돌리세요. 라이브러리 자체는 아무 경고도 주지 않습니다.

웹 데이터 추출을 위해 Thunderbit 체험하기 Get Started Free

FAQ

cheerio를 Python 파서와 비교하는 게 공정한가요? 알고리즘 비교가 아니라 스택 비교입니다. 4개 모두 5개 페이지 크기에서 해시 규칙 기준으로 정렬된 제목 텍스트와 href를 동일하게 만들어 냈습니다. 그렇다고 전체 파서가 동등하다는 뜻은 아닙니다. cheerio의 타이밍에는 Node 런타임의 동작이, 다른 쪽에는 CPython이 C 기반 파서를 호출하는 비용이 포함됩니다. 이 비교는 그런 엔드투엔드 선택을 설명합니다.

왜 1 KB 행은 순위를 매기지 않나요? 28마이크로초 수준에서는 측정값이 노이즈에 지배되기 때문입니다. 3회 실행에서 Python 파서들의 편차는 77.6%였고, 개별 실행값도 서로 겹쳤습니다. 그 크기에서의 순서는 우연일 뿐입니다. 10KB 이상부터는 읽을 만한 안정성이 있습니다.

10 MB에서 왜 점프가 생기나요? 이 테스트는 원인을 말해 주지 않습니다. 다만 그 점프가 실제이며 노이즈가 아니라는 점은 보여 줍니다. cheerio의 3회 실행값은 2,839ms, 2,928ms, 2,954ms로 다른 모든 값과 분리되어 있었고, 1MB에서는 격차가 4배였습니다. 원인을 분리하려면 cheerio의 파서 백엔드를 각각 따로 프로파일링해야 하는데, 이번 범위 밖이었습니다.

의존성은 실제로 몇 개인가요? 직접 의존성 11개, 해석 후 최상위 패키지 22개, 디스크 사용량 9.0 MiB입니다. 그중 두 개는 htmlparser2parse5라는 완전한 파서 구현체입니다. cheerio가 관대한 파싱과 스펙 준수 파싱 둘 다를 처리할 수 있기 때문입니다. 의존성 트리를 감사할 때 알아둘 만한 비용입니다.

이번 글에서 테스트하지 않은 것은 무엇인가요? 한 개의 226 KB 문서와 한 개의 10 MB 문서에 대한 프로세스 peak 메모리, 그리고 14개 입력으로 구성된 malformed+control 세트에서 cheerio가 예외를 던지지 않았고, 모두 비어 있지 않은 출력을 반환했으며, 점수 대상 sentinel 22개 중 11개를 복구했다는 점을 테스트했습니다. parse5-parser-stream을 통한 스트리밍, 인코딩 정확성, 백엔드별 복구 특성, 1~10MB 사이 성능 점프의 위치, XML 파싱, 더 최신의 Node 대안은 다루지 않았습니다.

Ke
Ke
Thunderbit CTO | 시니어 데이터 사이언티스트 & ML 전문가 머신러닝과 데이터 과학 분야에서 약 10년에 가까운 경험을 쌓아온 Ke Shen은 컬럼비아 대학교 출신이며, 전 Walmart Labs의 시니어 데이터 사이언티스트였습니다. Python, R, Java, 통계 분야에서 동료들에게도 인정받는 깊은 전문성을 바탕으로, 복잡한 AI 알고리즘을 이론에서 실제 운영 수준의 아키텍처로 전환하는 데 필요한 실전 인사이트를 공유합니다.
목차
Thunderbit · AI 웹 데이터 에이전트

1클릭 안에서 어떤 페이지든 데이터 추출

25만 명 이상의 사용자가 신뢰
무료 플랜 제공
웹페이지에서 스프레드시트까지
필요한 내용만 설명하세요 — Thunderbit의 AI 에이전트가 수집하고 Excel, Google Sheets, Airtable, Notion으로 내보냅니다. 시작은 무료입니다.
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week