반응형
자동화 프로그램 제작 시 대부분은 requests로 API를 직접 요청하는것을 선호하지만, 그것이 불가한 경우에는 selenium을 활용한다.
Selenium
- 셀레니움은 웹 애플리케이션 자동화 및 테스트를 위한 포터블 프레임워크이다. 자바, C#, 펄 루비 등 다양한 언어들로 제공되며, 윈도우, 리눅스, macOS 플랫폼에서 사용가능하다.
순서
1. Selenium 클래스화 코드
2. 유용한 Selenium 클래스, 함수
3. Javascript
4. 자주발생하는 에러와 해결방법
1. Selenium 클래스화 코드
- web_setting.py (모듈명은 임의 부여)
# ------------------------------------------------------------------------------------ # Selnium Setting from selenium import webdriver from selenium.webdriver.chrome.service import Service from webdriver_manager.chrome import ChromeDriverManager class WebDriver: """ Selenium WebDriver 클래스 """ def init(self, engine='chrome') -> None: self.engine = engine.lower().strip() if self.engine == 'chrome': self.options = webdriver.ChromeOptions() def set_argument(self, headless=True, linux=True, maximize=True, user_agent=False, lang_kr=False, secret_mode=False, download_path=None): """ 셀레니움 드라이버 옵션 설정 headless: GUI 사용 안함 gui: GUI 사용안할 시 추가 maximize: 브라우저 최대화 lang_kr: 한국어 설정 secret_mode: 시크릿 모드 download_path: headless 시 파일 다운로드 경로 설정 """ # 시스템에 부착된 ... 로그 제거 self.options.add_experimental_option('excludeSwitches', ['enable-logging']) if headless: self.options.add_argument('--headless') if linux: # Bypass OS security model self.options.add_argument('--no-sandbox') self.options.add_argument("--disable-gpu") # overcome limited resource problems. 메모리가 부족해서 에러가 발생하는 것 막음 self.options.add_argument('--disable-dev-shm-usage') # 크롬 드라이버에 setuid를 하지 않음으로써 크롬의 충돌 막음 self.options.add_argument('--disable-setuid-sandbox') # disabling extensions self.options.add_argument("--disable-extensions") # 일단 좀 더 확인 필요 self.options.add_argument('--single-process') if download_path: p = { "download.default_directory": download_path, "download.prompt_for_download": False, "download.directory_upgrade": True, "safebrowsing.enabled": True } self.options.add_experimental_option("prefs", p) if maximize: self.options.add_argument("--start-maximized") if user_agent: self.options.add_argument("user-agent=Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36") if lang_kr: self.options.add_argument("--lang=ko_KR") if secret_mode: self.options.add_argument("--incognito") def get_driver(self) -> webdriver.Chrome: """ 셀레니움 웹드라이버 실행 """ if self.engine == 'chrome': driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=self.options) driver.maximize_window() driver.implicitly_wait(5) return driver
- 셀레니움 4버전으로 넘어오면서, chrome_path를 인자로 전달하는 것은 deprecated되어, Service 객체를 전달하도록 바뀌었음
- 크롬의 경우 webdriver-manager 패키지로 인해 chrome 버전에 맞춰서 chromedriver를 다시 설치해야 할 필요가 없어졌다.
- 이 Service객체에 ChromeDriverManager 객체 생성 후 chromedriver를 설치하도록 하면 ~/.wdm 하위에 크롬버전에 맞는 driver가 설치됨
사용법
webdriver = WebDriver()
webdriver.set_argument()
driver = webdriver.get_driver()
- 기본적으로 자주사용하는 argument는 True로 기본값을 주었고, 추가적으로 넣을만한 argument들은 기본값은 False로 두었다.
- 특정 파일을 다운로드 받는 로직이 들어갈 경우 headless모드에서는 download_path를 설정하여 해당 경로에 파일이 들어가도록 설정할 수 있다.
2. 유용한 Selenium 클래스, 함수
# ------------------------------------------------------------------------------------
# Selenium Utils
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.alert import Alert
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time
MAX_RETRY_COUNT = 5
def click_alert(d):
'''
알림창 처리
'''
retry = 0
while True:
try:
Alert(d).accept()
break
except:
time.sleep(1)
retry+=1
if retry == MAX_RETRY_COUNT:
# 에러 발생
assert False, '알림창 클릭 실패 !!'
- By: webdriver element 탐색 시 셀렉터로 검색할 것인지, xpath로 검색할 것인지 등을 정의할 수 있고, 실제로 찾을땐 결국 CSS 셀렉터로 변환
- Keys: 다양한 키 입력 (Ctrl, Shift, Enter 등)
- Alert: 사이트 알림창 처리
- WebDriverWait: Element를 찾는데 특정 시간만큼 기다림
- EC: WebDriverWait 보조클래스
- click_alert(): 특정 로직 실행 후 알림창이 뜨는것을 처리하기 위한 함수
3. Javascript
- Selenium은 웹 드라이버를 관리하는 클래스인 만큼, 자바스크립트 코드도 실행 가능
# Y까지 스크롤 driver.execute_script("window.scrollTo(0, Y)") # 끝까지 스크롤 driver.execute_script("window.scrollTo(0, document.body.scrollHeight);") # 특정 element로 스크롤 driver.execute_script("arguments[0].scrollIntoView();", element) # 클릭 driver.execute_script("arguments[0].click();", element)
- execute_script에는 element를 전달할 수 있다. (WebElement 객체)
- 특정 위치로 scroll하는 경우를 주로 사용. headless여도 element가 보이지 않을 경우 element안의 텍스트를 가져오지 못하는 경우 발생. 또는 클릭이 불가능한 상태도 있을 수 있음
- 그 외에는 사이트 내 함수 실행 정도
- 스크롤이 불가능한 경우, text 속성을 가져와야한다면,
1. get_attribute("innerText")
2. get_attribute("textContent")를 사용하는것도 해결방법이 될 수 있음
4. 자주발생하는 에러와 해결방법
1. Console 창 없애기
- 사실 이건 어느순간부터 설정안해도 된 것 같음 옛날버전 셀레니움 사용 시 발생
from selenium.webdriver.chrome.service import Service
from subprocess import CREATE_NO_WINDOW
service = Service('path/to/chromedriver')
service.creationflags = CREATE_NO_WINDOW
driver = webdriver.Chrome(service=service)
2. Missing X server or $DISPLAY The platform failed to initialize. Exiting.
- GUI가 사용불가능한 환경에서 발생했음 (AWS EC2)
options.add_argument('--headless')
3. Chrome 미설치 오류
- 에러 메시지는 확인 못함, 확인 시 추가하겠음
# Chrome 설치
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
sudo apt install ./google-chrome-stable_current_amd64.deb
google-chrome --version
4. This version of ChromeDriver only supports Chrome version xxx
- chromedriver와 chrome 버전이 다를경우 발생
- webdriver_manager 패키지 사용하면 해결, 혹은
google-chrome --version
명령어로 버전 확인 후, 맞는 chromedriver 설치
5. DevToolsActivePort file doesn't exist (ChromeDriver is assuming that Chrome has crashed)
- 구글링 결과 정확한 원인은 파악되지 않았으나 경험에 근거하면 Ubuntu EC2에서 발생한 문제, 웹드라이버 여러개 실행했을 때 발생했던 것 같음, 혹은 드라이버를 완전히 종료하지 않았을 경우 발생할수도 있음
options.add_argument('--headless') self.options.add_argument('--disable-dev-shm-usage') self.options.add_argument('--single-process')
- 대충 위의 세가지 옵션으로 해결이 가능하다. 혹은 프로그램이 에러로 인해 종료될 때, driver를 종료해주는 옵션으로도 어느정도는 해결이 가능할 것
# webdriver 클래스로 driver객체를 생성했다고 가정 # 1. 예외처리문 활용 try: driver = webdriver.get_driver() except Exception as e: print(e) finally: driver.quit() # 2. 클래스의 생성자, 소멸자 활용 class Selenium: def init(self): self.driver = webdriver.get_driver() def __del__(self): self.driver.quit()
그 외 기타 오류 댓글, 오류발견 시 추가하겠음...
- 오류들을 해결하기만하고 따로 적어두진 않았음..
- 대부분은 환경에 맞게 argument 설정만 잘해줘도 해결되는 경우가 대부분
반응형
'슬기로운 개발자생활 > 크롤링' 카테고리의 다른 글
[데이터 크롤링] 네이버 뉴스 기사 데이터 크롤링 방법 + 자동화 (4) | 2020.11.27 |
---|