[Python] Selenium으로 웹 브라우저 제어하기 - (1)
웹 브라우저 제어하기
웹 드라이버까지 준비가 되었으면 이제 셀레니움으로 웹 브라우저를 제어해보겠습니다. 먼저 셀레니움을 사용하여 웹 브라우저를 시작하여 원하는 사이트에 접속한 다음 브라우저를 종료하는 방법을 알아보겠습니다.
from selenium import webdriver
# 웹드라이버 자동 관리를 통해 크롬 드라이버 객체 생성
driver = webdriver.Chrome()
# 웹 브라우저를 실행하여 지정한 url에 접속
driver.get("http://www.google.com")
# 작업 완료 후 브라우저 종료
driver.quit()
이 코드는 크롬 웹 드라이버를 사용하여 크롬 브라우저를 열고, 주어진 URL로 이동한 다음, 작업이 완료되면 브라우저를 종료합니다. `quit()`으로 브라우저를 종료하지 않으면 작업 완료 후에도 해당 브라우저 창이 닫히지 않고 그대로 남아있게 됩니다.
위의 코드를 실행하면 크롬 웹 브라우저가 실행되는데 이때 열리는 브라우저의 크기는 마지막으로 사용자가 닫았던 웹 브라우저 창의 크기로 자동으로 설정됩니다. 브라우저의 크기를 변경하는 것도 가능합니다. `set_window_size(width, height)` 메서드를 사용하면, 너비(width)와 높이(height) 값을 설정하여 셀레니움에서 이미 실행 중인 브라우저 창을 원하는 크기로 조정할 수 있습니다.
from selenium import webdriver
driver = webdriver.Chrome()
driver.get("http://www.google.com")
# 브라우저 창의 크기를 너비 800픽셀, 높이 600픽셀로 설정
driver.set_window_size(800, 600)
driver.quit()
창의 크기를 최대화하고 싶다면 `maxmize_window()` 메서드를 사용합니다. `maximize_window` 메소드는 셀레니움에서 브라우저 창을 현재 시스템의 최대화 상태로 만드는데 사용됩니다.
from selenium import webdriver
driver = webdriver.Chrome()
driver.get("http://www.google.com")
# 브라우저 창을 최대화
driver.maximize_window()
driver.quit()
위의 두 가지 방법은 먼저 크롬 브라우저를 열고 나서 크기를 변경한다는 특징이 있는데, 그렇지 않고 브라우저를 열 때 창의 크기도 미리 설정한 값으로 열고 싶다면 `크롬 옵션(ChromeOptions)`을 사용해야 합니다.
from selenium import webdriver
# 옵션 설정을 위해 option 객체 생성
options = webdriver.ChromeOptions()
# 창 크기를 너비 800픽셀, 높이 600픽셀로 설정
options.add_argument("window-size=800,600")
# 옵션을 적용하여 드라이버 객체 생성
driver = webdriver.Chrome(options=options)
driver.get("http://www.google.com")
driver.quit()
위의 코드를 실행하면 브라우저가 열릴 때부터 800x600의 크기인 것을 확인할 수 있습니다. 위의 코드에서와 같이 `window-size`값을 너비(width), 높이(height) 순으로 전달합니다. 만약 이 방법을 사용해 브라우저 창의 크기를 전체 화면으로 실행하려면 위의 코드에서 크기 설정 부분 대신에 `options.add_argument("--start-fullscreen")`를 사용하면 됩니다.
코드를 실행했을 때 생각보다 작업이 빨리 진행되고 바로 브라우저가 닫혀서 눈으로 확인하기 어렵다고 느낄 수도 있습니다. 이럴 경우에 시간을 지연시키는 방법이 몇 가지 있는데 그 중 하나인 `ime.sleep()`함수에 대해 알아보겠습니다.
time.sleep()
파이썬의 time 모듈에 포함된 함수로, 프로그램 실행을 지정된 시간 동안 일시 중지할 수 있게 해줍니다. 이 함수는 주로 스크립트의 실행을 잠시 멈추게 하여, 네트워크 요청이 완료되기를 기다리거나 사용자 입력을 받기 전에 일시적으로 대기하는 등의 목적으로 사용됩니다. 웹에서 데이터를 가져오거나 웹 작업을 자동화할 때 페이지가 완전히 로드되거나 특정 요소가 화면에 나타날 때까지 기다려야하는 경우가 많이 있는데, 이럴 때 time.sleep()을 사용하여 일정 시간을 기다린 다음 이후의 작업을 진행하도록 할 수 있습니다.
from selenium import webdriver
import time # 시간 관련 함수를 사용하기 위해 time 모듈을 임포트
driver = webdriver.Chrome()
driver.get("http://www.google.com")
# 브라우저 창의 크기를 너비 800픽셀, 높이 600픽셀로 설정
driver.set_window_size(800, 600)
time.sleep(5) # 5초 동안 대기
driver.quit()
위의 코드를 실행하면 브라우저가 종료되기 전에 5초의 시간이 있어서 브라우저 창을 자세히 볼 수 있는데, 주소창 아래에 "Chrome이 자동화된 테스트 소프트웨어에 의해 제어되고 있습니다."라는 문구를 확인할 수 있을 것입니다. 해당 크롬 브라우저가 사용자의 직접 제어가 아닌 자동화된 프로그래밍에 의해 작동하고 있다는 의미입니다.
웹 페이지 내 요소 선택하기
특정 요소를 찾은 다음에는 마우스로 클릭을 하거나 텍스트를 입력하는 등의 동작을 수행할 수 있기 때문에 정확히 원하는 요소에 접근하는 것이 중요합니다. 정적 웹페이지에서는 `Beautiful Soup`의 `find`나 `select`와 같은 메서드로 원하는 요소를 찾은 것처럼 셀레니움에서도 특정 요소를 찾는 `find_element`와 `find_elements` 메서드를 제공하고 있습니다.
`find_element` 메서드는 주어진 검색 기준(예: ID, 클래스 이름, 태그 이름 등)에 해당하는 첫 번째 요소를 반환합니다. 요소가 존재하지 않을 경우 `NoSuchElementException` 예외가 발생합니다. `find_element`를 사용할 때는 `find_element(By.속성, "속성값")`의 형태로 찾고자하는 기준 속성과 조건에 해당하는 속성값을 전달합니다.
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get("http://www.google.com")
# class 이름이 "gb_d"인 첫번째 요소 찾기
driver.find_element(By.CLASS_NAME, "gb_d")
driver.quit()
클래스 이름이 "gb_d"인 요소를 확인해 보기 위해 실제 구글 메인 페이지에서 "검사"를 실행해보겠습니다.
"Google 앱" 버튼의 클래스 이름이 "gb_d"인 것을 볼 수 있습니다. 따라서 위의 코드를 실행했을 때 해당 요소를 `find_element`가 찾았기 때문에 다음 코드인 브라우저 종료 단계로 넘어가 브라우저의 창이 닫힌 것이며, 만약 해당 요소가 페이지에 없었다면 `"NoSuchElementException"` 오류가 발생하고 `driver.quit()`으로 넘어가지 못하여 브라우저도 종료되지 않고 남아있게 됩니다.
`find_element`는 By 클래스를 사용해 웹 페이지에서 조건에 맞는 특정 요소를 찾습니다.
사용할 때는 By. 뒤에 찾고자하는 속성명을 넣고, 그 다음 인자로 요소의 식별값을 전달합니다. By 클래스의 주요 속성들은 다음과 같습니다.
위의 속성들을 사용할 때 `find_element` 대신 `find_elements` 메서드를 사용하면 조건을 만족하는 모든 요소를 찾아 리스트로 반환합니다. 요소가 없는 경우에는 빈 리스트를 반환하기 때문에 find_element 메서드와 달리 find_elements 메서드는 요소가 없어도 오류가 발생하지 않아 스크립트가 중단되지 않고 계속 실행될 수 있습니다.
웹 페이지 로드하기
본격적으로 웹 페이지에서 특정 요소를 찾아 추가 동작을 진행하는 방법을 학습하기 전에, 먼저 웹 페이지에서 필요한 요소가 완전히 로드될 때까지 대기하는 방법에 대해 알아보겠습니다. 브라우저에서 먼저 웹 페이지의 요소들을 모두 로드하고 나서야 페이지 내의 다양한 요소와 상호작용하며 필요한 작업을 수행할 수 있기 때문입니다.
예를 들어, 페이지 스크롤, 버튼 클릭, 텍스트 입력 등의 동작을 자동화했지만, 페이지의 로딩이 완료되지 않거나 필요한 요소가 화면에 나타나지 않을 경우 스크립트가 요소를 찾지 못하거나 에러를 일으킬 수 있습니다.
셀레니움은 요소가 로드될 때까지 대기하는 명시적 대기(Explicit Wait)와 암시적 대기(Implicit Wait) 메커니즘이 있습니다.
1) 명시적 대기(Explicit Wait)
명시적 대기는 특정 요소가 특정 조건(예: 요소가 클릭 가능해짐, 요소의 로딩이 완료됨 등)을 충족할 때까지 대기하도록 설정합니다. WebDriverWait 클래스와 expected_conditions 모듈을 함께 사용하여 구현되며, 특정 요소에 대한 대기 시간을 세밀하게 조절할 수 있습니다. 또한 각 요소 또는 상황에 맞게 대기 시간을 개별적으로 설정할 수 있어 효율적으로 사용할 수 있습니다.
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
# WebDriverWait 객체 생성 및 최대 대기 시간 설정
WebDriverWait(driver, 10).until(EC.메서드명((By.속성, "속성값")))
코드를 자세히 살펴보면, 먼저 `WebDriverWait` 객체를 생성합니다. WebDriverWait 객체를 생성할 때 두 개의 주요 인자가 필요한데, 하나는 브라우저와 상호 작용하는 데 사용할 웹 드라이버 객체이며, 두 번째 인자는 최대 대기 시간(초)입니다. 이 시간이 지나면 `TimeoutException`이 발생하여 조건이 만족되지 않았음을 알립니다.
`until` 메서드는 expected_conditions 모듈에서 제공하는 다양한 메서드 중 하나를 매개변수로 받아 해당 조건이 충족될 때까지 대기합니다. 조건이 충족되면 대기가 중단되고, 다음 코드 라인으로 넘어갑니다. EC.메서드명(())에서 EC가 ExpectedConditions의 약자입니다. '메서드명' 부분에 조건을 정의할 메서드를 넣어주고, 웹 요소를 찾는 기준 속성과 값을 (By.속성, "값")의 형태로 전달합니다.
대기할 조건을 지정하는 주요 메서드는 다음과 같습니다.
- `presence_of_element_located`
: 웹 페이지에 해당 요소가 존재하는지 확인할 때까지 대기합니다. HTML에 요소의 존재가 확인되어 다음의 작업으로 진행되더라도 해당 요소가 우리의 눈에는 보이지 않을 수도 있습니다. 요소가 HTML에는 존재하더라도 실제로 사용자에게 보이는지는 별개의 문제이며, CSS 스타일이나 JavaScript로 인해 보이지 않을 수도 있기 때문입니다. - `visibility_of_element_located`
: 해당 요소가 사용자에게 보이고, 실제로 클릭 가능하거나 상호작용 가능할 때까지 기다립니다. - `element_to_be_clickable`
: 해당 요소가 클릭 가능한 상태가 될 때까지 기다립니다. 클릭 가능한 상태란 요소가 화면에 보이고, 활성화되며, 클릭을 수신할 수 있는 상태일 때를 의미합니다.
명시적 대기를 사용할 때 주의할 점은 반드시 적절한 최대 대기 시간을 설정해야 한다는 것입니다. 대기 시간이 너무 짧으면 서버 지연 등으로 인해 예외가 발생할 수 있으며, 너무 길면 테스트의 효율성이 떨어지기 때문에 적절한 시간을 설정하는 것이 중요합니다.
2) 암시적 대기(Implicit Wait)
암시적 대기(implicit wait)는 셀레니움에서 웹 드라이버가 웹 요소를 찾을 수 없을 때, 지정된 시간 동안 자동으로 요소가 나타날 때까지 대기하도록 설정하는 방법입니다. 요소가 지정된 시간 내에 발견되면 대기를 중단하고 실행을 계속하며, 요소가 발견되지 않으면 예외를 발생시킵니다. 암시적 대기는 전역 설정으로, 한 번 암시적 대기를 설정하면 드라이버 세션이 종료될 때까지 적용됩니다. 또한 코드 내의 모든 요소 검색에 적용됩니다. 즉, 코드 전체에서 요소 검색에 대해서 모두 일관된 대기 시간이 적용됩니다.
from selenium import webdriver
driver = webdriver.Chrome()
driver.implicitly_wait(10) # 최대 10초 동안 대기
`implicitly_wait(10)`는 전체 코드 내에서 요소를 검색할 때마다 드라이버가 요소를 찾을 수 없을 때 최대 10초까지 기다리라는 명령입니다. 요소가 10초 내에 발견되면 즉시 다음 코드로 진행하며, 10초 동안 발견되지 않으면 `NoSuchElementException` 오류가 발생합니다.
`암시적 대기`는 웹 페이지 로딩 속도가 일정하고, 대부분의 요소가 비슷한 시간 안에 로드되는 경우에 일관된 대기 시간을 설정할 수 있어 유용합니다. 하지만 요소별로 상황에 따라 다른 대기 시간을 설정해야 할 경우나 더 복잡한 조건을 기다려야 할 때는 `명시적 대기`가 더 적합할 수 있습니다.