Selenium을 활용한 동적 웹 스크래핑 기술

동적 콘텐츠 수집을 위한 Selenium 활용법

Selenium은 주로 자동화 테스트에 사용되지만, JavaScript 기반으로 동적으로 렌더링되는 웹페이지를 크롤링할 때 매우 유용한 도구입니다. 이 방식은 화면에 실제로 표시되는 내용을 그대로 추출할 수 있어, 정적인 HTML 파싱만으로는 접근하기 어려운 사이트에서도 데이터를 효과적으로 수집할 수 있습니다.

사전 준비

Chrome 브라우저와 ChromeDriver를 설치해야 하며, Python 환경에서는 selenium 패키지를 설치합니다.

pip install selenium

드라이버 버전은 브라우저 버전과 일치해야 하며, 자동 업데이트를 방지하는 것이 안정적인 작동에 도움이 됩니다.

브라우저 인스턴스 생성

다음 코드는 다양한 옵션을 적용한 Chrome 브라우저 세션을 초기화합니다.

from selenium import webdriver

options = webdriver.ChromeOptions()
# 이미지 로딩 비활성화
options.add_experimental_option("prefs", {"profile.managed_default_content_settings.images": 2})
# 헤드리스 모드 활성화
options.add_argument("--headless")
# 사용자 에이전트 설정
options.add_argument("user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36")
# 자동화 탐지 우회 설정
options.add_experimental_option("excludeSwitches", ["enable-automation"])
options.add_experimental_option("useAutomationExtension", False)
# 프록시 서버 사용
options.add_argument("--proxy-server=192.168.0.28:808")

driver = webdriver.Chrome(executable_path="/path/to/chromedriver", options=options)
driver.get("https://example.com")
print(driver.page_source)

기본 동작 제어

페이지 탐색, 입력, 클릭 등의 기본 동작을 시뮬레이션할 수 있습니다.

driver.get("https://www.baidu.com")
search_box = driver.find_element_by_id("kw")
search_box.send_keys("Python")
search_button = driver.find_element_by_id("su")
search_button.click()

print(driver.current_url)
print(driver.get_cookies())
print(driver.page_source)

요소 찾기

Selenium은 다양한 방법으로 요소를 선택할 수 있습니다.

  • find_element_by_id(): ID로 요소 찾기
  • find_element_by_css_selector(): CSS 선택자 사용
  • find_element_by_xpath(): XPath 사용
  • find_element_by_class_name(): 클래스 이름 기반
  • find_element_by_tag_name(): 태그 이름 기반

또한 By 클래스를 사용하면 더 유연하게 조건을 지정할 수 있습니다.

from selenium.webdriver.common.by import By

element = driver.find_element(By.CSS_SELECTOR, ".search-input")

복수 요소 처리

조건에 맞는 모든 요소를 리스트 형태로 반환받으려면 find_elements_* 메서드를 사용합니다.

items = driver.find_elements_by_css_selector(".product-list li")
for item in items:
    print(item.text)

상호작용 및 액션 체인

입력 필드 조작이나 드래그 앤 드롭 같은 복잡한 동작도 가능합니다.

from selenium.webdriver import ActionChains

source = driver.find_element_by_id("drag-source")
target = driver.find_element_by_id("drop-target")
actions = ActionChains(driver)
actions.drag_and_drop(source, target).perform()

JavaScript 실행

스크롤 제어 등 Selenium에서 직접 제공하지 않는 기능은 JavaScript로 구현할 수 있습니다.

# 페이지 하단까지 스크롤
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")

# 특정 요소 위치로 스크롤
element = driver.find_element_by_id("target")
driver.execute_script("arguments[0].scrollIntoView();", element)

# 점진적 스크롤
for i in range(1, 8):
    driver.execute_script(f"window.scrollTo(0, {i * 600});")
    time.sleep(0.5)

프레임 전환

iframe 내부 요소에 접근하려면 명시적으로 전환해야 합니다.

driver.switch_to.frame("login-frame")
username_input = driver.find_element_by_id("username")
username_input.send_keys("user123")

대기 처리

비동기 로딩 요소를 안정적으로 처리하기 위해 대기 전략이 필요합니다.

암시적 대기

driver.implicitly_wait(10)  # 최대 10초까지 요소 발견 시도

명시적 대기

from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

wait = WebDriverWait(driver, 15)
search_input = wait.until(EC.presence_of_element_located((By.ID, "q")))
submit_btn = wait.until(EC.element_to_be_clickable((By.CLASS_NAME, "btn-search")))

탭 및 윈도우 관리

새 탭을 열고 전환하는 것도 가능합니다.

driver.execute_script("window.open('');")
tabs = driver.window_handles
driver.switch_to.window(tabs[1])
driver.get("https://another-site.com")

탐지 회피 기법

자동화 도구 감지를 피하기 위해 아래와 같은 설정을 추가할 수 있습니다.

options.add_argument("--disable-blink-features=AutomationControlled")
driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
    "source": """
        Object.defineProperty(navigator, 'webdriver', {
            get: () => false,
        });
    """
})

예외 처리

요소 미발견 또는 타임아웃 시 프로그램이 중단되지 않도록 예외 처리가 중요합니다.

from selenium.common.exceptions import TimeoutException, NoSuchElementException

try:
    driver.get("https://example.com")
except TimeoutException:
    print("페이지 로딩 시간 초과")

try:
    element = driver.find_element_by_id("nonexistent")
except NoSuchElementException:
    print("요소를 찾을 수 없음")

실전 예제: 상품 정보 수집

다음은 상품 목록을 스크롤하며 정보를 추출하는 예제입니다.

def collect_products(driver):
    def scroll_page():
        for i in range(1, 10):
            driver.execute_script(f"window.scrollTo(0, {i * 500});")
            time.sleep(random.uniform(0.5, 1.2))

    scroll_page()
    products = driver.find_elements_by_class_name("product-item")
    
    data = []
    for prod in products:
        title = prod.find_element_by_class_name("title").text
        price = prod.find_element_by_class_name("price").text
        location = prod.find_element_by_class_name("location").text
        data.append({"title": title, "price": price, "location": location})
    
    return data

태그: selenium Web Scraping python ChromeDriver Dynamic Content

8월 6일 21:06에 게시됨