ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Selenium을 이용한 크롤링
    개발로그/오늘뭐먹지 프로젝트 2022. 12. 23. 18:53

    0. 크롤링 이유 

     

    현재 만들고 있는 앱에 현재 위치와  DB를 기반으로 식사를 추천해주고, 추천된 데이터를 HTML로 뿌려주는 항목이 있었다. 뿌려주기를 원하는 데이터는 식당의 이름, 위치, 메뉴, 리뷰, 이미지 등이 있었는데 문제가 하나 있었다.

     

    https://www.data.go.kr/

     

    공공데이터 포털

    국가에서 보유하고 있는 다양한 데이터를『공공데이터의 제공 및 이용 활성화에 관한 법률(제11956호)』에 따라 개방하여 국민들이 보다 쉽고 용이하게 공유•활용할 수 있도록 공공데이터(Datase

    www.data.go.kr

     

     DB를 채워넣을때 위의 공공 데이터 포털을 이용해 데이터를 채워넣었는데 해당 사이트가 제공하는 데이터에는 식당의 메뉴, 리뷰, 이미지등이 포함되어있지 않다는 문제가 있었다. 이를 해결하기 위해 카카오맵이나 네이버 맵 둘 중 하나를 크롤링해 데이터를 채워넣기로 결심했고, 네이버 지도가 보다 더 크롤링하기 쉬울 것 같아, 네이버 지도를 크롤링하기로 했다.

     

     

     

     


     

     

    1. 크롤링 툴 선택

    보통 자바 진영에서 사용하는 크롤링 툴에는 JSoup과 Selenium 두 개의 선택지가 있었다. 이 중에 Selenium을 사용하기로 했는데, 그 이유는 네이버 맵은 동적으로 페이지가 추가되는데 Jsoup으로는 동적인 페이지 크롤링이 힘들다하여 Selenium으로 최종 결정 하였다.

     

     

     


     

    2. 셀레니움 세팅

    셀레니움 세팅을 위해선 크롬 드라이버를 설치해야하고, 드라이버를 코드로 세팅해주는 과정을 거쳐야 하는데, 이 과정은 Selenium webDriverManager라는 오픈소스를 통해 진행했다. 

     

    https://github.com/bonigarcia/webdrivermanager

     

    GitHub - bonigarcia/webdrivermanager: Automated driver management and other helper features for Selenium WebDriver in Java

    Automated driver management and other helper features for Selenium WebDriver in Java - GitHub - bonigarcia/webdrivermanager: Automated driver management and other helper features for Selenium WebDr...

    github.com

     

     

    // build.gradle
    
    // Selenium(Crawler) 추가
    implementation group: 'org.seleniumhq.selenium', name: 'selenium-java', version: '4.7.1'
    testImplementation 'org.seleniumhq.selenium:selenium-java:4.7.0'
    
    // webDriverManager(Selenium 브라우저 드라이버 관련)
    implementation 'io.github.bonigarcia:webdrivermanager:5.3.0'
    testImplementation 'io.github.bonigarcia:webdrivermanager:5.3.0'

     

     


     

     

    3. 네이버 맵 크롤링

     

    일단 코드 전문을 보자

    public class SeleniumTest {
    
        WebDriver driver;
    
        @BeforeAll
        static void setupAll() {
            WebDriverManager.chromedriver().setup();
        }
    
        @BeforeEach
        void setup() {
            driver = new ChromeDriver();
        }
    
        @AfterEach
        void teardown() {
            driver.quit();
        }
    
    
        @Test
        public void crawlFromNaver() {
    
            driver.get("https://map.naver.com/v5/search/대선칼국수");
            driver.manage().timeouts().implicitlyWait(Duration.ofMillis(1000));
    
            System.out.println("driver = " + driver.getTitle());
            driver.switchTo().frame(driver.findElement(By.cssSelector("iframe#searchIframe")));
    
    
            List<WebElement> elements = driver.findElements(By.cssSelector(".C6RjW>.place_bluelink"));
    
            System.out.println("TestTest**********************************");
            System.out.println("elements.size() = " + elements.size());
            elements.get(0).click();
    
            driver.manage().timeouts().implicitlyWait(Duration.ofMillis(500));
            driver.switchTo().defaultContent();
            driver.manage().timeouts().implicitlyWait(Duration.ofMillis(3000));
            driver.switchTo().frame(driver.findElement(By.cssSelector("iframe#entryIframe")));
    
    
            List<WebElement> placeSectionContents = driver.findElements(By.cssSelector(".place_section_content"));
            WebElement menuElement = placeSectionContents.get(placeSectionContents.size() - 1);
            List<WebElement> menus = menuElement.findElements(By.cssSelector("ul>li"));
    
            System.out.println("menumenu*******************************************");
            System.out.println("menus.size() = " + menus.size());
    
            for (WebElement menu : menus) {
                System.out.println(menu.getText());
            }
        }
    }

     

    • @BeforeEach, @BeforeAll - 셀레니움 웹 드라이버 세팅
    • @AfterEach - 테스트가 끝난 후 웹 드라이버를 종료시킴
    • void crawlFromNaver() - 네이버 맵을 크롤링함

     

     

    crawlFromNaver의 코드를 해석하자면 다음과 같다.

    driver.get("[url]")

    url에 해당하는 html을 드라이버에 로딩한다

     

    driver.manage().timeouts().implicitlyWait(Duration ...)

    정해진 시간초만큼 기다린다. 웹페이지 특성상 이미지 등이 로딩되는게 시간이 걸리기 때문에 필요한 절차이다.

     

    driver.findElements(By.cssSelector(...))

    cssSelector 문법을 이용해 원하는 HTML Element들을 찾아낸다.

     

    driver.switchTo().frame(...)

    해당하는 Iframe의 html로 이동한다.

     

    driver.switchTo().defaultContent()

    이동했던 Iframe에서 기존의 html로 이동한다.

     

     

    이 코드들 중 생소했던건 driver.switchTo().frame(...)이라는 구문이였다. 이게 왜 필요하게 되었냐면 

     

    빨간색 체크를 확대한 부분

     

     

     네이버 지도를 보면 Iframe으로 구성되어있는데 이 태그는 자바스크립트등을 이용해 HTML안에 또다른 HTML을 집어넣는 방식을 뜻한다. 이를 크롤링하기 위해선 driver.switchTo().frame(...)구문을 이용해 삽입된 HTML로 이동한 뒤 크롤링을 진행해야 정상적으로 데이터가 스크래핑되어진다.

     

     그 뒤에 driver.switchTo().defaultContent()를 사용하게 된 이유는, 이전에 driver.switchTo()... 구문을 통해 다른 HTML로 변경하고나서 또 다른 HTML으로 이동하고 싶다면 원래의 HTML(이전에 switchTo()를 통해 이동하기 전의 HTML)로 이동한 뒤에서야 정상적으로 이동이 가능하기 때문에 driver.switchTo().defaultContent() 구문을 통해 HTML을 기존 HTML로 이동시켜둬야한다.

     

    해당 코드를 동작시켜보면 

     

    해당 이미지와 같이 메뉴가 크롤링 되는 것을 볼 수 있다. 이제 사용법에 대한 테스트도 마쳤으니, 얻어낸 결과를 DTO에 담고 서비스에서 활용해야겠다.

     


     

    '개발로그 > 오늘뭐먹지 프로젝트' 카테고리의 다른 글

    JPA 초기화 전략  (0) 2023.01.05
    구조 리팩토링 - 테스트 DB 분리, 메시지 분리  (2) 2022.10.19
    CI/CD  (1) 2022.04.06
    AWS 배포  (0) 2022.03.31
    메인 기능 구현 완료  (0) 2022.03.10

    댓글

Designed by Tistory.