-
[Java/Spring] Selenium으로 데이터 크롤링하기Java, Spring 2024. 3. 14. 14:12
현재 진행 중인 프로젝트는 수도권 집중현상 완화를 위한 비인기 여행지 추천 플랫폼 개발이다.
위 프로젝트 진행 중 비인기 여행지의 정보를 얻어오기 위해 하국관광 데이터랩에서 제공하는
데이터들을 수집하여 데이터 베이스에 저장하기로 하였다.
우선 인구 소멸 위험지수가 0.5 이하인 지역들을 선정하고 위 지역들을 기반으로 한국관광 데이터랩에서 데이터를 검색하여 해당 관광지의 검색 건 수와 위치정보를 데이터베이스에 저장하고자 하였다.
물론 수작업을 일일히 할 수는 있지만, 이런 수작업을 줄이기 위해 프로그래밍이 존재하지 않겠는가!
그럼 바로 시작해보자!
로컬에서 진행하였고, 인구 소멸 위험지역의 여행지 정보를 저장하는 것이 목적이기에, 따로 클래스 분리는 하지 않았다.
아래 코드를 확인 해 보자.
public class Crawling { public static void main(String[] args) { // WebDriver와 ChromeDriver 설정 System.setProperty("webdriver.chrome.driver", "C:/Users/voslr/study/weather-crawling/src/main/java/liveinsoha/crawling/driver/chromedriver.exe"); WebDriver driver = new ChromeDriver(); // 웹 페이지 접속 String baseUrl = "https://datalab.visitkorea.or.kr/datalab/portal/bda/getTourVisitCnt.do"; try { driver.get(baseUrl); // 대기 시간을 줘서 로그인 페이지가 로드될 때까지 기다립니다. waitFor(3000); // 3초 대기 // 확인을 누르기 위해 경고창을 찾습니다. handleAlert(driver); // 다시 로그인 폼 입력 login(driver); List<String> areas = new ArrayList<>(); areas.add("경기도"); areas.add("강원특별자치도"); areas.add("충청북도"); areas.add("충청남도"); areas.add("전북특별자치도"); areas.add("전라남도"); areas.add("경상북도"); areas.add("경상남도"); areas.add("인천광역시"); areas.add("부산광역시"); areas.add("대구광역시"); for (String area : areas) { List<String> cities = getCities(area); for (String city : cities) { //페이지 새로고침 넣기 driver.navigate().refresh(); waitFor(500); System.out.println("city = " + city); selectArea(driver, area, city); confirmButton(driver); waitFor(1000); downloadButton(driver); // 학술 연구 및 과제수행 라디오 버튼 클릭 WebElement researchRadioButton = driver.findElement(By.xpath("//input[@value='5' and @id='rdoDataUtilExmn5']")); researchRadioButton.click(); // 대기 시간을 줘서 제출 버튼이 로드될 때까지 기다립니다. waitFor(1000); // 2초 대기 // 제출 버튼 클릭 submitButton(driver); } waitFor(500); } // 이후의 작업 수행 // ... } catch (UnhandledAlertException e) { // 예외 처리: 알림 처리 // handleAlert(driver); // 여기서 다른 작업 수행하거나 예외를 다시 던지거나 처리 // 예를 들어, 다시 로그인 시도 등을 수행할 수 있음 } catch (InterruptedException e) { e.printStackTrace(); } finally { // WebDriver 종료 // driver.quit(); } } private static void waitFor(int milliseconds) throws InterruptedException { Thread.sleep(milliseconds); } private static void handleAlert(WebDriver driver) { // 확인을 누르기 위해 경고창을 찾습니다. Alert alert = driver.switchTo().alert(); alert.accept(); // 경고창 확인 클릭 } private static void login(WebDriver driver) { WebElement loginIdInput = driver.findElement(By.id("mbrId")); loginIdInput.sendKeys("email@naver.com"); WebElement loginPwInput = driver.findElement(By.id("mbrPw")); loginPwInput.sendKeys("password"); // 로그인 버튼 클릭 WebElement loginButton = driver.findElement(By.xpath("//input[@value='로그인']")); loginButton.click(); // 대기 시간을 줘서 로그인이 완료될 때까지 기다립니다. try { waitFor(3000); // 3초 대기 } catch (InterruptedException e) { e.printStackTrace(); } } private static void selectArea(WebDriver driver, String area, String city) { // 전국 버튼 클릭하여 지역 선택 창 띄우기 WebElement areaSelectButton = driver.findElement(By.id("area-select")); areaSelectButton.click(); // 대기 시간을 줘서 지역 선택 창이 로드될 때까지 기다립니다. try { waitFor(1000); // 2초 대기 } catch (InterruptedException e) { e.printStackTrace(); } // 지역 선택 WebElement areaButton = driver.findElement(By.xpath("//a[text()='" + area + "']")); areaButton.click(); // 대기 시간을 줘서 해당 지역의 도시가 로드될 때까지 기다립니다. try { waitFor(1000); // 2초 대기 } catch (InterruptedException e) { e.printStackTrace(); } // 도시 선택 WebElement cityButton = driver.findElement(By.xpath("//a[text()='" + city + "']")); cityButton.click(); // 대기 시간을 줘서 Confirm 버튼이 로드될 때까지 기다립니다. try { waitFor(1000); // 1초 대기 } catch (InterruptedException e) { e.printStackTrace(); } } private static void confirmButton(WebDriver driver) { WebElement confirmButton = driver.findElement(By.xpath("//a[@class='button bg-blue modal-close' and contains(text(), '확인')]")); confirmButton.click(); } private static void downloadButton(WebDriver driver) { // 다운로드 버튼 클릭 WebElement downloadButton = driver.findElement(By.xpath("//a[contains(text(),'다운로드')]")); downloadButton.click(); } private static void submitButton(WebDriver driver) { // 제출 버튼 클릭 WebElement submitButton = driver.findElement(By.xpath("//a[@id='submit' and contains(@class, 'bg-blue') and contains(@class, 'focus')]")); submitButton.click(); } private static List<String> getCities(String area) { List<String> cities = new ArrayList<>(); switch (area) { case "강원특별자치도": cities.add("강릉시"); cities.add("동해시"); cities.add("삼척시"); cities.add("속초시"); cities.add("태백시"); cities.add("철원군"); cities.add("화천군"); cities.add("양구군"); cities.add("인제군"); cities.add("고성군"); cities.add("홍천군"); break; case "충청북도": cities.add("충주시"); cities.add("제천시"); cities.add("음성군"); cities.add("증평군"); break; case "충청남도": cities.add("공주시"); cities.add("논산시"); cities.add("서산시"); cities.add("보령시"); cities.add("당진시"); cities.add("홍성군"); cities.add("예산군"); break; case "전북특별자치도": cities.add("군산시"); cities.add("익산시"); cities.add("김제시"); cities.add("정읍시"); cities.add("남원시"); cities.add("완주군"); break; case "전라남도": cities.add("여수시"); cities.add("나주시"); cities.add("무안군"); cities.add("영암군"); cities.add("화순군"); cities.add("장성군"); cities.add("담양군"); cities.add("영광군"); break; case "경상북도": cities.add("포항시"); cities.add("경주시"); cities.add("영천시"); cities.add("김천시"); cities.add("문경시"); cities.add("안동시"); cities.add("영주시"); cities.add("칠곡군"); cities.add("예천군"); cities.add("울진군"); cities.add("울릉군"); break; case "경상남도": cities.add("밀양시"); cities.add("사천시"); cities.add("통영시"); cities.add("함안군"); cities.add("거창군"); break; case "인천광역시": cities.add("동구"); cities.add("옹진군"); break; case "부산광역시": cities.add("중구"); cities.add("동구"); cities.add("서구"); cities.add("남구"); cities.add("영도구"); cities.add("금정구"); cities.add("사하구"); break; case "대구광역시": cities.add("서구"); cities.add("남구"); break; // 이하 지역에 따른 도시 추가... default: break; } return cities; } }
위와 같이 자바 셀레니움 라이블리와 크롬드라이버를 활용하여 코드를 실행시키면, 코드가 브라우저를 자동으로 실행하고 스스로 인구 소멸 위험지역을 선택한 후 해당하는 데이터를 다운 받는 것을 확인할 수 있다.
이 과정에서 주의할 점은, 로그인을 진행해야 하므로 로그인하는 과정도 코드로 작성하였고, 어쨌든 위 과정은 웹브라우저 상에서 진행되기에, 데이터가 로드되는 시간을 부여해야했다. 이 과정은 스레드를 잠 재우는 Thread.sleep으로 시간을 벌어주었다.
위와 같은 과정을 거치고, 모든 인구 소멸 위험지역에 해당하는 비인기 여행지 정보를 얻었다.
이렇게 다운 받은 csv파일을 기반으로, 데이터베이스에 저장하고, 일정 기준의 검색 건 수에 해당하는 여행지들을 비인기 여행지로 최종 선정할 것이다.
또한 위에서 얻은 csv파일에는 해당 여행지의 도로명 주소도 포함되어 있으므로, 해당 여행지의 도로명 주소를 기반으로 카카오 맵 api를 활용하여, 여행지의 좌표를 얻어내는 작업이 필요할 거 같다.
다음엔 그 부분을 포스팅 해보도록 하자!
'Java, Spring' 카테고리의 다른 글
[Java/Spring] 여행지의 인스타그램 해시태그 수 크롤링 (1) 2024.03.15 [Java/Spring] Selenium과 Kakao Map API를 활용하여 좌표얻기 (2) 2024.03.14 [Java/Spring] csv파일 Parsing하여 데이터베이스에 저장하기 (0) 2024.03.14 Spring Security와 소셜 로그인 (0) 2024.03.09 HTTP 요청 메시지를 통해 클라이언트에서 서버로 데이터를 전달하는 방법 (1) 2024.01.26