跳至正文

웹 자동화

Selenium을 사용하면 웹 브라우저를 프로그래밍 방식으로 제어할 수 있습니다. 반복적인 웹 작업, 테스트, 데이터 수집 등을 자동화해보겠습니다.

설치하기

Selenium 설치

# Selenium 설치
pip install selenium

# WebDriver Manager (드라이버 자동 관리)
pip install webdriver-manager

브라우저 드라이버

Selenium은 브라우저를 제어하기 위해 드라이버가 필요합니다.

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

# Chrome 드라이버 자동 설치 및 실행
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))

# 또는 Firefox
from webdriver_manager.firefox import GeckoDriverManager
driver = webdriver.Firefox(service=Service(GeckoDriverManager().install()))

기본 사용법

브라우저 열고 닫기

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

# 브라우저 실행
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))

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

# 페이지 제목 출력
print(driver.title)

# 현재 URL 확인
print(driver.current_url)

# 브라우저 닫기
driver.quit() # 모든 창 닫기
# driver.close() # 현재 창만 닫기

요소 찾기

from selenium.webdriver.common.by import By

# ID로 찾기
element = driver.find_element(By.ID, "username")

# 클래스명으로 찾기
element = driver.find_element(By.CLASS_NAME, "btn-primary")

# 태그명으로 찾기
element = driver.find_element(By.TAG_NAME, "h1")

# CSS 선택자로 찾기
element = driver.find_element(By.CSS_SELECTOR, "div.container > p")

# XPath로 찾기
element = driver.find_element(By.XPATH, "//button[@type='submit']")

# 링크 텍스트로 찾기
element = driver.find_element(By.LINK_TEXT, "로그인")
element = driver.find_element(By.PARTIAL_LINK_TEXT, "자세히")

# 여러 요소 찾기 (리스트 반환)
elements = driver.find_elements(By.CLASS_NAME, "product-item")

요소와 상호작용

from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys

# 텍스트 입력
search_box = driver.find_element(By.NAME, "q")
search_box.send_keys("Python Selenium")

# Enter 키 입력
search_box.send_keys(Keys.RETURN)

# 버튼 클릭
button = driver.find_element(By.ID, "submit-btn")
button.click()

# 텍스트 가져오기
text = element.text

# 속성 가져오기
href = element.get_attribute("href")
class_name = element.get_attribute("class")

# 입력 필드 비우기
search_box.clear()

# 체크박스 선택 여부 확인
is_selected = checkbox.is_selected()

# 요소 표시 여부 확인
is_displayed = element.is_displayed()

# 요소 활성화 여부 확인
is_enabled = element.is_enabled()

대기 처리

암묵적 대기

# 페이지 로딩을 최대 10초까지 기다림
driver.implicitly_wait(10)

# 이후 모든 요소 찾기에 적용됨
element = driver.find_element(By.ID, "dynamic-content")

명시적 대기

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

# 특정 조건이 만족될 때까지 최대 10초 대기
wait = WebDriverWait(driver, 10)

# 요소가 나타날 때까지 대기
element = wait.until(
EC.presence_of_element_located((By.ID, "dynamic-element"))
)

# 요소가 클릭 가능할 때까지 대기
element = wait.until(
EC.element_to_be_clickable((By.ID, "submit-btn"))
)

# 요소가 보일 때까지 대기
element = wait.until(
EC.visibility_of_element_located((By.CLASS_NAME, "popup"))
)

# 제목에 특정 텍스트가 포함될 때까지 대기
wait.until(EC.title_contains("검색 결과"))

# 알림창이 나타날 때까지 대기
wait.until(EC.alert_is_present())

명시적 대기 조건

from selenium.webdriver.support import expected_conditions as EC

# 자주 사용되는 조건들
EC.presence_of_element_located() # 요소가 DOM에 존재
EC.visibility_of_element_located() # 요소가 보임
EC.element_to_be_clickable() # 요소가 클릭 가능
EC.invisibility_of_element_located() # 요소가 안 보임
EC.text_to_be_present_in_element() # 요소에 특정 텍스트 존재
EC.title_contains() # 제목에 텍스트 포함
EC.title_is() # 제목이 정확히 일치
EC.url_contains() # URL에 텍스트 포함
EC.alert_is_present() # 알림창 존재
EC.frame_to_be_available_and_switch_to_it() # 프레임 전환 가능

