작성일:

Python Selenium 이용한 웹 스크래핑 방법, 웹 자동화

Selenium 가이드: 웹 자동화

1. Selenium 이란?

Selenium은 웹 브라우저 자동화를 위한 아래와 같은 용도

  • 웹 애플리케이션 테스트 자동화
  • 웹 스크래핑 및 데이터 추출
  • 반복적인 웹 작업 자동화
  • 크로스 브라우저 테스팅

주요 기능

  • 브라우저 제어: 다양한 브라우저에서 웹 페이지를 자동으로 로드하고 조작할 수 있습니다.
  • DOM 조작: 웹 페이지의 DOM 요소를 검색하고 조작할 수 있습니다.
  • 폼 자동화: 폼을 자동으로 작성하고 제출할 수 있습니다.
  • 스크린샷 캡처: 웹 페이지의 스크린샷을 캡처할 수 있습니다.
  • 헤드리스 모드: 브라우저 창을 띄우지 않고 백그라운드에서 작업을 수행할 수 있습니다.

Selenium은 다양한 프로그래밍 언어를 지원하며, 웹 개발자와 QA 엔지니어들에게 필수적인 도구로 사용

2. 지원하는 웹 드라이버 종류

Selenium은 다양한 웹 브라우저를 지원합니다. 주요 웹 드라이버는 다음과 같습니다:

  • ChromeDriver (Google Chrome)
  • GeckoDriver (Mozilla Firefox)
  • EdgeDriver (Microsoft Edge)
  • SafariDriver (Apple Safari)
  • IEDriverServer (Internet Explorer)

각 브라우저의 버전에 맞는 웹 드라이버를 설치해야 합니다.

3. 기본적인 웹 페이지 호출 방법 (Chrome 드라이버 사용)

$ pip install selenium webdriver_manager

Chrome 드라이버를 사용하여 웹 페이지를 호출하는 기본적인 Python 코드는 다음과 같습니다:

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager

## WebDriver 객체 생성
## ChromeDriverManager().install():
##   webdriver_manager 라이브러리는 현재 시스템과 호환되는 ChromeDriver 버전을 찾아서 다운로드
##   다운로드된 ChromeDriver 파일은 로컬에 저장되며, 해당 파일의 경로를 반환
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))

## 웹 페이지 열기
driver.get('https://www.example.com')

## 브라우저 종료
driver.quit()

]

4. DOM 을 찾기 위한 find_element 사용법

Selenium은 다양한 방법으로 웹 요소를 찾을 수 있습니다. 주요 메서드는 다음과 같습니다:

메서드 설명
find_element(By.ID, 'id') ID 속성으로 요소 찾기
find_element(By.NAME, 'name') name 속성으로 요소 찾기
find_element(By.XPATH, 'xpath') XPath로 요소 찾기
find_element(By.CSS_SELECTOR, 'css_selector') CSS 선택자로 요소 찾기
find_element(By.CLASS_NAME, 'class_name') 클래스 이름으로 요소 찾기
find_element(By.TAG_NAME, 'tag_name') HTML 태그 이름으로 요소 찾기
find_element(By.LINK_TEXT, 'link_text') 링크 텍스트로 요소 찾기
find_element(By.PARTIAL_LINK_TEXT, 'partial_link_text') 부분적인 링크 텍스트로 요소 찾기
  • By.ID는 가장 빠르고 신뢰할 수 있는 방법이지만, 모든 요소에 고유한 ID가 있는 것은 아닙니다.
  • By.XPATH는 매우 유연하지만, 복잡한 XPath는 성능에 영향을 줄 수 있습니다.
  • By.CSS_SELECTORBy.XPATH보다 일반적으로 더 빠르고 읽기 쉽습니다.
  • By.LINK_TEXTBy.PARTIAL_LINK_TEXT는 특히 네비게이션 테스트에 유용합니다.

예시

from selenium.webdriver.common.by import By

## ID로 요소 찾기
element = driver.find_element(By.ID, 'search-input')

## 여러 요소 찾기
# 1. ID로 요소 찾기
elements_by_id = driver.find_elements(By.CSS_SELECTOR, '#example-id')
for element in elements_by_id:
    print(f"ID: {element.text}")

# 2. 클래스명으로 요소 찾기
elements_by_class = driver.find_elements(By.CSS_SELECTOR, '.example-class')
for element in elements_by_class:
    print(f"Class: {element.text}")

# 3. 태그명으로 요소 찾기
elements_by_tag = driver.find_elements(By.CSS_SELECTOR, 'div')
for element in elements_by_tag:
    print(f"Tag: {element.text}")

