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

최종 업데이트: August 17, 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까지 다섯 가지 페이지 크기, 각 50회 반복, 독립 실행 3회, 그리고 중요한 부분인 추출 결과를 해시로 검증하는 일치성 게이트가 포함돼 있었다. 즉, 제목과 href를 정렬한 뒤 참조 파서와 해시가 맞아야만 빠른 결과로 인정된다. 일을 몰래 건너뛴 파서는 빠른 시간 기록을 남길 수 없다.

여기에 cheerio를 추가하려면 숫자를 보기 전에 두 가지를 먼저 확인해야 했다.

참조값은 같은 위치에 그대로 있었나? selectolax를 같은 세션, 같은 테스트 파일로 다시 실행했다. 콘텐츠 해시는 5개 크기 전부에서 재현됐고, p50은 공개된 수치의 0.989배~1.079배 범위에 들어왔다. 즉, 이 비교는 원래 표를 만들어낸 같은 머신에서 수행된 것이다.

cheerio는 같은 스코어 필드를 만들어냈나? Node에서 동일한 규칙, 즉 정렬된 제목 텍스트와 정렬된 href에 대해 SHA-256을 적용한 콘텐츠 해시를 계산했더니 5개 크기 전부에서 참조값과 일치했다. 이는 이 테스트 파일들에서 정렬된 필드 기준의 일치성을 증명하는 것이지, DOM 구조, 문서 순서, 속성, 텍스트 정규화, 오류 복구까지 동일하다는 뜻은 아니다.

이제서야 시간 수치에 의미가 생긴다.