고급 기능

스크롤

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

# 특정 위치로 스크롤
driver.execute_script("window.scrollTo(0, 500);")

# 특정 요소까지 스크롤
element = driver.find_element(By.ID, "footer")
driver.execute_script("arguments[0].scrollIntoView();", element)

# 무한 스크롤 처리
import time

last_height = driver.execute_script("return document.body.scrollHeight")

while True:
# 끝까지 스크롤
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
time.sleep(2)

# 새로운 높이 계산
new_height = driver.execute_script("return document.body.scrollHeight")

if new_height == last_height:
break

last_height = new_height

드롭다운 선택

from selenium.webdriver.support.ui import Select

# Select 객체 생성
select_element = driver.find_element(By.ID, "country")
select = Select(select_element)

# 인덱스로 선택
select.select_by_index(2)

# 값으로 선택
select.select_by_value("kr")

# 보이는 텍스트로 선택
select.select_by_visible_text("대한민국")

# 현재 선택된 옵션
selected_option = select.first_selected_option
print(selected_option.text)

# 모든 옵션 가져오기
all_options = select.options
for option in all_options:
print(option.text)

창/탭 관리

# 현재 창 핸들
current_window = driver.current_window_handle

# 모든 창 핸들
all_windows = driver.window_handles

# 새 탭 열기
driver.execute_script("window.open('https://www.example.com');")

# 새 창으로 전환
driver.switch_to.window(driver.window_handles[1])

# 원래 창으로 돌아가기
driver.switch_to.window(current_window)

# 현재 창 닫기
driver.close()

iframe 처리

# iframe으로 전환
iframe = driver.find_element(By.ID, "iframe-id")
driver.switch_to.frame(iframe)

# 또는 인덱스로
driver.switch_to.frame(0)

# iframe 내부 요소 접근
element = driver.find_element(By.ID, "element-in-iframe")

# 기본 컨텐츠로 돌아가기
driver.switch_to.default_content()

알림창 처리

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

# 알림창 대기
wait = WebDriverWait(driver, 10)
alert = wait.until(EC.alert_is_present())

# 알림창 텍스트 가져오기
alert_text = alert.text
print(alert_text)

# 알림창 수락 (OK 버튼)
alert.accept()

# 알림창 취소 (Cancel 버튼)
alert.dismiss()

# Prompt에 텍스트 입력
alert.send_keys("입력할 텍스트")
alert.accept()

스크린샷

# 전체 페이지 스크린샷
driver.save_screenshot("screenshot.png")

# 특정 요소만 스크린샷
element = driver.find_element(By.ID, "logo")
element.screenshot("element.png")

# 바이너리 데이터로 가져오기
screenshot = driver.get_screenshot_as_png()

쿠키 관리

# 모든 쿠키 가져오기
cookies = driver.get_cookies()
print(cookies)

# 특정 쿠키 가져오기
cookie = driver.get_cookie("session_id")

# 쿠키 추가
driver.add_cookie({
"name": "test_cookie",
"value": "test_value"
})

# 쿠키 삭제
driver.delete_cookie("cookie_name")

# 모든 쿠키 삭제
driver.delete_all_cookies()

실전 예제

1. 로그인 자동화

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
import time

def auto_login(url, username, password):
"""웹사이트 자동 로그인"""

# 브라우저 실행
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))

try:
# 로그인 페이지 열기
driver.get(url)
print(f"접속: {url}")

# 페이지 로딩 대기
wait = WebDriverWait(driver, 10)

# 아이디 입력
username_field = wait.until(
EC.presence_of_element_located((By.ID, "username"))
)
username_field.clear()
username_field.send_keys(username)
print("아이디 입력 완료")

# 비밀번호 입력
password_field = driver.find_element(By.ID, "password")
password_field.clear()
password_field.send_keys(password)
print("비밀번호 입력 완료")

# 로그인 버튼 클릭
login_button = driver.find_element(By.CSS_SELECTOR, "button[type='submit']")
login_button.click()
print("로그인 버튼 클릭")

# 로그인 성공 확인 (URL 변경 대기)
wait.until(EC.url_changes(url))

# 로그인 후 페이지 확인
if "dashboard" in driver.current_url or "home" in driver.current_url:
print("로그인 성공!")
return driver
else:
print("로그인 실패 - URL 확인 필요")
return None

