HtmlUnit은 HTML 페이지를 모델링할 수 있는 헤드리스 브라우저입니다. 프로그래밍 방식으로 페이지를 모델링한 후, 양식 작성, 제출, 페이지 간 이동 등의 작업을 수행하여 상호작용할 수 있습니다. 웹 스크래핑을 통해 데이터를 추출하여 후속 처리를 하거나, 프로그램이 예상대로 웹 페이지를 생성하는지 확인하는 자동화된 테스트를 만드는 데 활용할 수 있습니다.
HtmlUnit을 이용한 웹 스크래핑
HtmlUnit과 Gradle을 활용한 웹 스크래핑 구현에는 IntelliJ IDEA IDE를 사용하지만, 선호하는 다른 IDE나 코드 편집기를 사용해도 됩니다.
IntelliJ는 Gradle과의 완벽한 통합을 지원하며 JetBrains 사이트에서 다운로드할 수 있습니다. Gradle은 애플리케이션 빌드 및 패키지 생성을 지원하는 빌드 자동화 도구입니다. 또한 의존성을 원활하게 추가하고 관리할 수 있게 해줍니다. 최신 IntelliJ IDEA 버전에서는 Gradle과 Gradle 확장 기능이 기본적으로 설치 및 활성화되어 있습니다.
이 튜토리얼의 모든 코드는 이 GitHub 저장소에서 확인할 수 있습니다.
Gradle 프로젝트 생성
IntelliJ IDE에서 새 Gradle 프로젝트를 생성하려면 메뉴 옵션에서 파일 > 새로 만들기 > 프로젝트를 선택하면 새 프로젝트 마법사가 열립니다. 프로젝트 이름을 입력하고 원하는 위치를 선택하세요:

HtmlUnit을 사용하여 Java로 웹 스크래핑 애플리케이션을 만들 예정이므로 Java 언어를 선택해야 합니다. 또한 Gradle 빌드 시스템을 선택합니다. 그런 다음 생성을 클릭합니다. 이렇게 하면 기본 구조와 필요한 모든 파일을 갖춘 Gradle 프로젝트가 생성됩니다. 예를 들어, build.gradle 파일에는 이 프로젝트를 빌드하는 데 필요한 모든 종속성이 포함되어 있습니다:

HtmlUnit 설치
의존성으로 HtmlUnit을 설치하려면 보기 > 도구 창 > 의존성을 선택하여 의존성 창을 엽니다.
그런 다음 “htmlunit”을 검색하고 추가를 선택합니다:

이제 build.gradle 파일의 종속성 섹션에서 HtmlUnit이 설치된 것을 확인할 수 있습니다:

이제 HtmlUnit을 설치했으므로 정적 및 동적 웹 페이지에서 데이터를 스크래핑할 차례입니다.
정적 페이지 스크래핑
이 섹션에서는 정적 웹 페이지인 HtmlUnit 위키를 스크래핑하는 방법을 배웁니다. 이 웹 페이지에는 제목, 목차, 소제목 목록, 각 소제목의 콘텐츠와 같은 요소가 포함되어 있습니다.
HTML 웹 페이지의 각 요소에는 속성이 있습니다. 예를 들어 ID는 전체 HTML 문서에서 요소를 고유하게 식별하는 속성이고, Name은 해당 요소를 식별하는 속성입니다. Name 속성은 고유하지 않으며 HTML 문서 내 여러 요소가 동일한 이름을 가질 수 있습니다. 웹 페이지의 요소는 이러한 속성 중 어느 것을 사용해서든 식별할 수 있습니다.
또는 XPath를 사용하여 요소를 식별할 수도 있습니다. XPath는 경로와 유사한 구문을 사용하여 웹 페이지 HTML 내의 요소를 식별하고 탐색합니다.
다음 예제에서는 HTML 페이지의 요소를 식별하기 위해 이 두 가지 방법을 모두 사용할 것입니다.
웹 페이지를 스크래핑하려면 HtmlUnit WebClient를 생성해야 합니다. WebClient는 Java 애플리케이션 내부의 브라우저를 나타냅니다. WebClient 초기화는 웹 페이지를 보기 위해 브라우저를 실행하는 것과 유사합니다.
WebClient를 초기화하려면 다음 코드를 사용하십시오:
WebClient webClient = new WebClient(BrowserVersion.CHROME);
이 코드는 Chrome 브라우저를 초기화합니다. 다른 브라우저도 지원됩니다.
웹 클라이언트 객체에서 제공되는 getPage() 메서드를 사용하여 웹 페이지를 가져올 수 있습니다. 웹 페이지를 확보한 후에는 다양한 메서드를 사용하여 웹 페이지에서 데이터를 스크래핑할 수 있습니다.
페이지 제목을 가져오려면 다음 코드에서 보여준 것처럼 getTitleText() 메서드를 사용하세요:
String webPageURl = "https://en.wikipedia.org/wiki/HtmlUnit";
try {
HtmlPage page = webClient.getPage(webPageURl);
System.out.println(page.getTitleText());
} catch (FailingHttpStatusCodeException | IOException e) {
e.printStackTrace();
}
그러면 페이지 제목이 출력됩니다:
HtmlUnit - Wikipedia
한 단계 더 나아가 웹 페이지에 있는 모든 H2 요소를 가져오겠습니다. 이 경우 H2 요소는 페이지의 두 섹션에 있습니다:
- 왼쪽 사이드바의 콘텐츠 표시 영역: 보시다시피, ‘Contents’ 섹션의 제목은 H2 요소입니다.
- 페이지 본문의 경우: 모든 소제목이 H2 요소입니다.

