성장일지 9차

이태형·2025년 1월 11일

Growth Log

목록 보기
9/18

중고물품 가격을 정리해서 보여주는 토이 프로젝트를 만들려고 Selenium 으로 크롤링하는 프로그램을 만드는 중입니다.

JAVA + Selenium 으로 진행했습니다.
코드 부분은 부분 코드만 기입했습니다.

Selenium 이란?

동적인 자료를 수집할 때 주로 사용하는 오픈소스 프레임워크입니다.
자바스크립트로 동적으로 변하는 웹 페이지를 크롤링 작업을 할 수 있습니다.

다양한 웹 브라우저를 지원하여 여러 브라우저에서 동작이 가능합니다.

실제 브라우저를 실행하여 테스트를 진행하기 때문에 속도 측면에서 다소 느린 부분이 있습니다.
JavaScript 의 동적 로딩을 대기해야해서 대기시간을 추가해야 합니다.
또한 Python, Java 등 다양한 언어를 지원해서 호환성이 좋습니다.

Driver 설정

크롤링을 Chrome 으로 진행하기 때문에 ChromeDriver 가 필요합니다.

  private ChromeOptions getChromeOptions() {
        ChromeOptions chromeOptions = new ChromeOptions();
        chromeOptions.addArguments("disable-extensions");
        chromeOptions.addArguments("no-sandbox");
        chromeOptions.addArguments("--remote-allow-origins=*");
        chromeOptions.addArguments("headless");

        return chromeOptions;
  }
  
  private void setProperty() throws IOException{
        System.setProperty("webdriver.chrome.driver", getChromeDriverExe().getFile().getAbsolutePath());
  }

크롤링 진행 시 설정을 지정하고, ChromeDriver 를 실행할 수 있게 경로를 설정해야 합니다.

크롤링

WebDriver 객체를 이용해 페이지 내에 요소에 접근하여 WebElement 객체로 반환합니다.
WebElement 객체로 클릭, 입력 등 요소의 동작을 지정할 수 있습니다.

private void searchKeywordAndMovePage() {
    // 입력 상자 찾기
    WebElement searchBox = driver.findElement(By.id(cssValueContainer.getSearchBoxCss()));
    // 검색어 입력
    searchBox.sendKeys(keyword);
    // 검색 버튼 클릭
    searchBox.submit();
}

WebDriverWait 을 사용하여 요소가 로딩이 완료되어 사용이 가능할 때까지 대기합니다.

    private List<WebElement> crawlingAndWaitForLoadingComplete() {
	    return waitDrvier.until(d -> waitForLoadingElement(d.findElements(By.xpath(cssValueContainer.getItemPostCssWithReplaceKeyword(keyword)))));
    }

    private List<WebElement> waitForLoadingElement(List<WebElement> itemPostElements) {
        try {
            // 모든 요소가 화면에 표시될 때까지 기다립니다.
            return (!itemPostElements.isEmpty() && itemPostElements.stream().allMatch(WebElement::isDisplayed)) ? itemPostElements : null;
        } catch (StaleElementReferenceException e) {
            return crawlingAndWaitForLoadingComplete();
        }
    }

요소 검색은 찾고자 하는 부분에 특정 class, tag, 속성값 등을 이용해서 특정 요소를 가져와야합니다.

가져온 요소에서 필요한 내용 ( 저같은 경우는 게시글 이름, 중고 가격, 올린 시간 ) 을 css 기준으로 뽑아내어 객체로 만들어서 반환하고 있습니다.

	private List<UsedItem> consolidateCrawlingData(List<WebElement> elements) {
        return elements.stream().map(this::createJoongnaItemList).filter(Objects::nonNull).toList();
    }

    private UsedItem getItem(WebElement element) {
        String url = getItemLink(element);
        String title = getItemTitle(element);
        String[] priceAndUploadTime = getItemPriceAndUploadTime(element);
        return new UsedItem(title, url, priceAndUploadTime[0], priceAndUploadTime[1]);
    }

    private String getItemTitle(WebElement element) {
        WebElement contentTag = element.findElement(By.tagName("img"));
        return contentTag.getAttribute("alt");
    }

    private String getItemLink(WebElement element) {
        return element.getAttribute("href");
    }

    private String[] getItemPriceAndUploadTime(WebElement element) {
        WebElement content = element.findElement(By.cssSelector(cssValueContainer.getPriceContainerCss()));
        return new String[]{getItemPrice(content), getItemUploadTime(content)};
    }

    private String getItemPrice(WebElement content) {
        return content.findElement(By.cssSelector(cssValueContainer.getItemPriceTagCss())).getText();
    }

    private String getItemUploadTime(WebElement content) {
        List<WebElement> elements = content.findElements(By.cssSelector(cssValueContainer.getItemUploadTextCss()));
        String uploadTime = elements.get(elements.size() - 1).getText();
        // 광고 게시글 제외
        isAdvertisement(uploadTime);
        return uploadTime;
    }

고민요소

Selenium 은 요소가 동적으로 로딩되는 것을 대기하여 동작이 진행되기 때문에 접근하는 요소가 로딩이 덜 되었거나, 사용이 불가능한 경우 ( 웹 페이지 크기 등으로 인해 요소가 화면에 안보이는 경우 등 ) 요소에 접근하지 못해 에러가 발생하게 됩니다.

적절한 대기 시간을 주거나, 해당 상황에 예외처리로 잘 대처하여 재요청을 진행하는 등 고려할 상황이 많았습니다.

또한 4k 화면에서 크롤링이 되던 것이 노트북 화면에서는 크롤링이 안되고 그런게 있어서 추가 수정이 필요합니다.

이후 개발은 ScheduleTask 를 추가해서 일정시간마다 자동으로 크롤링하여 DB에 저장하는 로직까지 추가가 남아있습니다.

0개의 댓글