셀레니움(Selenium)을 이용해 웹 브라우저 자동화 스크립트를 작성하다 보면 가장 자주 마주치는 대표적인 에러가 있습니다. 바로 NoSuchElementException 또는 ElementNotInteractableException입니다.
분명히 브라우저 화면에는 아이디 입력창이나 로그인 버튼이 잘 보이는데, 파이썬 코드는 해당 요소를 찾지 못했다며 빨간색 에러 메시지를 뿜어내고 프로그램을 중단시켜 버립니다. 이 현상의 원인은 파이썬 코드의 실행 속도가 실제 웹 브라우저가 인터넷을 통해 화면 요소(HTML/CSS/JS)를 불러오는 속도보다 훨씬 빠르기 때문에 발생합니다.
저 역시 셀레니움 입문 시절, 화면이 채 뜨기도 전에 코드가 다음 줄로 넘어가서 터지는 에러 때문에 무작정 time.sleep(10)을 남발했던 경험이 있습니다. 오늘은 단순 강제 대기의 한계를 극복하고, 화면 로딩 상태에 따라 스마트하게 기다려주는 동적 대기(WebDriverWait) 활용법을 정리해 드립니다.
1. 무작정 기다리기(time.sleep)의 치명적 단점
초보자들이 로딩 에러를 피하기 위해 가장 먼저 사용하는 방법은 파이썬 기본 모듈인 time.sleep(초)입니다.
# [비효율적인 방식] 화면이 이미 1초 만에 떠도 무조건 5초를 멍하니 기다림
driver.get("https://example.com")
time.sleep(5)
driver.find_element(By.ID, "login_btn").click()
time.sleep() 방식은 세 가지 결정적인 문제를 안고 있습니다.
시간 낭비: 인터넷 속도가 빠르면 0.5초 만에 화면이 뜨는데도 설정한 5초를 끝까지 멍하니 대기합니다. 수십 개의 페이지를 순회할 때 실행 시간이 엄청나게 늘어납니다.
불안정한 에러 발생: 네트워크 상태가 일시적으로 느려져 5.1초 만에 화면이 뜨면 여전히 에러가 발생하며 프로그램이 튕깁니다.
리소스 낭비: 컴퓨터 시스템의 효율성을 크게 떨어뜨립니다.
2. 암묵적 대기(implicitly_wait)와 명시적 대기(WebDriverWait)의 차이
셀레니움은 이러한 로딩 시간차 문제를 해결하기 위해 두 가지 지능형 대기 방식을 제공합니다.
1) 암묵적 대기 (Implicit Wait)
브라우저 전체에 지정한 시간(예: 10초) 동안 요소를 찾을 때까지 최대 10초간 대기하도록 일괄 설정하는 방식입니다.
장점: 코드 상단에
driver.implicitly_wait(10)단 한 줄만 적어두면 되므로 설정이 매우 간단합니다.한계: 화면에 요소는 존재하지만 '클릭할 수 없는 상태(Disabled/Invisible)'일 때는 대기하지 않고 즉시 에러를 발생시킵니다.
2) 명시적 대기 (Explicit Wait - WebDriverWait) [강력 추천]
특정 요소가 화면에 나타날 때까지, 혹은 클릭 가능해질 때까지(element_to_be_clickable) 지정한 조건이 충족될 때까지만 핀포인트로 대기하는 방식입니다.
장점: 지정한 조건이 1초 만에 충족되면 즉시 다음 코드로 넘어가므로 속도가 가장 빠르고 안정적입니다.
특징: 무작정 기다리는 것이 아니라 0.5초 간격으로 화면 상태를 체크하다가 조건이 충족되는 순간 즉시 동작을 수행합니다.
3. 실전 예제: WebDriverWait 사용법과 주요 조건 3가지
명시적 대기를 사용하려면 WebDriverWait 클래스와 조건들을 담고 있는 expected_conditions(보통 EC로 줄여 사용)를 불러와야 합니다.
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
service = Service(ChromeDriverManager().install())
driver = webdriver.Chrome(service=service)
try:
driver.get("https://example.com/dynamic-login")
# 1. WebDriverWait 객체 생성 (최대 10초까지 대기하도록 설정)
wait = WebDriverWait(driver, 10)
# 2. 조건 지정: ID가 'username'인 입력창이 화면에 보일 때까지 대기
id_input = wait.until(
EC.visibility_of_element_located((By.ID, "username"))
)
id_input.send_keys("my_user_id")
# 3. 조건 지정: 로그인 버튼이 '클릭 가능한 상태'가 될 때까지 대기
login_button = wait.until(
EC.element_to_be_clickable((By.CSS_SELECTOR, "button.btn-login"))
)
login_button.click()
print("동적 대기를 통해 안정적으로 클릭을 수행했습니다!")
except Exception as e:
print(f"10초 동안 요소를 찾지 못해 타임아웃 에러 발생: {e}")
finally:
driver.quit()
자주 쓰이는 3대 EC(expected_conditions) 조건
presence_of_element_located((By.XXX, "값")): 해당 요소가 HTML DOM 트리에 나타날 때까지 대기 (화면에 눈으로 보이지 않아도 데이터상 존재하면 통과)visibility_of_element_located((By.XXX, "값")): 요소가 HTML 상에 존재할 뿐만 아니라 실제로 눈에 보이고 화면에 그려질 때까지 대기element_to_be_clickable((By.XXX, "값")): 요소가 화면에 보이고, 마우스로 실제로 클릭이 가능한 활성화 상태가 될 때까지 대기
4. TimeoutException 방지와 스마트한 예외 처리 구조
WebDriverWait를 사용했음에도 서버 문제나 네트워크 연결 불안정으로 지정한 시간(예: 10초) 내에 요소가 뜨지 않으면 TimeoutException 에러가 터지게 됩니다.
이때 스크립트 전체가 멈추지 않게 하려면 try-except 구문으로 예외 처리를 감싸주는 것이 안전합니다.
from selenium.common.exceptions import TimeoutException
try:
# 5초 동안팝업창 닫기 버튼이 뜨는지 감시
close_btn = WebDriverWait(driver, 5).until(
EC.element_to_be_clickable((By.ID, "popup_close"))
)
close_btn.click()
print("이벤트 팝업창을 성공적으로 닫았습니다.")
except TimeoutException:
# 5초 동안 팝업창이 안 뜨면 팝업이 없는 것으로 간주하고 계속 진행
print("이벤트 팝업창이 존재하지 않아 다음 로직으로 이동합니다.")
이처럼 동적 대기와 예외 처리를 결합하면, 팝업창이 뜰 때도 있고 안 뜰 때도 있는 변덕스러운 웹사이트 환경에서도 멈추지 않고 100% 완주하는 강력한 자동화 프로그램을 완성할 수 있습니다.
핵심요약
단순
time.sleep()대기 방식은 실행 속도를 불필요하게 지연시키고 무작위 로딩 에러를 완벽히 막지 못합니다.WebDriverWait와expected_conditions(EC)를 결합한 명시적 동적 대기를 사용하면 요소가 로딩되는 즉시 코드가 실행되어 속도와 안정성을 모두 잡을 수 있습니다.element_to_be_clickable등 정밀한 조건을 지정하고TimeoutException예외 처리를 조합하면 어떤 네트워크 환경에서도 튕기지 않는 자동화 스크립트를 구축할 수 있습니다.


0 댓글