본문 바로가기

Selenium

웹 크롤링 - 축제 정보 가져와보기

1. 개요

 Tripot 프로젝트 중 축제 정보를 가져와 보여줄 기능을 구현하고자 했었다. 이에 대한민국 구석구석 사이트에서 정보를 크롤링해와 이를 보여주는 기능을 구현해보자. 후술하겠지만 해당 코드는 프로젝트에 사용하지 않았다.

 

  • 웹 크롤러: 조직적, 자동화된 방법으로 월드 와이드 웹을 탐색하는 컴퓨터 프로그램
  • 웹 크롤링: 크롤러가 하는 작

2. Selenium

웹 브라우저 자동화를 위해 만들어진 파이썬, 자바, C# 등 다양한 언어를 지원하는  오픈 소스 프레임워크이다. 

@NullMarked
public interface WebDriver extends SearchContext {
    void get(String url);

    @Nullable String getCurrentUrl();

    @Nullable String getTitle();

    List<WebElement> findElements(By by);

    WebElement findElement(By by);

    @Nullable String getPageSource();

    void close();

    void quit();

    Set<String> getWindowHandles();

    String getWindowHandle();

    TargetLocator switchTo();

    Navigation navigate();

    Options manage();

    @Beta
    public interface Window {
        Dimension getSize();

        void setSize(Dimension targetSize);

        Point getPosition();

        void setPosition(Point targetPosition);

        void maximize();

        void minimize();

        void fullscreen();
    }

    public interface Navigation {
        void back();

        void forward();

        void to(String url);

        void to(URL url);

        void refresh();
    }
    ...

 자바 셀레니움의 WebDriver 인터페이스이다. getter로 브라우저 및 접속 사이트의 이름 등을 가져오거나, get() 함수로 url에 요청을 보내거나, findElement()로 해당 페이지의 정보를 가져오는 등 다양한 역할을 수행할 수 있다. 

 3. ChromeDriver

지금은 Selenium을 크롤러로서 사용한다. 크롬, 사파리, 오페라 등 다양한 브라우저를 지원하지만 지금은 크롬 브라우저를 쓰도록 한다.

우선 설치되어있는 크롬의 버전을 확인한다. 설정 -> Chrome 정보를 통해 확인할 수 있다.

https://developer.chrome.com/docs/chromedriver/downloads?hl=ko

 

다운로드  |  ChromeDriver  |  Chrome for Developers

이 페이지는 Cloud Translation API를 통해 번역되었습니다. 다운로드 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 달리 명시되지 않는 한 이 페이지의 콘텐츠

developer.chrome.com

 

 

해당 사이트에서 크롬 버전에 맞는 크롬드라이버를 다운로드한다. 버전이 크게 차이나지 않는다면 정상적으로 동작한다.

4. 설정

@Configuration
public class WebDriverConfig {


    private static String WEB_DRIVER_ID = "webdriver.chrome.driver";//property 키
    private static String WEB_DRIVER_PATH = "D:\\Download\\chromedriver-win64\\chromedriver.exe";

    @Bean
    public ChromeOptions chromeOptions() {
        // webDriver 옵션 설정
        ChromeOptions chromeOptions = new ChromeOptions();
        chromeOptions.addArguments("--headless");           //브라우저 창 X
        chromeOptions.addArguments("--lang=ko");                //언어 설정
        chromeOptions.addArguments("--no-sandbox");             //리눅스에서 셀레니움이 적절히 동작하지 않을 때 사용할 수 있는 옵션
        chromeOptions.addArguments("--disable-dev-shm-usage");          ///  dev/shm 사용 안함
        chromeOptions.addArguments("--disable-gpu");                //gpu 가속 사용 x



        return chromeOptions;
    }

    @Bean
    public WebDriver webDriver(ChromeOptions chromeOptions) {

        System.setProperty(WEB_DRIVER_ID, WEB_DRIVER_PATH);

        WebDriver driver = new ChromeDriver(chromeOptions);
        driver.manage().timeouts().pageLoadTimeout(Duration.ofSeconds(30));

        return driver;
    }
}

 하나의 웹 드라이버를 만들어 사용할 것이므로 Config 클래스를 만들어 빈으로 등록해주었다. 크롬드라이버의 경로를 등록하고, chromeOption 설정을 주입해 크롬드라이버 인스턴스를 만든다.

 

5. 구현

https://korean.visitkorea.or.kr/kfes/list/festivalCalendar.do

 

월별 축제 달력 | 대한민국 구석구석 축제

전국 방방곡곡에서 열리는 축제 정보를 소개합니다. 전국의 다채로운 축제와 함께 행복하고 즐거운 여행 되세요!

korean.visitkorea.or.kr:443

 

 우선 크롤링 대상 웹사이트가 어떻게 구성되어있는지 확인해보자.

 웹 사이트에 접속하면 달력에 오늘 날짜가 적혀있고, 오늘 진행하는 축제 리스트가 아래에 표시된다.