페이지 크기selectolaxlxmlPyQuerycheerio (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 밀리초 기준, 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를 포함해 1 KB에서 어떤 것도 순위를 매기고 싶지 않다.

표의 중간 구간은 특별할 것 없다. 10 KB에서 1 MB 사이 페이지에서는 2배에서 4배 정도 차이만 난다. 수백 개 페이지를 긁는 스크래퍼라면 페이지당 45ms가 15ms가 되는 수준인데, 실사용에서는 거의 눈에 띄지 않는다.

10 MB 구간은 잡음이 아니다. cheerio의 3회 실행값은 2,839ms, 2,928ms, 2,954ms였고, 서로 가깝지만 다른 행들과는 분명히 떨어져 있었다. 10 MB에서의 종단 간 결과는 작은 크기에서 보이던 패턴과 확실히 달라진다. 다만 5개 크기 지점만으로는 점근적 복잡도를 단정할 수도 없고, 어떤 층 — 런타임, 파서, 선택자, 메모리 할당, GC — 이 급증을 일으켰는지도 알 수 없다.

BeautifulSoup 계열과 같은 구간에 있다. 같은 10 MB 테스트 파일에서 추가로 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 성능 얘기에서 늘 “이걸 다른 걸로 바꾸라”는 대상이기도 하다. 10 MB 문서에서 cheerio는 C 기반 파서들보다가 아니라, 바로 그 BeautifulSoup 계열의 최하단에 자리한다.

다만 이 글에서는 Node 쪽 대체재에 대한 판단은 유보한다. 더 새로운 Node 대안은 테스트하지 않았기 때문에, 이 결과만으로 라이브러리 교체가 불가능하다고 말할 수도 없고, 덜 성숙하다고 치부할 수도 없다. 여기서 보여주는 것은 측정된 cheerio 경로와 위에 나열한 Python 스택의 비교일 뿐이다.

이것은 라이브러리 비교이면서 동시에 런타임 비교다. cheerio의 밀리초 수치는 Node의 JIT와 가비지 컬렉터 영향을 받는다. 다른 쪽은 CPython이 C 기반 파서를 호출한 결과다. 콘텐츠 해시는 같은 작업이 수행됐음을 증명하고, 개발자가 스택을 선택할 때 실제로 마주하는 수치도 맞다. 하지만 이 결과를 “cheerio의 알고리즘이 selectolax보다 18배 더 나쁘다”로 읽어서는 안 된다. 이 머신에서, 각 라이브러리의 네이티브 런타임 안에서 벌어진 일일 뿐이다.

실제 환경에서의 구성 비용

라이브러리패키지 수디스크 사용량라이선스스타마지막 푸시
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만 개와 테스트 전날의 푸시는 이 범주에서 유지관리 상태가 꽤 건강하다는 신호다.

메모리와 깨진 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의 가장 높은 import floor는 66.8 MiB이며, Node 런타임과 의존성을 모두 포함한다. 10 MB 테스트 파일에서 프로세스는 398.5 MiB까지 올라갔다. 다른 행들은 파서, 변환기, 기사 추출기처럼 주된 역할이 서로 다르므로, 성능 순위보다는 프로세스 풋프린트의 맥락으로 보는 것이 맞다. 같은 런타임인 turndown 행은 더 높게 치솟았지만, 그것은 cheerio와 동일한 제목+href 선택자 계약이 아니라 변환 작업을 수행하기 때문이다.

깨진 HTML. 딱 한 가지씩만 망가뜨린 문서 12개 — 닫히지 않은 태그, 잘못 중첩된 인라인 요소, 공백이 포함된 따옴표 없는 속성, 남은 닫는 태그, <html> 태그 자체가 없음, 중복 속성, 태그 도중 잘린 문서, 잘못된 엔티티, 닫히지 않은 <script>, 거짓 charset 선언, 마크업을 담은 주석, 그리고 600단계 중첩 — 에 더해 정상 문서 2개를 같은 크기로 넣었다. “아무 것도 반환하지 않았다”는 말은, 같은 크기의 정상 문서에서도 조용히 아무것도 안 했을 때만 깨짐 여부를 말해주기 때문이다.

cheerio는 14개 중 0개에서 예외를 던졌고, 0개에서 빈 결과를 반환했으며, 잘못된 형식의 테스트에서 점수화 가능한 경계값 22개 중 11개를 복구했다(malformed-results.json). 파서용 채점기는 열한 개의 점수화 가능한 malformed 문서에서 제목과 링크 경계값을 확인하며, 문단 경계값은 채점하지 않는다. 또한 닫히지 않은 <script> 테스트 파일은 제외된다. 이 섹션에는 동일한 계약의 기준선이 없으므로 11/22를 품질 순위로 해석할 수는 없다. 다만 cheerio는 예외 없이 14개 입력 모두에서 비어 있지 않은 출력을 반환했고, 점수화 대상 마커의 절반을 복구했다는 점은 말할 수 있다.

장단점

장점. 익숙한 jQuery 문법. MIT 라이선스. 테스트 전날의 저장소 활동까지 확인된 30,449개 스타라는 유지관리 신호. 두 개의 파서 백엔드와 인코딩 관련 패키지가 포함돼 있지만, 여기서는 백엔드 복구나 인코딩 정확도를 개별적으로 분리해 보지 않았다. 정렬된 제목+href 해시는 모든 테스트 크기에서 참조값과 일치했다.

단점. 10 MB 문서에서는 selectolax보다 18.5배 느리고, 1 MB에서도 4배 느리다. 두 개의 완전한 파서 구현을 포함한 직접 의존성 11개. Node 전용. 그리고 문서 어디에도 어떤 크기부터는 명백한 선택이 아닌지에 대한 기준은 없다.

누가 써야 하고, 누가 피해야 하나

cheerio를 써도 좋은 경우는 Node 환경이고, jQuery처럼 생긴 API가 중요하며, 실제 문서가 테스트된 범위처럼 1 MB 이하에 주로 머무는 경우다. 1 MB는 10 MB에서 급격히 느려지기 직전의 가장 큰 테스트 지점이다. 이 글은 그 경계가 정확히 어디인지도, 웹 전체에서 얼마나 많은 문서가 그보다 작은지도 말해주지 않는다.

아주 큰 HTML 문서에는 먼저 벤치마크를 해보라. 생성 리포트, 카탈로그 덤프, 긴 리스트 페이지 같은 경우다. XML sitemap 동작은 테스트하지 않았다. 10 MB HTML 파일에서 문서당 2.9초는 누적되면 무시하기 어려운 비용이다.

Python 환경이라면 이 비교가 전달하는 메시지는 다르다. selectolax, lxml, PyQuery는 10 KB 이상부터 사실상 동률이다(0.5%~5.4% 범위, 실행 범위도 겹친다). 따라서 속도보다는 API를 보고 고르면 된다. 이 셋 사이의 차이보다 중요한 건 cheerio와 이들 사이의 격차다.

관리형 API가 들어갈 자리

cheerio는 이미 가지고 있는 HTML을 파싱하는 도구다. 페이지를 가져오거나, 자바스크립트를 렌더링하거나, 봇 차단 계층을 처리하지는 않는다. 실제 대상 사이트에서는 그 뒤쪽 절반이 더 어렵다.

우리의 Thunderbit를 포함한 관리형 가져오기/렌더링/추출 서비스는 책임 범위가 다르다. Thunderbit은 여기서 벤치마크하지 않았다. 중요한 차이는, HTML을 받아 선택자로 파싱하는 일과 데이터 수집·렌더링·추출 자체를 외주화하는 일 사이의 경계다. 이 글은 동일 지표 기준의 품질, 지연 시간, 비용 비교를 제공하지 않는다.

공정하게 말하면, HTML을 이미 갖고 있고 셀렉터도 알고 있다면 cheerio는 공짜에 가깝고 쓰기 편하다. 하지만 페이지를 대규모로 가져와야 하거나, DOM보다 데이터 자체를 기술하고 싶다면 얘기가 달라진다.

더 넓은 선택지를 보려면 웹 스크래핑 API 정리에서 호스팅 옵션을, 오픈소스 스크래퍼 종합 글에서 자체 호스팅 옵션을 확인할 수 있다. 파싱 결과를 모델에 넣을 예정이라면 Python에서 HTML을 Markdown으로 변환할 때 무엇을 잃는지도 참고할 만하다.

웹 데이터 추출을 위해 Thunderbit 사용해 보기

cheerio를 써야 할까?

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

API의 친숙함과 현재의 유지관리 신호는 충분히 합리적인 선택 기준이다. 다만 이 벤치마크가 특정 지원 체계가 존재함을 증명하는 것도 아니고, 테스트된 페이지 분포가 실제 운영 데이터와 같다는 뜻도 아니다.

기억해야 할 숫자는 10 MB 결과다. 1 MB와 10 MB 사이 어딘가에서 cheerio의 비용은 다른 도구들과의 비례 관계를 잃고 급증하기 시작한다. 4배가 18.5배가 된다. 데이터셋에 그런 문서가 있다면, 채택 전에 꼭 벤치마크를 해라. 라이브러리 스스로는 아무 경고도 주지 않는다.

웹 데이터 추출을 위해 Thunderbit 사용해 보기 Get Started Free

FAQ

cheerio를 Python 파서와 비교하는 게 공정한가요? 알고리즘 비교가 아니라 스택 비교이기 때문이다. 네 도구 모두 5개 페이지 크기 전부에서 해시 규칙에 따라 정렬된 제목 텍스트와 href를 동일하게 만들어냈다. 그렇다고 파서 전체가 동일하다는 뜻은 아니다. cheerio의 시간에는 Node 런타임 동작이 포함되고, 나머지는 CPython이 C 기반 파서를 호출한 결과다. 이 비교는 그런 종단 간 선택을 설명한다.

왜 1 KB 행은 순위를 매기지 않나요? 28마이크로초 수준에서는 측정값이 잡음에 지배되기 때문이다. 3회 실행 동안 Python 파서 간 편차는 77.6%였고, 개별 실행값도 서로 겹쳤다. 그 크기에서의 순서는 우연일 가능성이 크다. 10 KB 이상부터는 충분히 안정적으로 읽을 수 있다.

10 MB에서 왜 급증하나요? 이 테스트만으로는 알 수 없다. 다만 급증이 실제이고 잡음이 아니라는 점은 분명하다. cheerio의 3회 실행값은 2,839ms, 2,928ms, 2,954ms였고, 다른 결과들과 분명히 떨어져 있었다. 1 MB에서의 차이는 4배였는데 10 MB에서 18.5배로 벌어졌다. 원인을 파악하려면 cheerio의 파서 백엔드를 별도로 프로파일링해야 하는데, 이번 범위 밖이다.

의존성은 실제로 몇 개인가요? 직접 의존성은 11개, 풀린 뒤 최상위 패키지는 22개, 디스크 사용량은 9.0 MiB다. 이 중 두 개는 완전한 파서 구현인 htmlparser2parse5다. cheerio가 둘 중 어떤 것도 사용할 수 있기 때문이다. 이는 유연성과 표준 준수 파싱을 함께 지원하는 대가이며, 의존성 트리를 감사할 때 알아둘 가치가 있다.

여기서 테스트하지 않은 것은 무엇인가요? 현재 초안은 226 KB 문서 하나와 10 MB 문서 하나에서의 프로세스 최대 메모리를 테스트했고, 14개 입력의 malformed+control 세트에서 cheerio가 예외를 던지지 않았고, 모두 비어 있지 않은 출력을 반환했으며, 점수화 가능한 경계값 22개 중 11개를 복구했다. 하지만 parse5-parser-stream을 통한 스트리밍, 인코딩 정확도, 백엔드별 복구 특성, 1~10 MB 사이 성능 급증의 정확한 위치, XML 파싱, 더 최신의 Node 대안은 테스트하지 않았다. 원본 상대 경로 링크도 발행 시점에 동일한 공개 디렉터리 구조가 있어야 한다. 그렇지 않다면 지속 가능한 공개 URL이나 저장소 커밋 참조가 필요하다.

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

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

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