Jsoup은 Java용 HTML 파싱 라이브러리입니다. jQuery와 유사한 CSS 선택자 문법을 지원하며, HTML에서 데이터를 추출하거나 수정하는 데 사용됩니다.

1. 의존성 추가

Gradle

implementation 'org.jsoup:jsoup:1.17.2'

참고: Jsoup Download

2. HTML 입력

문자열에서 파싱

String html = "<html><head><title>First parse</title></head>"
    + "<body><p>Parsed HTML into a doc.</p></body></html>";
Document doc = Jsoup.parse(html);

Base URI와 함께 파싱

Document doc = Jsoup.parse(html, "http://example.com/");
// 상대 URL을 절대 URL로 변환할 때 사용

URL에서 가져오기 (GET)

Document doc = Jsoup.connect("http://en.wikipedia.org/").get();

POST 요청

Document doc = Jsoup.connect("http://example.com")
    .data("query", "Java")
    .userAgent("Mozilla")
    .cookie("auth", "token")
    .timeout(3000)
    .post();

JSON 가져오기

String json = Jsoup.connect("https://api.example.com/data")
    .ignoreContentType(true)
    .execute()
    .body();

3. 데이터 추출

DOM 메서드

Element content = doc.getElementById("content");
Elements links = content.getElementsByTag("a");

for (Element link : links) {
    String linkHref = link.attr("href");
    String linkText = link.text();
}

CSS 선택자

// a 태그 중 href 속성이 있는 것
Elements links = doc.select("a[href]");

// src가 .png로 끝나는 img
Elements pngs = doc.select("img[src$=.png]");

// class가 masthead인 div
Element masthead = doc.select("div.masthead").first();

// h3.r 바로 다음의 a
Elements resultLinks = doc.select("h3.r > a");

// 복합 선택자
Elements newsHeadlines = doc.select("#mp-itn b a");

참고: Jsoup Selector Syntax

데이터 추출 예제

String html = "<p>An <a href='http://example.com/'><b>example</b></a> link.</p>";
Document doc = Jsoup.parse(html);
Element link = doc.select("a").first();

String text = doc.body().text();           // "An example link"
String linkHref = link.attr("href");       // "http://example.com/"
String linkText = link.text();             // "example"

String linkOuterH = link.outerHtml();      // "<a href="http://example.com"><b>example</b></a>"
String linkInnerH = link.html();           // "<b>example</b>"

절대 URL 가져오기

Document doc = Jsoup.connect("http://jsoup.org").get();
Element link = doc.select("a").first();

String relHref = link.attr("href");        // "/"
String absHref = link.attr("abs:href");    // "http://jsoup.org/"

// 또는
String absUrl = link.absUrl("href");       // "http://jsoup.org/"

4. HTML 수정

속성 일괄 변경

doc.select("div.comments a").attr("rel", "nofollow");

HTML 내용 변경

Element div = doc.select("div").first();    // <div></div>
div.html("<p>lorem ipsum</p>");             // <div><p>lorem ipsum</p></div>
div.prepend("<p>First</p>");
div.append("<p>Last</p>");
// 결과: <div><p>First</p><p>lorem ipsum</p><p>Last</p></div>

요소 감싸기

Element span = doc.select("span").first();  // <span>One</span>
span.wrap("<li><a href='http://example.com/'></a></li>");
// 결과: <li><a href="http://example.com"><span>One</span></a></li>

텍스트 수정

div.text("five > four");    // <div>five &gt; four</div>
div.prepend("First ");
div.append(" Last");
// 결과: <div>First five &gt; four Last</div>

5. 에러 처리

403 에러 해결

User-Agent 헤더 추가:

return Jsoup.connect("http://example.com/search?q=test")
    .userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36")
    .get()
    .select(".result")
    .first()
    .text();

HTTPS 인증서 에러

필요시 SSL 검증 우회 (개발 환경에서만 사용):

Jsoup.connect(url)
    .sslSocketFactory(trustAllCerts())
    .get();

6. XSS 공격 방지

사용자 입력 HTML을 정제하여 XSS 공격을 방지합니다:

String unsafe = "<p><a href='http://example.com/' onclick='stealCookies()'>Link</a></p>";
String safe = Jsoup.clean(unsafe, Whitelist.basic());
// 결과: <p><a href="http://example.com/" rel="nofollow">Link</a></p>

왜 필요한가?

피드백 입력, 질문 입력, ID/PW 입력 등 모든 입력창은 잠재적으로 스크립트 공격 가능성이 있습니다.

예: 사용자가 피드백에 스크립트를 작성하면, CMS에서 피드백을 확인할 때 스크립트가 실행되어 CMS의 정보가 유출될 수 있습니다.

Whitelist 종류

  • Whitelist.none(): 모든 태그 제거, 텍스트만 유지
  • Whitelist.simpleText(): b, em, i, strong, u 태그만 허용
  • Whitelist.basic(): 기본 포맷팅 태그 + 링크 허용
  • Whitelist.basicWithImages(): basic + img 태그 허용
  • Whitelist.relaxed(): 대부분의 안전한 태그 허용

참고 자료