# 4. 계층 구조로 요소 찾기 (자식 요소)
elements_by_hierarchy = driver.find_elements(By.CSS_SELECTOR, 'div > p')
for element in elements_by_hierarchy:
    print(f"Hierarchy (child): {element.text}")

# 5. 계층 구조로 요소 찾기 (후손 요소)
elements_by_descendant = driver.find_elements(By.CSS_SELECTOR, 'div p')
for element in elements_by_descendant:
    print(f"Hierarchy (descendant): {element.text}")

# 6. 여러 클래스 조합으로 요소 찾기
elements_by_multiple_classes = driver.find_elements(By.CSS_SELECTOR, '.class1.class2')
for element in elements_by_multiple_classes:
    print(f"Multiple Classes: {element.text}")

# 7. 속성 값으로 요소 찾기
elements_by_attribute = driver.find_elements(By.CSS_SELECTOR, '[attribute-name="value"]')
for element in elements_by_attribute:
    print(f"Attribute: {element.text}")

5. 주요 DOM 조작 기능

find_elements 메서드를 사용하여 여러 요소가 조회되면 모든 일치하는 요소를 리스트(배열)로 처리 해야함

click() : 요소를 클릭

button = driver.find_element(By.ID, 'submit-button')
button.click()

send_keys() : 텍스트를 입력

input_field = driver.find_element(By.NAME, 'username')
input_field.send_keys('myusername')

text : 요소의 텍스트

paragraph = driver.find_element(By.CLASS_NAME, 'content')
print(paragraph.text)

get_attribute : 요소의 속성 값을 가져옵니다.

link = driver.find_element(By.TAG_NAME, 'a')
href = link.get_attribute('href')

6. 동적 웹 페이지 처리: WebDriverWait 사용

동적 웹 페이지에서는 요소가 즉시 로드되지 않을 수 있습니다. 이런 경우 WebDriverWait를 사용하여 특정 조건이 만족될 때까지 대기할 수 있습니다.

WebDriverWait 기본 사용법

from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By

# 최대 10초 동안 대기하며, 'a.ClassName' 선택자를 가진 요소가 나타날 때까지 기다립니다.
node = WebDriverWait(driver, 10).until(
    EC.presence_of_element_located((By.CSS_SELECTOR, "a.ClassName"))
)

이 코드는 다음과 같은 기능을 수행합니다:

  1. 최대 10초 동안 대기합니다.
  2. CSS 선택자 “a.ClassName”에 해당하는 요소가 DOM에 존재할 때까지 기다립니다.
  3. 요소가 발견되면 해당 요소를 반환합니다.
  4. 지정된 시간 내에 요소가 나타나지 않으면 TimeoutException을 발생시킵니다.

주요 Expected Conditions

WebDriverWait와 함께 사용할 수 있는 주요 Expected Conditions는 다음과 같습니다:

조건 설명
presence_of_element_located 요소가 DOM에 존재하는지 확인
visibility_of_element_located 요소가 보이는지 확인
element_to_be_clickable 요소가 클릭 가능한지 확인
text_to_be_present_in_element 요소에 특정 텍스트가 있는지 확인
title_contains 페이지 제목에 특정 텍스트가 포함되어 있는지 확인

예시 코드

from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.common.exceptions import TimeoutException

driver = webdriver.Chrome()
driver.get("https://example.com")

try:
    # 클릭 가능한 버튼을 기다립니다.
    button = WebDriverWait(driver, 10).until(
        EC.element_to_be_clickable((By.ID, "submit-button"))
    )
    button.click()
    
    # 특정 텍스트가 포함된 요소를 기다립니다.
    element = WebDriverWait(driver, 10).until(
        EC.text_to_be_present_in_element((By.CLASS_NAME, "result"), "성공")
    )
    print("작업이 성공적으로 완료되었습니다.")
    
except TimeoutException:
    print("요소를 찾는 데 시간이 초과되었습니다.")

finally:
    driver.quit()
  • 이 방법을 사용하면 동적으로 로드되는 웹 페이지의 요소를 안정적으로 처리할 수 있습니다. 페이지 로딩 시간이 일정하지 않거나, AJAX 요청으로 콘텐츠가 동적으로 변경되는 경우에 특히 유용합니다.
  • WebDriverWait를 사용하면 명시적 대기를 구현할 수 있어, 불필요한 고정 대기 시간을 줄이고 테스트의 안정성과 효율성을 높일 수 있습니다[1].

7. Headless 모드 및 기타 옵션

