데이터 크롤링

Jsoup · Selenium · Playwright · BeautifulSoup · Puppeteer, 뭘 언제 쓸까

2026. 08. 05.
CrawlingJsoupSeleniumPlaywrightBackend
Jsoup
Selenium
Playwright
BeautifulSoup
Puppeteer

역할이 다 다르다.

예시 시나리오 외부 API를 주기적으로 호출해서 JSON으로 저장하던 예시 수집기가 있다. 이제 여기에 "API로 제공되지 않는 사이트의 데이터를 긁어오는 크롤링 기능"을 붙여야 한다.


크롤링의 두 가지 큰 갈래

먼저 도구들을 크게 두 부류로 나눠야 한다. 이 구분이 모든 것의 시작이다.

1. HTML 파서 계열
   - HTTP 요청으로 HTML을 받아오고, 문자열을 파싱해서 원하는 값을 뽑는다.
   - "브라우저를 흉내 내지 않는다."
   - 대표: Jsoup (Java), BeautifulSoup (Python)

2. 브라우저 자동화 계열
   - 진짜 브라우저(Chromium 등)를 띄워서 페이지를 열고 JS를 실행한 뒤 결과 DOM을 본다.
   - "사람이 크롬 여는 것과 똑같이 동작한다."
   - 대표: Selenium, Playwright, Puppeteer

이 구분이 왜 중요하냐면, 요즘 웹의 절반 이상은 JavaScript로 렌더링되기 때문이다.

<!-- 서버가 준 원본 HTML -->
<div id="root"></div>
<script src="app.js"></script>

이런 페이지를 Jsoup으로 가져오면 <div id="root"></div> 만 나온다. 실제 데이터는 브라우저가 JS를 실행한 뒤에야 채워진다. HTML 파서로는 손도 못 댄다.

반대로 브라우저 자동화 도구는 무겁고 느리다. 서버 자원도 훨씬 많이 먹는다.

모든 크롤링의 첫 질문은 "이 사이트가 JS 렌더링이 필요한가?" 다.


다섯 도구 한눈에 보기

도구언어계열JS 실행특징
JsoupJava/KotlinHTML 파서JVM 진영의 표준. 가볍고 빠름
BeautifulSoupPythonHTML 파서Python 진영의 표준. requests와 짝
Selenium다국어 (Java, Python, JS 등)브라우저 자동화가장 오래된 표준. 테스트 자동화 뿌리
Playwright다국어 (JS, Python, Java, .NET)브라우저 자동화MS가 만든 차세대. Selenium 대안
PuppeteerJS/TS브라우저 자동화Google 제작. Chrome 전용

HTML 파서 계열

Jsoup — JVM 진영의 표준

어떻게 동작하나?

  1. 내부 HTTP 클라이언트로 URL에 GET 요청을 날린다.
  2. 응답으로 받은 HTML 문자열을 파싱해서 DOM Tree를 만든다.
  3. CSS Selector나 DOM 탐색 API로 원하는 노드를 뽑는다.
Document doc = Jsoup.connect("https://example.com/list")
    .userAgent("Mozilla/5.0")
    .timeout(5000)
    .get();
 
Elements titles = doc.select("div.article > h2.title");
titles.forEach(t -> log.info(t.text()));

장점

  • 가볍다. 라이브러리 하나 추가하면 끝. 브라우저 프로세스도 필요 없음.
  • 빠르다. 페이지 하나 처리에 수십~수백 ms.
  • Spring Boot에 그대로 녹인다. 별도 프로세스 관리 불필요.
  • CSS Selector가 강력하다.

단점

  • JS 실행 못 함. SPA는 못 긁는다.
  • 로그인·쿠키·세션은 직접 관리해야 함 (일부는 지원하지만 브라우저만큼 자연스럽지 않음).
  • 최신 웹의 안티봇(Cloudflare 등)에 상대적으로 취약.

언제 쓰나? 서버가 HTML을 완성해서 주는 사이트. 뉴스, 블로그, 위키, 오래된 관리자 페이지, 공공데이터 페이지 등.

BeautifulSoup — Python 진영의 사촌

Jsoup와 거의 같은 포지션이다. 다만 Python이라는 점이 다르다.

import requests
from bs4 import BeautifulSoup
 
html = requests.get("https://example.com/list").text
soup = BeautifulSoup(html, "html.parser")
 
