Playwright 101
작성일:
Playwright 가이드: 웹 자동화
1. Playwright란?
Playwright는 웹 브라우저 자동화를 위한 도구로, Selenium의 대안으로 설계되었습니다. 주요 용도는 다음과 같습니다:
- 웹 애플리케이션 테스트 자동화
- 웹 스크래핑 및 데이터 추출
- 반복적인 웹 작업 자동화
- 크로스 브라우저 테스팅
주요 기능
- 브라우저 제어: Chromium, Firefox, Webkit(Safari 엔진) 지원
- DOM 조작:
querySelector
와locator
로 요소 검색 및 조작 - 폼 자동화: 입력 및 제출 자동화
- 스크린샷 캡처: 페이지 또는 요소 캡처
- 헤드리스 모드: GUI 없는 실행
Playwright는 Selenium보다 최신 기술을 활용하며, 자동 대기와 간결한 API를 제공합니다.
Selenium과의 비교
기능 | Selenium | Playwright |
---|---|---|
브라우저 지원 | 별도 드라이버 필요 | 드라이버 내장 |
DOM 탐색 | find_element 등 복잡 |
querySelector 와 locator 로 간결 |
동적 요소 처리 | WebDriverWait 로 명시적 대기 |
기본적으로 자동 대기 지원 |
설치 | WebDriver 관리 필요 | playwright install 로 통합 |
속도 | 일부 작업에서 느릴 수 있음 | 최적화로 더 빠름 |
Playwright는 Selenium보다 설정이 간단하고, 동적 웹 페이지 처리에서 우수합니다.
2. 지원하는 브라우저
- Chromium (Chrome과 유사)
- Firefox
- Webkit (Safari와 유사)
별도 드라이버 설치 없이 Playwright 자체에서 관리합니다.
3. 기본적인 웹 페이지 호출 방법 (Chromium 사용)
설치 명령어:
$ pip install playwright
$ playwright install
동기 방식 예시:
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.goto('https://www.example.com')
browser.close()
비동기 방식:
async_playwright
와await
사용 가능 (후술).
4. DOM을 찾기 위한 querySelector
및 locator
사용법
querySelector
CSS 선택자로 단일 요소를 찾습니다:
element = page.query_selector('#search-input')
querySelectorAll
여러 요소를 리스트로 반환:
elements = page.query_selector_all('.example-class')
for el in elements:
print(el.text_content())
locator
Playwright 고유의 기능으로, 동적 요소를 안정적으로 추적:
locator = page.locator('#search-input')
print(locator.text_content())
선택 방법
방법 | 설명 |
---|---|
page.query_selector('#id') |
ID로 요소 찾기 |
page.query_selector('.class') |
클래스명으로 요소 찾기 |
page.query_selector('tag') |
태그명으로 요소 찾기 |
page.locator('text=Link') |
텍스트로 요소 찾기 |
예시 (동기)
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.goto('https://www.example.com')
# querySelector
element = page.query_selector('#example-id')
print(f"ID: {element.text_content()}")
# locator
locator = page.locator('div > p')
print(f"Locator: {locator.text_content()}")
browser.close()
동적 도메인 추가 (href
예시)
href
속성 값을 가져올 때, 상대 경로(/path
)라면 현재 페이지 도메인을 동적으로 추가:
link = page.query_selector('a')
href = link.get_attribute('href')
base_url = page.url # 현재 페이지 URL
from urllib.parse import urljoin
full_url = urljoin(base_url, href) # 도메인 추가
print(f"Full URL: {full_url}")
5. 주요 DOM 조작 기능
click()
button = page.query_selector('#submit-button')
button.click()
fill()
input_field = page.query_selector('[name="username"]')
input_field.fill('myusername')
text_content()
paragraph = page.query_selector('.content')
print(paragraph.text_content())
get_attribute()
link = page.query_selector('a')
href = link.get_attribute('href')
6. 동적 웹 페이지 처리: wait_for_selector
동기 방식으로 요소가 나타날 때까지 대기:
node = page.wait_for_selector('a.ClassName', timeout=10000) # 10초 대기
주요 메서드
메서드 | 설명 |
---|---|
wait_for_selector |
요소가 나타날 때까지 대기 |
wait_for_load_state |
페이지 로드 완료 대기 |
예시
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.goto('https://example.com')
button = page.wait_for_selector('#submit-button', timeout=10000)
button.click()
browser.close()
7. Headless 모드 및 기타 옵션
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
page = browser.new_page()
page.goto('https://www.example.com')
browser.close()
옵션
viewport={'width': 1200, 'height': 600}
args=['--user-agent=Mozilla/5.0 ...']
8. 비동기 방식 (aio
) 사용 시 이슈 및 해결 방안
Playwright는 async_playwright
로 비동기 실행을 지원합니다.
비동기 예시
from playwright.async_api import async_playwright
import asyncio
async def main():
async with async_playwright() as p:
browser = await p.chromium.launch()
page = await browser.new_page()
await page.goto('https://www.example.com')
await browser.close()
asyncio.run(main())
이슈 및 해결 방안
- 이슈: 비동기 코드의 복잡성
- 동기 방식보다 코드가 복잡해지고,
await
누락 시 오류 발생. - 해결: 간단한 작업은 동기 방식(
sync_playwright
) 사용 권장.
- 동기 방식보다 코드가 복잡해지고,
- 이슈: 타임아웃 문제
- 비동기 작업 중 네트워크 지연으로 요소가 늦게 로드될 수 있음.
- 해결:
await page.wait_for_selector('#id', timeout=10000)
로 명시적 대기 추가.
- 이슈: 이벤트 루프 충돌
- 기존 asyncio 환경과 충돌 가능.
- 해결:
asyncio.run()
대신 기존 루프에 통합하거나,nest_asyncio
사용:import nest_asyncio nest_asyncio.apply()
- 이슈: 리소스 관리
- 비동기 컨텍스트가 제대로 닫히지 않으면 메모리 누수 발생.
- 해결:
async with
문으로 자동 리소스 정리 보장.
9. connect_over_cdp
를 사용한 Chromium 연결
Playwright는 connect_over_cdp
를 통해 기존에 실행 중인 Chromium 인스턴스에 연결할 수 있습니다. 이는 Chrome DevTools Protocol(CDP)을 활용하며, 브라우저를 직접 제어하거나 디버깅할 때 유용합니다.
사용 방법 (동기 방식)
먼저, Chromium을 CDP 지원 모드로 실행합니다:
google-chrome --remote-debugging-port=9222
Playwright에서 연결:
with sync_playwright() as p:
browser = p.chromium.launch(headless=False, devtools=True)
page = browser.new_page()
page.goto('https://example.com')
browser.close()
Playwright는 Selenium보다 간결하고 강력한 기능을 제공하며, 동기와 비동기 방식을 모두 지원합니다. 동적 도메인 추가와 같은 실용적 기능도 쉽게 구현 가능합니다.
댓글남기기