ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [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를 활용하여, 여행지의 좌표를 얻어내는 작업이 필요할 거 같다.

     

    다음엔 그 부분을 포스팅 해보도록 하자!  

Designed by Tistory.