개요
자바를 이용한 뉴스 크롤러 만들기. 다음뉴스와 네이버뉴스에 대해서 기사 본문 및 댓글을 가져오는 기능을 만든다. 여기서 데이터를 가져오는 것들은 그렇게 어렵지 않았지만 데이터를 가져오는 과정에서 나타난 문제점들을 위주로 작성하려고 한다. 그 이전에 순차적으로 어떻게 접근했는지에 대해서 보자.
크롤링 혹은 스크래핑이라 불리우는 이 표현은 혼용해서 쓰는 것으로 알고 있다. 웹 상에 떠다니는 데이터에 대해서 읽어들이는 행위를 의미한다. 이하 본문에서는 크롤링(Crawling) 이라는 표현을 사용하고자 한다.
우선적으로 크롤링을 하기위한 라이브러리를 살펴보면 두가지가 있다.
jsoup
https://jsoup.org/selenium
https://www.seleniumhq.org/
- 명시적으로 경로를 알려주도록 만든상태
- 환경변수 Path Variable 에서 크롬 드라이버 경로를 설정해준 상태
- 해당 경우는 따로 코드상에 명시적으로 알려주지 않아도 상관 없음
Intermediate
selenium : Alerts & windows
이 부분이 오늘 살펴볼 내용이다. 위에 내용들은 튜토리얼 내용만 따라해도 바로 알 수 있는 내용들인 것에 반해서 지금부터는 내가 겪은 문제들이다.
1) org.openqa.selenium.TimeoutException
2) ElementNotVisibleException
3) NoSuchElementException
4) StaleElementReferenceException
셀레니움으로 웹 애플리케이션에 접근하다보면, 페이지가 사용자에 의해 상호작용으로 로드되고 그 시간간격이 일정하지 않다는 점을 확인할 수 있다. 요소를 식별하는 것이 안될 수도 있을 뿐더러 요소가 없으면 ElementNotVisibleException 예외를 발생한다. 이러한 문제들은 wait를 사용하여 해결할 수 있다.
Thread.sleep() 을 이용하는 것을 그다지 추천하지 않는다. 왜냐하면 ajax call 로 인해서 응답받을 수 있는 데이터 시간이 불분명하고 해당되는 스레드를 실행 큐에서 다시 대기 큐로 옮겨가면서 유휴시간이 발생하고 결과적으로 메모리와 시간을 낭비하기 때문이다. (오버헤드 발생)
따라서 thread.sleep() 말고 다른 방법을 제시하고자 한다.
wait를 하는 것에 있어서도 우선순위가 존재한다.
Explicit wait (10 sec)
Implicit wait (20 sec)
1 2 3 4 | ChromeDriver webDriver = new ChromeDriver(); webDriver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS); webDriver.get(URI); | cs |
첫번째 인자는 정수형(int) 값, 두번째 인자는 시간 측정을 허용하기 위한 기준. 이렇게 암묵적 대기 시간을 줌으로써, WebDriver가 즉시 가져올 수 없는 요소들을 찾는 경우 특정시간 DOM을 Polling 하도록 기다린다. 결과적으로 브라우저 내 페이지의 요소를 검색할 대기시간을 설정할 수 있다.
Explicit wait ( 명시적 대기 )
명시적 대기는 WebDriver 에게 ElementNotVisibleException 예외를 throw 하기 이전에 특정 조건을 충족하거나 최대 시간을 초과할 때까지 대기하도록 명시한다. 명시적인 대기는 동적으로 로드되는 ajax요소를 기다린다.
명시적인 대기는 Fluent Wait 를 사용하여 조건 확인 빈도를 구성할 수 있다.
요소를 동적으로 기다리는 가장 좋은 방법
매 초마다 주어진 조건을 검사하고 조건이 충족되는 스크립트에서 다음의 명령을 계속해서 수행하는 방식으로 해결이 가능하다. 만약 해당 시간 내에 찾고자 하는 요소가 존재하지 않는다면 Fluent wait 를 이용해서 해결할 수 있다.
Fluent wait 를 이용해 타임 아웃 및 폴링 간격을 조절할 수 있다.
withTimeout() : 전체 시간
pollingEvery() : 폴링 시간
- withTimeout() 의 최대 시간 동안 매 폴링 시간 만큼 웹 페이지의 요소를 검색
1 2 3 | FluentWait<WebDriver> fluentWait = new FluentWait<WebDriver>(webDriver); fluentWait.withTimeout(Duration.ofMinutes(10)); // FluentWait 인스턴스가 조건을 기다리는 최대 시간 (10분 설정) fluentWait.pollingEvery(Duration.ofSeconds(10)); // CPU가 리소스에 접근하기 위한 폴링 간격 조절 (10초) | cs |
그리고 until() 메소드를 수행하기 위한 함수를 함수를 초기화한다.
Function<WebDriver, Boolean> commentViewFunction
fluentWait.until(commentViewFunction);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | // <입력 파라미터, apply() 메소드 반환 값> Function<WebDriver, Boolean> commentViewFunction = new Function<WebDriver, Boolean>(){ @Override public Boolean apply(WebDriver webDriver) { // [ 댓 글 더 보 기 ] 계속 클릭 : 다음은 하나의 화면 내에서 댓글을 볼 수 있도록 관리 되어있음 WebElement viewMoreComment = webDriver.findElement(By.xpath("//*[@id='alex-area']/div/div/div/div[3]/div[1]/a")); // 해당 요소 사이즈 확인 Dimension dimension = viewMoreComment.getSize(); int width = dimension.getWidth(); int height = dimension.getWidth(); // 중단 (클릭할 요소가 없기 때문에) if(width == 0 && height == 0){ return true; } // 계속 시행 else{ viewMoreComment.click(); return false; } } }; fluentWait.until(commentViewFunction); | cs |
until() 메소드가 호출되고 플로우는 아래와 같이 나타낼 수 있다.
[ http://toolsqa.com/selenium-webdriver/advance-webdriver-waits/ ]
'jvm lang' 카테고리의 다른 글
20180403 Java Variable Types (수정 : 20190630) (0) | 2018.04.03 |
---|---|
20180331 JVM Memory Model ( Garbage Collector : GC ) (0) | 2018.03.31 |
20180222 Enum Type (Enumeration) 02 (0) | 2018.02.22 |
20180221 Enum Type (Enumeration) 01 (0) | 2018.02.21 |
20180219 instanceof 연산자 (0) | 2018.02.19 |