웹 자동화
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로 효율적인 데이터 수집하기
- 데이터 저장: 수집한 데이터를 데이터베이스에 저장하기
웹 자동화는 데이터 수집, 테스트, 반복 작업 자동화에 매우 유용한 기술입니다. 실제 프로젝트에 적용해보세요!