앞서 JSOUP 라이브러리를 이용한 스크래핑은 정적 페이지에서만 데이터를 가져올 수 있었다.
하지만, 데이터를 조회해와야 하는 페이지들이 대부분 동적 페이지라 JSOUP 라이브러리 말고 다른 방법을 찾아봐야 했다.
동적 페이지에서는 'selenium' 라이브러리를 이용하여 데이터를 가져온다고 한다.
그래서 'selenium' 라이브러리를 이용한 데이터 스크래핑을 해보도록 하겠다.
1. JSOUP과 selenium 비교
비교 항목 | JSOUP | Selenium |
주요 용도 | HTML 문서 파싱 및 데이터 추출에 사용. | 웹 애플리케이션 자동화 및 브라우저 상호작용 테스트. |
작동 방식 | 서버 측에서 HTML 소스만을 파싱. 자바스크립트 실행은 불가능. | 실제 브라우저를 띄워서 웹 페이지를 탐색하고 상호작용. 자바스크립트도 실행 가능. |
자바스크립트 지원 | 자바스크립트 실행을 지원하지 않음. | 자바스크립트를 포함한 모든 브라우저 동작을 실행 가능. |
속도 | 빠른 속도로 작동. 서버로부터 HTML만 가져오고 처리함. | 실제 브라우저를 실행하므로 상대적으로 느림. |
리소스 사용 | 상대적으로 가볍고 메모리와 CPU 사용량이 적음. | 브라우저를 구동하므로 메모리와 CPU 사용량이 많음. |
설정 복잡성 | 비교적 간단하게 설정 가능. HTML 파싱에 집중. | 브라우저 드라이버 설치 필요(예: ChromeDriver, GeckoDriver). |
브라우저 상호 작용 | HTML을 파싱하고 데이터 추출 가능, 클릭 등 사용자 인터랙션은 불가능. | 웹페이지 내에서 클릭, 스크롤, 폼 입력 등 실제 사용자 행동을 시뮬레이션. |
주요 사용 사례 | 정적인 웹페이지에서 데이터 스크래핑. | 자바스크립트 기반 웹페이지 탐색, 웹 애플리케이션 자동화, 테스팅. |
다중 페이지 탐색 | 특정 페이지를 파싱하고, 추가적으로 수동으로 요청하여 탐색해야 함. | 여러 페이지 간 이동 및 상호작용을 쉽게 구현 가능. |
2. selenium사용하기
2.1 selenium이란?
selenium에 대한 설명은 위의 표를 통해 알아보자.
2.2 selenium사용하기 - 예제
먼저, selenium을 사용하기 위해서는 라이브러리와 드라이버가 필요하다.
implementation 'org.seleniumhq.selenium:selenium-java:4.23.1'
웹 브라우저를 크롬으로 사용한다 가정하였을 때, 먼저 크롬 버전을 확인해주어야 한다.
크롬 버전은 설정-크롬 정보에서 확인할 수 있다.
크롬 버전을 확인하였다면 다음은 드라이버를 설치해주어야 한다.
다운로드 | ChromeDriver | Chrome for Developers
이 페이지는 Cloud Translation API를 통해 번역되었습니다. 다운로드 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 달리 명시되지 않는 한 이 페이지의 콘텐츠
developer.chrome.com
혹은 아래 링크에서 크롬 버전과 OS를 확인하여 다운로드 받을 수도 있다.
https://googlechromelabs.github.io/chrome-for-testing/
나의 경우에는 로컬 개발 환경에서는 윈도우를 사용중이기 때문에 window 드라이버를 설치해 주었다.
(테스트를 위해서 로컬에서 1차적으로 테스트 후, aws에서는 linux 드라이버를 다운로드 받아 테스트 해주었다.)
실제 해당 드라이버를 특정 경로에 복사해놓고, 테스트를 해보면 크롬 브라우저가 띄워지는 것을 확인할 수 있다.
웹 브라우저가 띄워지는 것을 끄기 위해 아래의 옵션과 웹 페이지 로드 타임을 추가해 주었다.
public WebDriver requestSelenium(String url) {
System.setProperty("webdriver.chrome.driver", WEB_DRIVER_PATH);
ChromeOptions options = new ChromeOptions();
options.addArguments("--headless");
WebDriver driver = new ChromeDriver(options);
driver.get(url);
driver.manage().timeouts().pageLoadTimeout(Duration.ofSeconds(15));
return driver;
}
selenium에서 By 클래스를 이용하여 아래와 같이 필요 데이터에 접근할 수 있다.
WebElement element = findFirstElement(driver, By.className("className1"));
List<WebElement> trTags = element.findElements(By.className("className2"));
2.3 selenium사용하기 - KBO 사이트
먼저 해당 월의 전체 게임을 받아오는 코드를 작성해보았다.
...
public List<Game> getGame() {
WebDriver driver = scrapingConfig.requestSelenium(gameUrl);
List<Game> gameList = new ArrayList<>();
try {
WebElement element = findFirstElement(driver, By.className("ScheduleLeagueType_match_list_container__1v4b0"));
List<WebElement> trTags = element.findElements(By.className("ScheduleLeagueType_match_list_group__18ML9"));
for (WebElement tr : trTags) {
String date = extractDate(tr.findElement(By.className("ScheduleLeagueType_title__2Kalm")).getText());
List<WebElement> games = tr.findElements(By.className("MatchBox_item_content__3SGZf"));
games.forEach(row -> gameList.add(createGame(row, date)));
}
} catch (Exception e) {
throw new IllegalStateException(스크래핑을 실행할 수 없습니다.);
} finally {
driver.quit();
}
return gameList;
}
...
데이터를 접근할때 생각보다 쉽게 접근할 수 없어서 초반에 애를 먹었지만, 작성된 코드로 한 번에 데이터를 모아올 수 있어 아주 편안했다.
위 코드를 통해 받아온 9월 전체 경기는 아래와 같이 DB에 저장되었다.
각 구단별 ID를 지정하여 홈팀/원정팀, 게임시작 시간 등을 받아와 저장하고, 다른 배치를 통해서는 주기마다 게임이 시작된 이후 점수나 상태값을 바꿔주는 배치를 만들어 두었다.
3. 마무리
개인 프로젝트에서 스크래핑 기능을 사용하게 되면서 JSOUP과 selenium 라이브러리를 처음 사용해 보았다.
사용해보지 않은 기능을 공부하고 만들어 사용해보는 것은 어려움도 있지만 만들어진 결과물과 그 과정을 다시 돌이켜 보면 재밌다는 느낌을 더 주는 것 같다.
이렇게 스크래핑을 통해 웹 데이터를 쉽고 간단하게 가져올 수 있었지만 그만큼 그 사이트의 HTML 구조에 의존적이라 코드가 변경된다면 정상적으로 데이터를 가져오지 못한다는 아주 큰 단점이 있는 것 같다.
만약, 페이지가 변경될 때 상호 협의 하에 작업이 이루어진다면 바로 대처가 가능하기 때문에 문제가 없겠지만, 나와 같이 일반적으로 데이터를 가져다 쓰는 경우에는 오류가 발생한 이후에나 대처가 가능하기 때문에 이 점에 대해서도 다시 한 번 고민해보면 좋을 것 같다.
참고사이트:
ChatGPT