에러 메세지 및 원인
에러 메세지를 캡처를 못했다..
No Such Element Exception이 발생하며 해당 에러 뒤의 Crawling Data는 전부 Null이 들어왔다.
파싱한 페이지의 Element를 읽어오지 못하는 모양이다.
Crawler 제작 시 Selenium을 사용하여 총 click을 세 번 수행하고, 파싱된 페이지들에서 총 5개의 데이터를 Crawling해야 할 일이 있었는데, 해당 에러가 발생한 뒤 부터는 모든 데이터가 NULL로 입력되었다.
해결
Selenium 공식문서를 참조하여 해결했는데, Implicit wait를 활용하여 대기 시간을 10초를 주었었는데
데이터를 하나 가져올 때 마다 쓰레드를 2초 정도 멈춰야 했었고, 그렇기 때문에 대기 시간을 20초로 늘려주는 코드로 변경해서 해결했다.
Selenium의 WebDriverWait 정리
공식 문서를 참조하여, 해당 내용들을 정리했다.
조금만 내리면 아래와 같이 No Such Element Exception의 원인을 볼 수 있고
해결책에 대한 예시들을 볼 수 있다. Waiting Strategy를 클릭하면 예시 코드까지 볼 수 있음.
공식 문서에서는 세 가지 방법으로 Solution을 주었다. 아래의 코드들은 전부 공식문서의 예시들이다.
Explicit wait / Implicit wait / FluentWait
Explicit wait는 WebDriver에서 가져온 WebElement에 대해 WebDriverWait를 통해 원하는 조건을 줘야한다.
WebElement foo = new WebDriverWait(driver, Duration.ofSeconds(3))
.until(driver -> driver.findElement(By.name("q")));
assertEquals(foo.getText(), "Hello from JavaScript!");
Explicit wait는 지정한 조건이 충족될 때 까지 대기하며, 시간이 초과됐는데도 조건을 충족하지 못했을 경우 timeout Exception을 던진다. 또한 시간 내에 조건을 만족했더라도, 남은 시간동안 계속 대기한다.
공식 문서에서 정의한 조건의 예시들은 다음과 같으며, 직역하니 이상해서 원문으로 남긴다.
alert is present, element exists, element is visible, title contains, title is, element staleness, visible text
Implicit wait는 일정 시간만큼 기다린다.
이 때 지정한 시간 내에 로딩이 완료된다면, 남은 시간만큼 더 기다리지 않고 넘어간다.
WebDriver driver = new FirefoxDriver();
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
driver.get("http://somedomain/url_that_delays_loading");
WebElement myDynamicElement = driver.findElement(By.id("myDynamicElement"));
Implicit wait는 웹 페이지가 완전히 로드된 후, 일정 시간이 지나야만 나타나는 요소 혹은 사용자의 동작에 따라 업데이트되는 동적 요소가 View될 때 까지 대기할 수 있다.
또한, Explicit wait와 Implicit wait를 혼합해서 사용하지 말라는 경고 메세지가 있었다.
혼합하여 사용 시 예측할 수 없는 대기시간이 발생한다는 이유에서였다.
예를 들어 Implicit wait를 10초, Explicit wait를 15초로 설정하면 20초 후에 시간 초과가 발생할 수 있다고 한다.
FluentWait는 기다리는 최대 시간을 지정하며, 해당 요소의 존재여부를 확인하는 주기 설정 또한 가능하다.
withTimeout에서 최대 대기 시간을 설정할 수 있으며, pollingEvery에서 요소의 확인 주기를 설정할 수 있다.
또한 남은 시간을 기다리지 않고 다음 동작을 수행한다.
Wait<WebDriver> wait = new FluentWait<WebDriver>(driver)
.withTimeout(Duration.ofSeconds(30))
.pollingEvery(Duration.ofSeconds(5))
.ignoring(NoSuchElementException.class);
WebElement foo = wait.until(driver -> {
return driver.findElement(By.id("foo"));
});
해당 언어나 라이브러리 등의 공식문서를 보는 눈이 조금 생겨서
에러가 발생할 때 공식문서를 먼저 보는 습관을 길렀더니 도움이 되는 것 같다.
2023.04 ~ 백엔드 개발자의 기록
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!