for h2 in soup.select("div.article > h2.title"):
    print(h2.get_text())

Jsoup와의 차이

항목JsoupBeautifulSoup
HTTP 요청내장 (Jsoup.connect(...))별도 라이브러리 (requests, httpx)
파싱 엔진자체 파서 (관대함)html.parser / lxml / html5lib 선택 가능
SelectorCSS Selector 강력CSS Selector + find_all DSL
언어 진영JVM 표준Python 표준

기능적으로는 대체로 대등하다. 팀의 언어 스택을 따라가는 게 답이다. Spring Boot 프로젝트에 갑자기 Python 서비스를 붙일 이유는 없다.


브라우저 자동화 계열

세 도구(Selenium, Playwright, Puppeteer)는 진짜 브라우저를 원격 제어한다는 점에서 같다. 차이는 "어떻게 제어하느냐"에 있다.

공통 원리: 브라우저 제어 프로토콜

[내 코드]
   │ 명령 (예: goto, click, waitFor)
   ▼
[드라이버 / 프로토콜 계층]
   │ WebDriver Protocol 또는 Chrome DevTools Protocol(CDP)
   ▼
[실제 브라우저 프로세스 (Chrome/Firefox 등)]
   │ 페이지 로드, JS 실행, DOM 변경
   ▼
[내 코드] → DOM/스크린샷/네트워크 응답 회수

핵심은 브라우저를 프로세스로 띄운다는 점이다. 그래서 무겁고 느리다. 하지만 사람이 크롬 여는 것과 똑같이 JS까지 실행된 최종 화면을 볼 수 있다.

Selenium

  • 2004년부터 있어온 가장 오래된 표준.
  • WebDriver 프로토콜을 사용. W3C 표준.
  • 다국어 지원. Java 진영에서 첫 선택지가 이거였다.
  • 원래 뿌리는 E2E 테스트 자동화. 크롤링은 부차적.
WebDriver driver = new ChromeDriver();
driver.get("https://spa-example.com");
new WebDriverWait(driver, Duration.ofSeconds(10))
    .until(ExpectedConditions.presenceOfElementLocated(By.cssSelector(".loaded")));
 
List<WebElement> items = driver.findElements(By.cssSelector(".item"));

장점: 표준. 문서 풍부. 다양한 브라우저 지원. 단점: 상대적으로 느림. API가 오래되어 대기(wait) 처리가 번거로움. 최신 웹(Shadow DOM, iframe)에서 다루기 까다로움.

Playwright

  • MS가 2020년경 내놓은 후발주자. Selenium을 겨냥한 "다음 세대".
  • CDP(Chrome DevTools Protocol) 기반. Firefox·WebKit은 자체 패치 브라우저.
  • 자동 대기(auto-waiting): click()이 알아서 요소가 나타날 때까지 기다린다.
  • 네트워크 가로채기(intercept), 여러 컨텍스트 격리, 병렬 실행이 기본 지원.
  • Java 바인딩 있음.
try (Playwright pw = Playwright.create()) {
    Browser browser = pw.chromium().launch();
    Page page = browser.newPage();
    page.navigate("https://spa-example.com");
    page.waitForSelector(".loaded");
    List<ElementHandle> items = page.querySelectorAll(".item");
}

장점: Selenium보다 빠르고 안정적. API가 현대적. 안티봇 회피 옵션도 상대적으로 강함. 단점: 상대적으로 후발이라 몇몇 legacy 환경 지원이 부족. 브라우저 바이너리를 자체 관리.

Puppeteer

  • Google이 만든 Chrome 자동화 라이브러리. Node.js 전용(공식은).
  • CDP 기반. Playwright의 뿌리가 여기.
  • Chrome/Chromium에 특화. 파이어폭스도 실험적으로 지원.
const puppeteer = require('puppeteer');
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://spa-example.com');
await page.waitForSelector('.loaded');
const items = await page.$$('.item');

장점: Chrome과 완벽 궁합. 성능·안정성 좋음. 단점: Node.js 전용이라 JVM 백엔드에선 별도 워커를 두거나 Playwright로 우회해야 함.

셋 중에 뭘 골라야 하나 (Java/Spring Boot 관점)

  • 이미 Selenium을 쓰고 있으면 → 굳이 옮길 이유 없음
  • 새로 시작한다면 → Playwright(Java) 가 가장 현대적이고 안정적
  • Node.js 크롤러를 따로 붙일 수 있으면 → Puppeteer도 훌륭