  처음에는 12개까지의 결과만 보여지고, 무한 스크롤이 구현되어있어 스크롤을 맨 아래로 내려야 모든 축제 정보를 받아올 수 있다.

 

 이를 기반으로 서비스 코드를 작성해보자.

    private final WebDriver webDriver;
    
    
   	...
    
    public List<FestivalDto> crawl(){
    

        List<FestivalDto> response = new ArrayList<>();

        try {
            webDriver.get("https://korean.visitkorea.or.kr/kfes/list/festivalCalendar.do");
            Thread.sleep(2000);

 우선 해당 페이지에 get을 사용하여 요청을 보낸다. 응답을 받아야 다음 과정을 진행할 수 있으므로 2초동안 sleep을 건다.

            JavascriptExecutor js = (JavascriptExecutor) webDriver;
            
            Long lastHeight = (Long) js.executeScript("return document.body.scrollHeight");
            
            while (true) {
                js.executeScript("window.scrollTo(0, document.body.scrollHeight);");
                Thread.sleep(500);
                Long newHeight = (Long) js.executeScript("return document.body.scrollHeight");
                log.info("lastHeight: {}, newHeight={}", lastHeight, newHeight);
                if (lastHeight.equals(newHeight)) {
                    break;
                }
                lastHeight = newHeight;
            }

 그 다음 해당 사이트의 스크롤을 끝까지 내려야 한다. JavascriptExecutor을 사용하여 스크립트를 실행한다. 해당 페이지의 높이를 구하고, 스크롤을 한번 끝까지 내렸을 때 높이가 달라지지 않았다면 축제 정보가 더 로딩되지 않은 것이고, 이때 크롤링을 하면 오늘 열리는 모든 축제 정보를 가져올 수 있다.

 

 F12를 눌러 개발자 도구를 연 후 흰색 박스에 해당하는 버튼을 클릭 후

원하는 요소를 클릭하면

해당 위치로 이동할 수 있다. list라는 id를 가진 ul(unordered list) 내에 list로 축제 정보 요소가 있는 것을 확인할 수 있다.

각 요소에는 하이퍼링크 내에 other_festival_content가 있고, 이 안에 축제 이름, 기간(date), 지역(loc)이 있는 것을 확인할 수 있다.

 

해당 위치의 cssSelector는 우클릭후 위의 위치의 버튼을 눌러 복사할 수 있다.

            List<WebElement> elements = webDriver.findElements(By.cssSelector("#list > li"));

이를 붙여넣고, findElements를 이용해 리스트 내의 요소를 모두 받아올 수 있다. 모든 정보를 확인하려면 뒤에 li가 붙어야 한다.

for (WebElement element : elements) {
                String title = element.findElement(By.cssSelector("a > div.other_festival_content > strong")).getText();

                String duration = element.findElement(By.cssSelector("a > div.other_festival_content > div.date")).getText();
                String place = element.findElement(By.cssSelector("a > div.other_festival_content > div.loc")).getText();

                response.add(FestivalDto.builder()
                        .title(title)
                        .duration(duration)
                        .place(place)
                        .build());


            }

 각 요소에 대해 동일한 과정을 수행하여 제목, 기간, 위치를 받아 응답 리스트에 추가 후 리턴한다.

 정상적으로 받아서 리턴하는 것을 확인할 수 있었다.

    @PreDestroy
    private void closeWebDriver(){
        if (Objects.nonNull(webDriver)) {
            webDriver.close();
        }
    }

물론 프로그램을 종료할 때 웹 드라이버를 꺼주어야 한다.

5. 주의

https://knto.or.kr/helpdeskCopyrightguide

 

한국관광공사

관광으로 행복한 나라를 만드는 기업, 한국관광공사의 공식 웹사이트입니다.

knto.or.kr:443

 해당 html 파일, 축제 정보 등 엄연히 저작물이므로 상업적 목적으로 이용할 수 없다. Tripot 프로젝트는 광고를 통해 수익을 내는 앱이므로 해당 정보를 사용하지 않기로 했다.

 

 일부 사이트는 루트 페이지에 /robots.txt를 붙이면 어떤 경로에서 크롤링이 가능하고, 불가능한지 알 수 있다.

google.com/robots.txt

User-agent: *
Disallow: /search
Allow: /search/about
Allow: /search/static
Allow: /search/howsearchworks
Disallow: /sdch
Disallow: /groups
Disallow: /index.html?
Disallow: /?
Allow: /?hl=
Disallow: /?hl=*&
Allow: /?hl=*&gws_rd=ssl$
Disallow: /?hl=*&*&gws_rd=ssl
Allow: /?gws_rd=ssl$
Allow: /?pt1=true$
Disallow: /imgres
Disallow: /u/
Disallow: /setprefs
Disallow: /default
Disallow: /m?
Disallow: /m/
Allow:    /m/finance
Disallow: /mobile?
Disallow: /wml?
...