except Exception as e:
print(f"오류 발생: {e}")
driver.quit()
return None

# 사용 예제
driver = auto_login(
url="https://example.com/login",
username="user@example.com",
password="password123"
)

if driver:
# 로그인 후 작업 수행
time.sleep(3)
driver.quit()

2. 폼 자동 작성

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import Select
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
import time

def fill_registration_form(form_data):
"""회원가입 폼 자동 작성"""

driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))

try:
# 회원가입 페이지 열기
driver.get("https://example.com/register")

# 기본 정보 입력
driver.find_element(By.ID, "name").send_keys(form_data['name'])
driver.find_element(By.ID, "email").send_keys(form_data['email'])
driver.find_element(By.ID, "phone").send_keys(form_data['phone'])

# 비밀번호 입력
driver.find_element(By.ID, "password").send_keys(form_data['password'])
driver.find_element(By.ID, "confirm_password").send_keys(form_data['password'])

# 성별 선택 (라디오 버튼)
if form_data['gender'] == 'male':
driver.find_element(By.ID, "male").click()
else:
driver.find_element(By.ID, "female").click()

# 생년월일 선택 (드롭다운)
year_select = Select(driver.find_element(By.ID, "birth_year"))
year_select.select_by_value(form_data['birth_year'])

month_select = Select(driver.find_element(By.ID, "birth_month"))
month_select.select_by_value(form_data['birth_month'])

day_select = Select(driver.find_element(By.ID, "birth_day"))
day_select.select_by_value(form_data['birth_day'])

# 관심사 선택 (체크박스)
for interest in form_data['interests']:
checkbox = driver.find_element(By.CSS_SELECTOR, f"input[value='{interest}']")
if not checkbox.is_selected():
checkbox.click()

# 약관 동의
terms_checkbox = driver.find_element(By.ID, "terms")
driver.execute_script("arguments[0].click();", terms_checkbox)

# 스크린샷 저장
driver.save_screenshot("form_filled.png")
print("폼 작성 완료 - 스크린샷 저장됨")

# 제출 버튼 클릭 (주석 처리 - 실제로는 제출 안 함)
# driver.find_element(By.ID, "submit").click()

time.sleep(2)

finally:
driver.quit()

# 사용 예제
form_data = {
'name': '홍길동',
'email': 'hong@example.com',
'phone': '010-1234-5678',
'password': 'SecurePass123!',
'gender': 'male',
'birth_year': '1990',
'birth_month': '5',
'birth_day': '15',
'interests': ['sports', 'music', 'travel']
}

fill_registration_form(form_data)

3. 웹 스크래핑 (동적 페이지)

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
import time
import csv

def scrape_product_list(url, max_pages=3):
"""상품 목록 스크래핑"""

# Headless 모드 (브라우저 창 안 띄움)
options = webdriver.ChromeOptions()
options.add_argument('--headless')
options.add_argument('--disable-gpu')
options.add_argument('--no-sandbox')

driver = webdriver.Chrome(
service=Service(ChromeDriverManager().install()),
options=options
)

products = []

try:
for page in range(1, max_pages + 1):
print(f"페이지 {page} 스크래핑 중...")

# 페이지 로드
page_url = f"{url}?page={page}"
driver.get(page_url)

# 페이지 로딩 대기
wait = WebDriverWait(driver, 10)
wait.until(
EC.presence_of_all_elements_located((By.CLASS_NAME, "product-item"))
)

# 스크롤하여 모든 이미지 로드
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
time.sleep(2)

# 상품 정보 추출
product_elements = driver.find_elements(By.CLASS_NAME, "product-item")

for element in product_elements:
try:
# 상품명
name = element.find_element(By.CLASS_NAME, "product-name").text

# 가격
price = element.find_element(By.CLASS_NAME, "product-price").text
price = price.replace('원', '').replace(',', '')

# 평점
rating = element.find_element(By.CLASS_NAME, "rating").get_attribute("data-rating")

# 링크
link = element.find_element(By.TAG_NAME, "a").get_attribute("href")

products.append({
'name': name,
'price': int(price),
'rating': float(rating),
'link': link
})

except Exception as e:
print(f"상품 추출 오류: {e}")
continue