실제 기업에서는 어떻게 선택하나

정리된 사례를 몇 가지 패턴으로 보면:

패턴 1: HTML 파서 우선 + 필요할 때만 브라우저

가장 흔한 실무 패턴이다.

  1. Jsoup으로 먼저 시도.
  2. 응답 HTML에 원하는 데이터가 없으면 (JS 렌더링 사이트) → Playwright/Selenium으로 fallback.
  3. 브라우저 자동화는 비싸므로 최후의 수단.

이유: 브라우저 자동화는 페이지당 처리 시간이 10~100배, 서버 자원(메모리, CPU)은 수십 배 든다. 만 개 페이지를 다 브라우저로 열면 서버가 죽는다.

패턴 2: 정적 사이트 대량 → HTML 파서만

뉴스 수집, 상품 카탈로그, 공공데이터 등. 사이트가 서버 렌더링이라면 Jsoup/BeautifulSoup만으로 하루 수백만 페이지도 처리 가능.

패턴 3: SPA 필수 → 브라우저 자동화만

React/Vue 기반의 최신 서비스, 로그인 후 세션 유지가 복잡한 사이트, 캡차·안티봇이 심한 사이트.

Playwright/Puppeteer로 처리하고, 인스턴스 풀·큐잉·크레딧 관리를 함께 설계한다. Kubernetes 위에서 Chromium Pod를 여러 개 띄우는 구조가 흔하다.

패턴 4: 하이브리드

  • 리스트 페이지는 Jsoup으로 빠르게 URL만 수집
  • 상세 페이지는 Playwright로 JS 렌더링 결과 파싱
  • 두 파이프라인을 큐로 연결

수집기 규모가 커지면 자연스럽게 이 형태로 간다.


예시 수집기에 적용한다면

지금 예시 수집기의 상태를 정리하면:

  • Scheduler(@Scheduled) → 10분마다 트리거
  • Spring Batch → Chunk 단위 처리, JobRepository로 이력 관리, 재시작 지원
  • 외부 API 호출 → JSON 저장 (완료)

여기에 크롤링을 붙일 때 자연스러운 구조는 이렇다.

[Batch Job: dataCollectionJob]
   ├── Step: apiCollectStep       ── 기존 (API 호출)
   ├── Step: jsoupCrawlStep       ── 신규 (정적 HTML 사이트)
   └── Step: browserCrawlStep     ── 신규 (JS 렌더링 사이트)
  • jsoupCrawlStep: Chunk 지향. Reader가 크롤 대상 URL 목록을 뱉고, Processor가 Jsoup으로 HTML 파싱, Writer가 JSON 저장.
  • browserCrawlStep: 필요할 때만. Playwright(Java)로 페이지 열어서 렌더링된 DOM 수집. Chunk size는 작게(2~5) 잡아 리소스 폭발 방지.

중요한 판단 포인트

  1. 새 사이트를 추가할 때 무조건 Jsoup으로 먼저 시도한다. 되면 그걸로 끝.
  2. Jsoup으로 원하는 값이 안 나오면 → 브라우저 개발자 도구의 Network 탭을 열어보자. JS가 뒤에서 호출하는 내부 JSON API가 보인다면 그 API를 직접 때리는 게 최고다 (=크롤링이 아니라 다시 API 호출).
  3. 그마저도 안 되는 진짜 SPA만 Playwright로 처리.

이 순서를 지키면 수집기의 90%는 Jsoup으로, 10%만 브라우저로 돌아간다.


정리

  • 크롤링 도구는 크게 HTML 파서 vs 브라우저 자동화로 갈린다.
  • JS 렌더링이 필요한가가 첫 번째이자 가장 중요한 갈림길.
  • Java/Spring Boot 백엔드라면: Jsoup을 기본, Playwright(Java) 를 fallback.
  • BeautifulSoup는 Python판 Jsoup, Puppeteer는 Node.js판 Playwright에 가깝다.
  • 브라우저 자동화는 비싼 도구다. 최후의 수단으로 미뤄라.
  • 수집기 아키텍처는 Jsoup Step + Browser Step을 분리해서 리소스 계획을 따로 잡는 것이 좋다.

다음 글에서는 Jsoup 내부를 뜯어본다. HTTP 요청부터 DOM Tree 생성, CSS Selector 검색이 어떻게 이루어지는지 단계별로 본다.