본문의 모든 H2를 가져오려면 H2 요소의 XPath를 사용할 수 있습니다. XPath를 찾으려면 H2 요소를 마우스 오른쪽 버튼으로 클릭하고 ‘검사’를 선택하세요. 그런 다음 강조 표시된 요소를 마우스 오른쪽 버튼으로 클릭하고 ‘복사’ > ‘전체 XPath 복사’를 선택하세요:

이렇게 하면 XPath가 클립보드에 복사됩니다. 예를 들어, 본문 콘텐츠의 H2 요소 XPath는 /html/body/div[1]/div/div[3]/main/div[2]/div[3]/div[1]/h2입니다.
XPath를 사용하여 모든 H2 요소를 가져오려면 getByXpath() 메서드를 사용할 수 있습니다:
String xPath = "/html/body/div[1]/div/div[3]/main/div[2]/div[3]/div[1]/h2";
String webPageURL = "https://en.wikipedia.org/wiki/HtmlUnit";
try {
HtmlPage page = webClient.getPage(webPageURL);
// XPath를 사용하여 모든 헤딩 가져오기
List<HtmlHeading2> h2 = (List<HtmlHeading2>)(Object) page.getByXPath(xPath);
// 첫 번째 헤딩 텍스트 콘텐츠 출력
System.out.println((h2.get(0)).getTextContent());
} catch (FailingHttpStatusCodeException | IOException e) {
e.printStackTrace();
}
첫 번째 H2 요소의 텍스트 콘텐츠는 다음과 같이 출력됩니다:
장점[편집]
마찬가지로 getElementById() 메서드를 사용해 ID로 요소를 가져올 수 있으며,getElementByName() 메서드를 사용해 이름으로 요소를 가져올 수 있습니다.
다음 섹션에서는 이러한 메서드를 사용하여 동적 웹 페이지를 스크래핑할 것입니다.
HtmlUnit을 사용한 동적 웹 페이지 스크래핑
이 섹션에서는 로그인 양식을 작성하고 제출함으로써 HtmlUnit의 양식 작성 및 버튼 클릭 기능을 배웁니다. 또한 헤드리스 브라우저를 사용하여 웹 페이지를 탐색하는 방법도 배웁니다.
동적 웹 스크래핑을 시연하기 위해 Hacker News 웹사이트를 사용해 보겠습니다. 로그인 페이지의 모습은 다음과 같습니다:

다음 코드는 이전 페이지의 HTML 양식 코드입니다. 로그인 레이블을 마우스 오른쪽 버튼으로 클릭하고 ‘검사’를 클릭하면 이 코드를 확인할 수 있습니다:
<form action="login" method="post">
<input type="hidden" name="goto" value="news">
<table border="0">
<tbody>
<tr><td>사용자 이름:</td><td><input type="text" name="acct" size="20" autocorrect="off" spellcheck="false" autocapitalize="off" autofocus="true"></td></tr>
<tr><td>비밀번호:</td><td><input type="password" name="pw" size="20"></td></tr></tbody></table><br>
<input type="submit" value="로그인"></form>
HtmlUnit을 사용하여 양식을 작성하려면 webClient 객체를 사용하여 웹 페이지를 가져옵니다. 이 페이지에는 로그인 및 계정 생성이라는 두 개의 양식이 포함되어 있습니다. getForms().get(0) 메서드를 사용하여 로그인 양식을 가져올 수 있습니다. 또는 양식에 고유한 이름이 있는 경우 getFormByName() 메서드를 사용할 수도 있습니다.
다음으로 getInputByName() 메서드와 name 속성을 사용하여 양식 입력값(즉, 사용자 이름 및 비밀번호 필드)을 가져옵니다.
setValueAttribute() 메서드를 사용하여 입력 필드의 사용자 이름과 비밀번호 값을 설정하고, getInputByValue() 메서드를 사용하여 제출 버튼을 가져옵니다. click() 메서드를 사용하여 버튼을 클릭할 수도 있습니다.
버튼 클릭 후 로그인이 성공하면 제출 버튼의 대상 페이지가 HTMLPage 객체로 반환되며, 이를 추가 작업에 활용할 수 있습니다.
다음 코드는 양식을 가져와 작성하고 제출하는 방법을 보여줍니다:
HtmlPage page = null;
String webPageURl = "https://en.wikipedia.org/wiki/HtmlUnit";
try {
// 첫 페이지 가져오기
HtmlPage signUpPage = webClient.getPage(webPageURL);
// 인덱스를 사용하여 양식 가져오기. 0은 첫 번째 양식을 반환합니다.
HtmlForm form = signUpPage.getForms().get(0);
// 이름으로 사용자 이름 및 비밀번호 필드 가져오기
HtmlTextInput userField = form.getInputByName("acct");
HtmlInput pwField = form.getInputByName("pw");
// 해당 필드에 사용자 이름과 비밀번호 설정
userField.setValueAttribute("draftdemoacct");
pwField.setValueAttribute("test@12345");
// 값을 사용하여 제출 버튼 가져오기
HtmlSubmitInput submitButton = form.getInputByValue("login");
//제출 버튼 클릭 시 해당 버튼의 대상 페이지로 이동
page = submitButton.click();
} catch (FailingHttpStatusCodeException | IOException e) {
e.printStackTrace();
}
양식이 제출되고 로그인이 성공하면 사용자의 홈 페이지로 이동하게 되며, 오른쪽 모서리에 사용자 이름이 표시됩니다:

사용자 이름 요소의 ID는 “me”입니다. 다음 코드에서 보여준 것처럼 getElementById() 메서드를 사용하여 ID “me”를 전달하면 사용자 이름을 얻을 수 있습니다:
System.out.println(page.getElementById("me").getTextContent());
웹 페이지의 사용자 이름이 추출되어 출력됩니다:
draftdemoacct
다음으로, 페이지 하단의 ‘더 보기’ 하이퍼링크 버튼을 클릭하여 Hacker News 사이트의 두 번째 페이지로 이동해야 합니다:

더 보기 버튼 객체를 얻으려면, 검사(Inspect ) 옵션을 사용하여 더 보기 버튼의 XPath를 확인하고 인덱스 0을 사용하여 첫 번째 링크 객체를 가져옵니다:

click() 메서드를 사용하여 ‘더 보기’ 링크를 클릭합니다. 링크가 클릭되고 링크의 대상 페이지가 HtmlPage 객체로 반환됩니다:
HtmlPage nextPage = null;
try {
List<HtmlAnchor> links = (List<HtmlAnchor>)(Object)page.getByXPath("html/body/center/table/tbody/tr[3]/td/table/tbody/tr[92]/td[2]/a");
HtmlAnchor anchor = links.get(0);
nextPage = anchor.click();
} catch (IOException e) {
throw new RuntimeException(e);
}
이 시점에서 HtmlPage 객체에 두 번째 페이지가 포함되어 있어야 합니다.
두 번째 페이지가 성공적으로 로드되었는지 확인하려면 HtmlPage의 URL을 출력할 수 있습니다:
System.out.println(nextPage.getUrl().toString());
다음은 두 번째 페이지의 URL입니다:
https://news.ycombinator.com/news?p=2
Hacker News 사이트의 각 페이지에는 30개의 항목이 포함됩니다. 따라서 두 번째 페이지의 항목은 일련번호 31부터 시작합니다.
두 번째 페이지의 첫 번째 항목 ID를 가져와 31과 일치하는지 확인해 보겠습니다. 이전과 마찬가지로
검사( Inspect ) 옵션을 사용하여 첫 번째 항목의 XPath를 가져옵니다. 그런 다음 목록에서 첫 번째 항목을 가져와 텍스트 내용을 표시합니다:
String firstItemId = null;
List<Object> entries = nextPage.getByXPath("/html/body/center/table/tbody/tr[3]/td/table/tbody/tr[1]/td[1]/span");
HtmlSpan span = (HtmlSpan) (entries.get(0));
firstItemId = span.getTextContent();
System.out.println(firstItemId);
첫 번째 항목의 ID가 표시됩니다:
31.
이 코드는 HtmlUnit을 사용하여 양식을 작성하고, 버튼을 클릭하며, 웹 페이지를 탐색하는 방법을 보여줍니다.
결론
이 글에서는 HtmlUnit을 사용하여 정적 및 동적 웹사이트를 스크래핑하는 방법을 배웠습니다. 또한 웹 페이지를 스크래핑하고 이를 구조화된 데이터로 변환하는 HtmlUnit의 고급 기능에 대해서도 알아보았습니다.
IntelliJ IDEA와 같은 IDE에서 이 작업을 수행할 때는 요소를 수동으로 검사하여 속성을 찾고, 해당 속성을 사용해 스크래핑 함수를 처음부터 작성해야 합니다. 반면 Bright Data의서버리스 함수는 강력한 차단 방지 프록시 인프라, 편리한 스크래핑 함수, 인기 웹사이트용 코드 템플릿을 제공합니다. IP 차단 및 속도 제한 문제 없이 웹 페이지를 스크래핑하려면 효율적인 프록시 인프라가 필수적입니다. 프록시 사용은 다른 지리적 위치의 사용자를 모방하는 데에도 도움이 됩니다.
Bright Data 전문가와 상담하여 비즈니스에 적합한 솔루션을 찾거나, 간단히 등록하여 이용 가능한 제품을 살펴보세요.