print(f"페이지 {page}: {len(product_elements)}개 상품 수집")

# 다음 페이지 대기
time.sleep(1)

finally:
driver.quit()

return products

def save_to_csv(products, filename):
"""CSV 파일로 저장"""
with open(filename, 'w', newline='', encoding='utf-8-sig') as f:
writer = csv.DictWriter(f, fieldnames=['name', 'price', 'rating', 'link'])
writer.writeheader()
writer.writerows(products)

print(f"\n총 {len(products)}개 상품 저장: {filename}")

# 사용 예제
products = scrape_product_list("https://example.com/products", max_pages=3)
save_to_csv(products, "products.csv")

# 가격 순으로 정렬
products_sorted = sorted(products, key=lambda x: x['price'])
print("\n최저가 상품 TOP 5:")
for product in products_sorted[:5]:
print(f"{product['name']}: {product['price']:,}원 (평점: {product['rating']})")

4. 파일 다운로드 자동화

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
import os
import time

def download_files(url, download_dir):
"""파일 자동 다운로드"""

# 다운로드 폴더 생성
os.makedirs(download_dir, exist_ok=True)

# Chrome 옵션 설정
options = webdriver.ChromeOptions()
prefs = {
"download.default_directory": download_dir,
"download.prompt_for_download": False,
"download.directory_upgrade": True,
"safebrowsing.enabled": True
}
options.add_experimental_option("prefs", prefs)

driver = webdriver.Chrome(
service=Service(ChromeDriverManager().install()),
options=options
)

try:
driver.get(url)
print(f"접속: {url}")

# 다운로드 링크 찾기
wait = WebDriverWait(driver, 10)
download_links = wait.until(
EC.presence_of_all_elements_located((By.CSS_SELECTOR, "a[download]"))
)

print(f"{len(download_links)}개의 다운로드 링크 발견")

# 각 파일 다운로드
for i, link in enumerate(download_links, 1):
filename = link.get_attribute("download") or f"file_{i}"
print(f"{i}. 다운로드 중: {filename}")

link.click()
time.sleep(2) # 다운로드 시작 대기

# 모든 다운로드 완료 대기
print("\n다운로드 완료 대기 중...")
time.sleep(5)

# 다운로드된 파일 확인
downloaded_files = os.listdir(download_dir)
print(f"\n다운로드 완료: {len(downloaded_files)}개 파일")

for file in downloaded_files:
file_path = os.path.join(download_dir, file)
size_mb = os.path.getsize(file_path) / (1024 * 1024)
print(f" - {file} ({size_mb:.2f} MB)")

finally:
driver.quit()

# 사용 예제
download_files(
url="https://example.com/downloads",
download_dir="/Users/username/Downloads/auto_downloads"
)

5. 반복 작업 스케줄링

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
import time
from datetime import datetime

def check_stock_availability(url, product_name):
"""재고 확인 자동화"""

options = webdriver.ChromeOptions()
options.add_argument('--headless')

driver = webdriver.Chrome(
service=Service(ChromeDriverManager().install()),
options=options
)

try:
driver.get(url)

# 상품 검색
search_box = driver.find_element(By.NAME, "q")
search_box.send_keys(product_name)
search_box.submit()

time.sleep(2)

# 재고 상태 확인
try:
stock_status = driver.find_element(By.CLASS_NAME, "stock-status").text

timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")

if "재고있음" in stock_status or "구매가능" in stock_status:
print(f"[{timestamp}] ✓ {product_name}: 재고 있음!")
# 알림 보내기 (예: 이메일, 메시지 등)
send_notification(product_name)
return True
else:
print(f"[{timestamp}] ✗ {product_name}: 품절")
return False

except:
print(f"[{timestamp}] ? {product_name}: 상태 확인 불가")
return None

finally:
driver.quit()

def send_notification(product_name):
"""알림 전송 (예제)"""
print(f"🔔 알림: {product_name} 재고 확보!")
# 실제로는 이메일, SMS, 메신저 등으로 알림

def monitor_stock(url, product_name, interval=60):
"""주기적으로 재고 확인"""

print(f"재고 모니터링 시작: {product_name}")
print(f"확인 주기: {interval}초\n")

while True:
in_stock = check_stock_availability(url, product_name)

if in_stock:
print("\n재고 확인 완료 - 모니터링 종료")
break