Headless 모드는 GUI 없이 브라우저를 실행하는 방법으로 서버 환경이나 백그라운드 작업에 유용

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from webdriver_manager.chrome import ChromeDriverManager

chrome_options = Options()
chrome_options.add_argument('--headless')
chrome_options.add_argument('--disable-gpu') # 필요한 경우
chrome_options.add_argument('user-agent=Mozilla/5.0 ...') # User-Agent 설정

driver = driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=chrome_options)
driver.get('https://example.com')

print(driver.title)

driver.quit()

기타 유용한 옵션:

  • --start-maximized: 브라우저를 최대화하여 시작
  • --disable-extensions: 확장 프로그램 비활성화
  • --disable-gpu: GPU 하드웨어 가속 비활성화
  • --no-sandbox: 샌드박스 비활성화 (주의: 보안상 위험할 수 있음)
  • window-size=1200x600: 브라우저 창 크기 설정
  • user-agent=Mozilla/5.0 ...: User-Agent 설정

8. 원격 디버깅: –remote-debugging-port 옵션 사용

Chrome 브라우저의 원격 디버깅 기능을 활용하면 Selenium 스크립트 실행 중 브라우저의 상태를 실시간으로 검사하고 디버깅할 수 있습니다. 이는 복잡한 웹 애플리케이션 자동화 시 매우 유용합니다.

–remote-debugging-port 옵션 사용법

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from webdriver_manager.chrome import ChromeDriverManager

chrome_options = Options()
chrome_options.add_argument('--remote-debugging-port=9222')

driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=chrome_options)
driver.get('https://example.com')

이 코드는 다음과 같은 기능을 수행합니다:

  1. Chrome 브라우저를 9222 포트에서 원격 디버깅이 가능한 상태로 실행합니다.
  2. 개발자는 Chrome DevTools Protocol을 통해 브라우저에 연결하여 디버깅할 수 있습니다.

원격 디버깅의 장점

  1. 실시간 상태 확인: 자동화 스크립트 실행 중 브라우저의 상태를 실시간으로 확인할 수 있습니다.
  2. 네트워크 트래픽 분석: 페이지 로드 시 발생하는 네트워크 요청을 분석할 수 있습니다.
  3. JavaScript 디버깅: 페이지에서 실행되는 JavaScript 코드를 디버깅할 수 있습니다.
  4. DOM 요소 검사: 동적으로 생성되는 DOM 요소를 실시간으로 검사할 수 있습니다.

주의사항

  • 원격 디버깅 포트를 열면 보안 위험이 있을 수 있으므로, 개발 환경에서만 사용하고 프로덕션 환경에서는 사용을 피해야 합니다.
  • 포트 번호는 다른 프로세스와 충돌하지 않도록 주의해야 합니다.

원격 디버깅 연결 : Shell 에서 Chrome 브라우저 실행

원격 디버깅 연결을 위해 Chrome 브라우저를 Shell에서 직접 실행

  • Windows
    "C:\Program Files\Google\Chrome\Application\chrome.exe"
    
  • macOS
    /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome
    
  • Linux
    google-chrome
    

원격 디버깅 포트 열기

  • 원격 디버깅을 위해 특정 포트를 열고 Chrome을 실행
    google-chrome --remote-debugging-port=9222
    

기타 유용한 옵션

  • 사용자 프로필 디렉토리 설정:
    google-chrome --remote-debugging-port=9222 --user-data-dir=/tmp/chrome
    
    • 독립적인 프로필 생성: 기존 Chrome 인스턴스와 충돌 방지
    • 보안 강화: 기존 사용자 데이터 보호
    • 성능 및 안정성 향상
  • 헤드리스 모드로 실행:
    google-chrome --headless
    
  • 특정 URL로 시작:
    google-chrome --headless https://example.com
    
  • User Agent 설정:
    google-chrome --user-agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36"
    
  • 창 크기 설정:
    google-chrome --window-size=1200,800
    
  • 확장 프로그램 비활성화:
    google-chrome --disable-extensions
    

주의사항

  • 위의 명령어들은 Chrome이 기본 설치 경로에 있다고 가정합니다. 만약 다른 경로에 설치되어 있다면 해당 경로를 사용해야 합니다.
  • 일부 옵션은 보안상의 이유로 주의해서 사용해야 합니다. 특히 --no-sandbox 옵션은 보안을 약화시킬 수 있으므로 개발 환경에서만 사용해야 합니다.
  • 헤드리스 모드를 사용할 때는 GUI가 없으므로 콘솔 출력을 통해 결과를 확인해야 합니다.

댓글남기기