이 문서는 셀레니움 wait 에 관한 implicitly wait 와 explicitly wait 에 대해서 다루고 있습니다. 셀레니움 사용법 전반에 대해서 알아보시려면 셀레니움 크롤러 기본 사용법을 확인하시기 바랍니다.
셀레니움 wait 의 개념
wait 는 말 그대로 ‘기다리라’는 뜻이다. 초보자들이 wait 의 개념을 간과하고 크롤링하다가 에러에 맞딱뜨리고 많은 땀을 흘리는데 오늘 이 wait 에 대해서 박살을 내보겠다. 뭐 implicitly wait 니 explicitly wait 이니 어려운 용어들이 보이는데 내용이 어렵지 않기 때문에 쉽게 이해하자. 모르면 답답한데 알면 속이 시원해진다. 자 그럼 다같이 속이 시원해져보자!
implicitly Wait VS Explicitly Wait
우리는 명령어로 셀레니움을 조작한다. 예를 들어 driver.get(‘https://pythondocs.net’) 이라고 치면
파이썬은 셀레니움을 신호를 보내고
셀레니움은 브라우저에 url 을 이동하라고 신호를 보낸다.
브라우저는 pythondocs 서버로 웹페이지를 보여달라는 신호를 보내고
pythondocs.net 은 신호를 받고 html, css, 자바스크립트라는 언어로 된 웹페이지 정보를 나의 브라우저로 보내준다.
나의 브라우저는 수신한 언어를 사람들의 눈에 보기 좋은 화면으로 변환(render, 렌더)해서 브라우저 화면으로 보여준다.
자 그럼 당연히 시간차라는 것이 생기게 된다. driver.get(‘https://pythondocs.net’) 명령어를 쓰자마자 서버로부터 데이터를 받아오기도 전에 브라우저 화면에 있는 것을 어떻게 하라는 그 다음 명령어가 실행되면 컴퓨터는 당연히 에러 내거나 올바르게 코드가 작동하지 않는다.
기다려야 한다!
그런데 셀레니움에서 기다리는 방법이 2가지가 있었으니 그것이 바로 implicitly wait (웹페이지 전체가 넘어올때까지 기다리기) 와 explicitly wait (웹페이지의 일부분이 나타날때까지 기다리기) 이다.
time.sleep
일단 위 wait 를 배우기 전 그냥 기본적으로 물리적인 시간을 기다리는 명령어는 time.sleep 이다.
적절하게 상황에 따라 사용하면 좋다.
Implicitly Wait
그럼 implicitly wait 를 먼저 보자. 일단 설정부터 해보자.
참고로 드라이버 열기에 관한 문서도 읽어보기 바란다.
저 임플리시틀리 웨이트는 쉽게 설명하자면 페이지 이동등의 명령어를 줬을 때 다음 웹페이지가 넘어올때까지 기다리라는 뜻이다. 드라이버를 열고 나서 한번만 설정해주면 된다. 위 예제에서 괄호 안에 숫자의 의미는 10초동안 웹페이지가 로딩될때까지 기다리고 10초가 넘어가면 웹페이지가 로딩이 됐던 안됐던 다음 명령어를 실행하겠다는 것이다. 10초면 어지간한 웹페이지가 로딩되는데 충분한 시간이다. 상황에 적정한 시간을 설정하면 되겠다.
여기서 햇갈리지 말아야 할 것은 페이지가 넘어오기까지 최대 10초까지 기다리겠다는 의미이지 요청하고나서 무조건 10초를 기다리겠다는 것이 아니다. (무조건 10초를 기다리려면 time.sleep 을 쓰면 되겠다.)
예를 들어서 임플리시틀리 웨이트를 10초로 설정하고 특정 사이트에 접속했는데 웹페이지가 1초만에 넘어왔다면 1초후에 다음 명령어를 실행하게 된다. 다시 말하자면 페이지가 다 넘어왔으면 10초가 지나지 않아도 다음 명령어를 실행하는 것이고 안넘어온다면 최대 10초까지만 기다려보고 10초가 지나도록 안넘어오면 그냥 다음 명령어를 실행하겠다는 의미이다.
Explicitly Wait
임플리시틀리 웨이트로 모든 문제가 해결이 되면 정말 좋겠지만 임플리시틀리 웨이트는 불완전하다. 우리가 사용하는 웹언어는 생각보다 복잡하기 때문이다.
우리는 웹사이트를 돌아다니면서 어떤 사이트에 접속을 하면 한번에 모든 페이지가 다 나오는 경우도 있지만, 전체 페이지의 일부분이 먼저 나오거나 늦게 나오는 경우가 있다. 이것을 동적DOM 이라고 부르는데 어려운 용어라고 생각해서 쫄 필요 없다. 그냥 웹페이지가 전체가 딱! 한번에 표시되지 않는 경우도 있다는 점에 주목하라.
예를 들어 우리는 임플리시틀리 웨이트를 10초로 설정해놓았다. 그런데 웹페이지가 1초만에 넘어왔다면 1초 후에 다음 명령어를 실행한다고 하였다. 그런데 웹페이지는 1초만에 넘어왔는데 넘어온 웹페이지의 일부분이 자바스크립트로 구현되어 있어서 그 일부분이 화면상에서 렌더링 되느라 비교적 늦게 브라우저에 표시되었다고 가정하자.
(이런 경우는 우리가 온라인상에서 흔하게 볼 수 있다. 어떤 블로그를 방문하였는데 블로그 내용이 먼저 보이고 몇초 후에 광고가 나타나는 경우가 이를 설명하는 좋은 예일 수 있겠다.)
그럼 이런 문제가 발생한다. 임플리시틀리 웨이트를 충분히 줬다고 하더라도 웹페이지가 넘어오는 순간 다음 명령어가 실행되어 버릴 것이고 웹페이지는 넘어왔으나 일부 자바스크립트의 내용이 렌더링 되기도 전에 그 다음 명령어가 작동이 되어버린다면…?
이러한 문제를 해결하기 위해서 익스플리시틀리 웨이트가 존재한다. 익스플리시틀리를 쉽게 정의하자면
내가 필요한 그! 부분이 표시될때까지 기다려라! 를 의미한다.
자 그럼 배워보자
임포트(import) 할 것이 많다. 위에 있는 것들은 그저 복붙하기만 하면 되지 그 내용과 의미를 알려고 하는 호기심이 생기면 쓸데없는데 많은 시간을 퍼붓게 되는 것에 반해 얻는 것은 별로 없다. 그저 셀레니움에서 익스플리시틀리 웨이트 기능을 사용하겠다는 의미 그 이상도 이하도 아니기 때문이다.
일단 element 라고 변수를 잡아주고 WebDriverWait(driver, 10) 의 의미는 구동한 driver는 셀레니움 구동한 드라이버 변수명, 뒤에 10은 10초까지 그 부분이 필요할때까지 기다려보겠다는 의미이다. 물론 10초 이전에 그 부분이 나타나면 10초까지 다 기다리지 않는다. 엘레먼트가 나타나면 해당 엘레먼트를 element 변수가 가리키게 된다.
.until(EC.presence_of_element_located((By.ID, “idname”))) 이 부분을 잘 이해해야한다.
EC.presence_of_element_located라는 것은 괄호안에 요소가 나타날때까지 기다리라는 의미인데, 괄호안에 요소는 보통
(By.ID, ‘아이디이름’)
(By.CLASS_NAME, ‘클래스명’)
(By.XPATH, ‘xpath경로’)
(By.NAME, ‘네임명’)
(By.CSS_SELECTOR, ‘CSS셀렉터’)
(By.PARTIAL_LINK_TEXT, ‘링크텍스트일부분’)
(By.LINK_TEXT, ‘링크텍스트(전부일치)’)
이다. find_by_~~~ 로 엘레먼트를 찾는 것과 같다고 보면 된다.
여기서 주의해야 할 것은 괄호의 갯수이다. EC.presence_of_element_located뒤에 괄호가 2개 붙어있어야 하고 맨 마지막에는 3개로 닫는다. 구성을 유심히 잘 보면 이해하기 쉽다. 헷갈리면 복붙이 최선이다.
자 그 다음!
EC.presence_of_element_located 말고도 다른 종류가 많이 있다.
이렇게 많은 기다리는 방법들이 있다. 이중에서 가장 많이 사용하는 것은 EC.presence_of_element_located 와 EC.element_to_be_clickable 이니 상황에 따라 내가 하려는 목적에 맞게 골라서 쓰면 되겠다.
사용하다보면 implicitly wait 나 explicitly wait 둘다 완전하지 못하다는 사실을 깨닫게 되는 날이 올 것이다. 그럴 때에는 time.sleep 을 적절하게 섞어서 써가다보면 어느새 안정적인 셀레니움 코드가 짜여져 있을 것이다. 위 안정적인 셀레니움 구동을 위해 3가지 웨이트는 모두 필요하고 능숙하게 사용할 줄 알게 되는 순간 웹자동화는 아주 쉬워질 것이다.
implicitly Wait, Explicitly Wait. 다른 사이트에서 설명을 10번은 본 것 같은데, 이제야 확실히 이해가 됩니다. 감사합니다.
감사합니다 선생님!!
셀레니움 코드가 불안정한 거 같아 어떤 방법으로 기다리게 할지 고민이였는데, 덕분에 고민 해결했습니다!
Thank you, I’ve just been looking for information about this topic for a while and yours is
the best I’ve found out so far. But, what in regards to the conclusion? Are you sure about
the supply?