# 다음 확인까지 대기
time.sleep(interval)

# 사용 예제
monitor_stock(
url="https://example.com",
product_name="인기 상품",
interval=60 # 60초마다 확인
)

Headless 모드

브라우저 창을 띄우지 않고 백그라운드에서 실행합니다.

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

# Chrome Headless 모드
options = webdriver.ChromeOptions()
options.add_argument('--headless')
options.add_argument('--disable-gpu')
options.add_argument('--no-sandbox')
options.add_argument('--window-size=1920,1080')

driver = webdriver.Chrome(
service=Service(ChromeDriverManager().install()),
options=options
)

# 사용
driver.get("https://www.example.com")
print(driver.title)
driver.quit()

User Agent 변경

봇 탐지를 피하기 위해 User Agent를 변경할 수 있습니다.

options = webdriver.ChromeOptions()

# User Agent 설정
user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
options.add_argument(f'user-agent={user_agent}')

# 자동화 탐지 방지
options.add_experimental_option("excludeSwitches", ["enable-automation"])
options.add_experimental_option('useAutomationExtension', False)

driver = webdriver.Chrome(
service=Service(ChromeDriverManager().install()),
options=options
)

# WebDriver 속성 숨기기
driver.execute_script("Object.defineProperty(navigator, 'webdriver', {get: () => undefined})")

자주 묻는 질문

Q1. Selenium이 느린데 더 빠르게 할 수 있나요?

A: Headless 모드를 사용하고, 이미지 로딩을 비활성화하세요.

options = webdriver.ChromeOptions()
options.add_argument('--headless')

# 이미지 로딩 비활성화
prefs = {'profile.default_content_setting_values': {'images': 2}}
options.add_experimental_option('prefs', prefs)

driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=options)

Q2. 요소를 찾을 수 없다는 오류가 자주 나요.

A: 명시적 대기를 사용하고, 올바른 선택자를 사용하세요.

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

# 대기 시간 증가
wait = WebDriverWait(driver, 20)

# 요소가 클릭 가능할 때까지 대기
element = wait.until(EC.element_to_be_clickable((By.ID, "button")))
element.click()

Q3. Selenium vs BeautifulSoup 어떤 것을 사용해야 하나요?

A: 정적 페이지는 BeautifulSoup, 동적 페이지는 Selenium을 사용하세요.

# 정적 페이지 (빠름)
import requests
from bs4 import BeautifulSoup

response = requests.get(url)
soup = BeautifulSoup(response.text, 'html.parser')

# 동적 페이지 (JavaScript 실행 필요)
from selenium import webdriver
driver = webdriver.Chrome()
driver.get(url)

Q4. 웹사이트가 봇을 차단하는 것 같아요.

A: 다음 방법들을 시도해보세요:

options = webdriver.ChromeOptions()

# User Agent 변경
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)

driver = webdriver.Chrome(options=options)

# WebDriver 속성 숨기기
driver.execute_cdp_cmd('Page.addScriptToEvaluateOnNewDocument', {
'source': '''
Object.defineProperty(navigator, 'webdriver', {
get: () => undefined
})
'''
})

# 요청 사이에 랜덤 대기
import random
time.sleep(random.uniform(1, 3))

Q5. 여러 브라우저를 동시에 실행할 수 있나요?

A: 네, 멀티스레딩이나 멀티프로세싱을 사용하세요.

from concurrent.futures import ThreadPoolExecutor
from selenium import webdriver

def scrape_page(url):
driver = webdriver.Chrome()
driver.get(url)
# 작업 수행
result = driver.title
driver.quit()
return result

# 동시에 5개의 브라우저 실행
urls = ['url1', 'url2', 'url3', 'url4', 'url5']

with ThreadPoolExecutor(max_workers=5) as executor:
results = executor.map(scrape_page, urls)

for result in results:
print(result)

다음 단계

웹 자동화를 배웠다면, 다음 주제로 넘어가세요:

  • 작업 스케줄링: 정기적인 웹 크롤링 자동화하기
  • API 활용: requests로 효율적인 데이터 수집하기
  • 데이터 저장: 수집한 데이터를 데이터베이스에 저장하기

웹 자동화는 데이터 수집, 테스트, 반복 작업 자동화에 매우 유용한 기술입니다. 실제 프로젝트에 적용해보세요!