개발 블로그
← 블로그 목록

Jsoup으로 웹 크롤링하기 — Java HTML 파싱 실전 가이드


Java에서 웹 크롤링을 구현할 때 가장 많이 쓰이는 라이브러리가 Jsoup입니다. HTTP 요청부터 HTML 파싱, CSS 셀렉터 기반 데이터 추출까지 한 라이브러리로 처리할 수 있습니다. 유로 환율 알림 프로젝트에서 실제로 사용한 내용을 정리합니다.

의존성 추가

// build.gradle
implementation 'org.jsoup:jsoup:1.17.2'

기본 사용법

Jsoup.connect()로 URL에 요청을 보내고 Document 객체를 받아옵니다. 이후 CSS 셀렉터로 원하는 요소를 추출합니다.

Document doc = Jsoup.connect("https://example.com")
        .userAgent("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)")
        .timeout(5000)
        .get();

// CSS 셀렉터로 요소 추출
String title = doc.select("h1.title").text();
Elements items = doc.select("ul.list > li");
for (Element item : items) {
    System.out.println(item.text());
}

환율 크롤링 실제 예시

네이버에서 유로 환율을 크롤링하는 코드입니다. 개발자 도구로 환율 숫자가 담긴 HTML 요소의 클래스명을 확인하고 셀렉터를 작성했습니다.

@Service
public class ExchangeRateCrawler {

    public BigDecimal fetchEuroRate() {
        try {
            Document doc = Jsoup.connect("https://finance.naver.com/marketindex/")
                    .userAgent("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) "
                             + "AppleWebKit/537.36 (KHTML, like Gecko) "
                             + "Chrome/120.0.0.0 Safari/537.36")
                    .timeout(5000)
                    .get();

            // 환율 요소 선택 후 텍스트 추출
            String rateText = doc.select("div.환율셀렉터 span.value")
                    .first()
                    .text()
                    .replace(",", "");

            return new BigDecimal(rateText);

        } catch (IOException e) {
            log.error("환율 크롤링 실패", e);
            throw new RuntimeException("환율 조회 실패", e);
        }
    }
}

자주 쓰는 셀렉터 패턴

// 클래스로 선택
doc.select(".className")

// ID로 선택
doc.select("#elementId")

// 태그 + 클래스 조합
doc.select("span.price")

// 자식 요소
doc.select("ul > li")

// 속성값으로 선택
doc.select("a[href*=finance]")  // href에 finance 포함

// 첫 번째 요소만
doc.select("div.list").first()

// 텍스트 추출
element.text()          // 자식 포함 전체 텍스트
element.ownText()       // 직접 텍스트만

// 속성값 추출
element.attr("href")
element.attr("src")

User-Agent 설정이 중요한 이유

User-Agent 없이 요청을 보내면 많은 사이트가 403 오류를 반환하거나 빈 응답을 줍니다. Jsoup의 기본 User-Agent는 크롤러로 식별되기 때문에 실제 브라우저 User-Agent를 사용하는 것이 중요합니다.

Jsoup.connect(url)
    .userAgent("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) "
             + "AppleWebKit/537.36 (KHTML, like Gecko) "
             + "Chrome/120.0.0.0 Safari/537.36")
    .header("Accept-Language", "ko-KR,ko;q=0.9")
    .timeout(5000)
    .get();

스케줄러와 조합하기

환율 알림 프로젝트에서는 1분마다 크롤링하고, 이전 값과 비교해서 10원 이상 차이가 나면 카카오톡을 전송했습니다.

@Scheduled(fixedDelay = 60000)  // 1분마다
public void checkExchangeRate() {
    BigDecimal current = crawler.fetchEuroRate();
    String prevStr = redis.get("euro:rate");

    if (prevStr != null) {
        BigDecimal prev = new BigDecimal(prevStr);
        BigDecimal diff = current.subtract(prev).abs();

        if (diff.compareTo(new BigDecimal("10")) >= 0) {
            kakaoService.sendMessage(
                "유로 환율 변동: " + prev + " → " + current
            );
        }
    }

    redis.set("euro:rate", current.toPlainString());
}

주의사항

  • 사이트 정책 확인: robots.txt와 이용약관을 먼저 확인하세요. 크롤링을 명시적으로 금지한 사이트는 접근하면 안 됩니다.
  • 요청 간격: 과도한 요청은 서버에 부하를 주고 IP가 차단될 수 있습니다. 적절한 딜레이를 두세요.
  • HTML 구조 변경: 사이트의 HTML이 바뀌면 셀렉터가 동작하지 않습니다. 크롤링 실패 시 알림을 받을 수 있도록 에러 핸들링을 꼼꼼하게 해두는 것이 좋습니다.
  • SPA 대응: JavaScript로 동적으로 렌더링하는 사이트는 Jsoup으로 데이터를 가져올 수 없습니다. 이 경우 Selenium이나 Playwright 같은 도구가 필요합니다.