Playwright: 웹 스크래핑 및 테스트
테스트 및 스크래핑을 위한 브라우저 자동화 숙련하기
Playwright은 웹 크롤링 및 종단간 테스트(end-to-end testing)를 혁신적으로 변화시키는 강력하고 현대적인 브라우저 자동화 프레임워크입니다.
마이크로소프트에서 개발한 이 프레임워크는 Chromium, Firefox, WebKit 브라우저를 사용하여 이전에 없었던 신뢰성과 속도로 자동화를 제공합니다.

Playwright란 무엇인가요?
Playwright는 개발자가 신뢰성 있는 종단간 테스트를 작성하고 복잡한 웹 크롤링 솔루션을 구축할 수 있도록 해주는 오픈소스 브라우저 자동화 프레임워크입니다. 전통적인 자동화 도구와 달리 Playwright는 동적 콘텐츠, 단일 페이지 애플리케이션(SPAs), 복잡한 자바스크립트 프레임워크를 처리하기 위해 근본적으로 설계되었습니다.
이 프레임워크는 이전 자동화 도구가 직면했던 핵심 문제인 불안정성(flake)을 해결합니다. Playwright는 자동으로 요소가 실행 가능한 상태가 되기 전에 자동으로 대기하는 메커니즘을 도입하여 테스트에 사용되는 임의의 타임아웃과 sleep 문을 제거하고 테스트의 신뢰성을 극적으로 향상시킵니다.
주요 기능
크로스 브라우저 지원: Playwright는 주요 브라우저 엔진인 Chromium(Chrome 및 Edge 포함), Firefox, WebKit(Safari)을 지원합니다. 이는 자동화 스크립트를 한 번 작성하여 다른 브라우저에서 수정 없이 실행할 수 있음을 의미하며, 웹 애플리케이션이 모든 환경에서 일관되게 작동하도록 보장합니다.
자동 대기: Playwright의 가장 강력한 기능 중 하나는 내장된 자동 대기 메커니즘입니다. 어떤 작업을 수행하기 전에 Playwright는 요소가 보이기, 활성화, 안정성, 가려지지 않은 상태가 되기를 자동으로 기다립니다. 이는 Selenium과 같은 도구에서 명시적 대기가 자주 필요한 것에 비해, 경쟁 조건을 제거하고 테스트의 신뢰성을 극적으로 높입니다.
네트워크 중개: Playwright는 네트워크 요청 및 응답을 중개, 수정, 모킹할 수 있습니다. 이는 테스트의 경계 사례를 확인하거나 느린 네트워크를 시뮬레이션하거나 크롤링 중 불필요한 자원을 차단하거나 백엔드가 필요 없는 API 응답을 모킹하는 데 매우 유용합니다.
모바일 에뮬레이션: 특정 뷰포트 크기, 사용자 에이전트 및 터치 이벤트를 사용하여 다양한 모바일 기기를 에뮬레이션하여 모바일 웹 애플리케이션을 테스트할 수 있습니다. Playwright는 인기 있는 스마트폰 및 태블릿을 위한 기기 설명자(device descriptors)를 포함합니다.
강력한 선택자: CSS 및 XPath 선택자 외에도 Playwright는 텍스트 선택자, 접근성 테스트를 위한 역할 기반 선택자, 그리고 컴포넌트 기반 프레임워크를 위한 실험적인 React 및 Vue 선택자를 지원합니다.
설치 및 설정
Playwright는 다양한 프로그래밍 언어에서 간단하게 설정할 수 있습니다.
Python 설치
Python 프로젝트에서 Playwright는 pip를 통해 설치할 수 있으며 동기식 및 비동기식 API 모두를 제공합니다. 더 빠르고 현대적인 Python 패키지 관리자를 찾고 있다면 uv - Python 패키지, 프로젝트 및 환경 관리자에 있는 가이드를 확인해 보세요:
# Playwright 패키지 설치
pip install playwright
# 브라우저 설치 (Chromium, Firefox, WebKit)
playwright install
# 특정 브라우저만 설치
playwright install chromium
Playwright와 함께 작업할 때 Python 문법과 일반적으로 사용되는 명령에 대한 포괄적인 참조는 Python Cheatsheet를 참조하세요.
JavaScript/TypeScript 설치
Node.js 프로젝트에서는 npm 또는 yarn을 통해 Playwright를 설치할 수 있습니다:
# npm 사용
npm init playwright@latest
# yarn 사용
yarn create playwright
# 수동 설치
npm install -D @playwright/test
npx playwright install
npm init playwright 명령어는 예제 테스트, 구성 파일, GitHub Actions 워크플로우를 포함하여 프로젝트를 구성하는 대화형 설정을 제공합니다.
기본 구성
playwright.config.ts (TypeScript) 또는 playwright.config.js (JavaScript) 파일을 생성하세요:
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests',
timeout: 30000,
retries: 2,
workers: 4,
use: {
headless: true,
viewport: { width: 1280, height: 720 },
screenshot: 'only-on-failure',
video: 'retain-on-failure',
},
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
{
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
},
{
name: 'webkit',
use: { ...devices['Desktop Safari'] },
},
],
});
Playwright를 이용한 웹 크롤링
Playwright는 전통적인 크롤링 라이브러리가 어려워하는 동적 콘텐츠를 포함한 현대 웹사이트에 특히 탁월합니다.
기본 크롤링 예시
다음은 핵심 크롤링 개념을 보여주는 포괄적인 Python 예시입니다:
from playwright.sync_api import sync_playwright
import json
def scrape_website():
with sync_playwright() as p:
# 브라우저 실행
browser = p.chromium.launch(headless=True)
# 고립을 위한 컨텍스트 생성
context = browser.new_context(
viewport={'width': 1920, 'height': 1080},
user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64)'
)
# 새 페이지 열기
page = context.new_page()
# URL 이동
page.goto('https://example.com/products')
# 콘텐츠 로드 대기
page.wait_for_selector('.product-item')
# 데이터 추출
products = page.query_selector_all('.product-item')
data = []
for product in products:
title = product.query_selector('h2').inner_text()
price = product.query_selector('.price').inner_text()
url = product.query_selector('a').get_attribute('href')
data.append({
'title': title,
'price': price,
'url': url
})
# 정리
browser.close()
return data
# 크롤러 실행
results = scrape_website()
print(json.dumps(results, indent=2))
동적 콘텐츠 처리
현대 웹사이트는 자주 JavaScript를 통해 콘텐츠를 동적으로 로드합니다. Playwright는 이를 부드럽게 처리합니다:
async def scrape_dynamic_content():
async with async_playwright() as p:
browser = await p.chromium.launch()
page = await browser.new_page()
await page.goto('https://example.com/infinite-scroll')
# 더 많은 콘텐츠를 로드하기 위해 스크롤
for _ in range(5):
await page.evaluate('window.scrollTo(0, document.body.scrollHeight)')
await page.wait_for_timeout(2000)
# 네트워크가 비활성 상태가 될 때까지 대기
await page.wait_for_load_state('networkidle')
# 로드된 모든 항목 추출
items = await page.query_selector_all('.item')
await browser.close()
크롤링된 콘텐츠를 Markdown으로 변환
Playwright로 HTML 콘텐츠를 추출한 후에는 일반적으로 더 사용 가능한 형식으로 변환해야 합니다. HTML을 Markdown으로 변환하는 방법에 대한 포괄적인 가이드는 다음 기사들을 참조하세요: Python을 이용한 HTML to Markdown 변환: 포괄적인 가이드는 6가지 다른 Python 라이브러리를 비교하고, LLM과 Ollama를 이용한 HTML 콘텐츠의 Markdown 변환은 AI 기반 변환을 위한 내용이며, Word 문서를 사용하는 경우 Word 문서를 Markdown으로 변환하는 방법을 확인해 보세요.
인증 및 세션 관리
인증이 필요한 크롤링 작업은 Playwright가 브라우저 상태를 쉽게 저장하고 재사용할 수 있도록 합니다:
def login_and_save_session():
with sync_playwright() as p:
browser = p.chromium.launch(headless=False)
context = browser.new_context()
page = context.new_page()
# 로그인
page.goto('https://example.com/login')
page.fill('input[name="username"]', 'your_username')
page.fill('input[name="password"]', 'your_password')
page.click('button[type="submit"]')
# 로그인 후 네비게이션 대기
page.wait_for_url('**/dashboard')
# 인증된 상태 저장
context.storage_state(path='auth_state.json')
browser.close()
def scrape_with_saved_session():
with sync_playwright() as p:
browser = p.chromium.launch()
# 저장된 인증 상태 재사용
context = browser.new_context(storage_state='auth_state.json')
page = context.new_page()
# 이미 인증됨!
page.goto('https://example.com/protected-data')
# ... 보호된 콘텐츠 크롤링
browser.close()
이 접근법은 API 작업이나 AI 통합을 위한 MCP 서버 구축 시 특히 유용합니다. 웹 크롤링을 AI 도구 통합에 구현하는 방법에 대한 완전한 가이드는 Python에서 MCP 서버 구축: 웹검색 및 크롤링을 참조하세요.
종단간 테스트
Playwright의 주요 사용 사례는 웹 애플리케이션에 대한 견고한 종단간 테스트를 작성하는 것입니다.
첫 번째 테스트 작성
다음은 TypeScript에서의 완전한 테스트 예시입니다:
import { test, expect } from '@playwright/test';
test('사용자가 항목을 장바구니에 추가할 수 있다', async ({ page }) => {
// 홈페이지 이동
await page.goto('https://example-shop.com');
// 제품 검색
await page.fill('[data-testid="search-input"]', 'laptop');
await page.press('[data-testid="search-input"]', 'Enter');
// 검색 결과 대기
await expect(page.locator('.product-card')).toBeVisible();
// 첫 번째 제품 클릭
await page.locator('.product-card').first().click();
// 제품 페이지 로드 확인
await expect(page).toHaveURL(/\/product\/.+/);
// 장바구니에 추가
await page.click('[data-testid="add-to-cart"]');
// 장바구니 업데이트 확인
const cartCount = page.locator('[data-testid="cart-count"]');
await expect(cartCount).toHaveText('1');
});
페이지 객체 모델
더 큰 테스트 세트에서 사용하면 유지보수가 편리해지는 페이지 객체 모델 패턴을 사용하세요:
// pages/LoginPage.ts
export class LoginPage {
constructor(private page: Page) {}
async navigate() {
await this.page.goto('/login');
}
async login(username: string, password: string) {
await this.page.fill('[name="username"]', username);
await this.page.fill('[name="password"]', password);
await this.page.click('button[type="submit"]');
}
async getErrorMessage() {
return await this.page.locator('.error-message').textContent();
}
}
// tests/login.spec.ts
import { test, expect } from '@playwright/test';
import { LoginPage } from '../pages/LoginPage';
test('잘못된 자격 증명으로 로그인하면 오류가 표시된다', async ({ page }) => {
const loginPage = new LoginPage(page);
await loginPage.navigate();
await loginPage.login('invalid@email.com', 'wrongpass');
const error = await loginPage.getErrorMessage();
expect(error).toContain('잘못된 자격 증명');
});
고급 기능
Codegen - 자동 테스트 생성
Playwright의 Codegen 도구는 웹 페이지와의 상호작용을 기록하여 테스트를 생성합니다:
# Codegen 실행
playwright codegen example.com
# 특정 브라우저 사용
playwright codegen --browser firefox example.com
# 저장된 인증 상태 사용
playwright codegen --load-storage=auth.json example.com
페이지와 상호작용하면서 Codegen은 실시간으로 코드를 생성합니다. 이는 테스트의 빠른 프로토타이핑이나 Playwright의 선택자 구문 학습에 매우 유용합니다.
디버깅을 위한 Trace Viewer
테스트가 실패하면 왜 실패했는지 이해하는 것이 어렵습니다. Playwright의 Trace Viewer는 테스트 실행의 시간선을 보여줍니다:
// 구성에서 추적 활성화
use: {
trace: 'on-first-retry',
}
테스트가 실패하고 재시도한 후 추적을 보세요:
playwright show-trace trace.zip
Trace Viewer는 각 작업에 대한 스크린샷, 네트워크 활동, 콘솔 로그 및 DOM 스냅샷을 보여줘서 디버깅을 간단하게 만듭니다.
네트워크 중개 및 모킹
테스트의 경계 사례를 위해 네트워크 트래픽을 중개하고 수정하세요:
def test_with_mocked_api():
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
# API 응답 모킹
def handle_route(route):
if 'api/products' in route.request.url:
route.fulfill(
status=200,
body=json.dumps({
'products': [
{'id': 1, 'name': '테스트 제품', 'price': 99.99}
]
})
)
else:
route.continue_()
page.route('**/*', handle_route)
page.goto('https://example.com')
# 페이지는 모킹된 데이터를 사용합니다
browser.close()
모바일 테스트
다양한 기기에서 반응형 디자인을 테스트하세요:
from playwright.sync_api import sync_playwright
def test_mobile():
with sync_playwright() as p:
# 기기 설명자 사용
iphone_13 = p.devices['iPhone 13']
browser = p.webkit.launch()
context = browser.new_context(**iphone_13)
page = context.new_page()
page.goto('https://example.com')
# 모바일 사용자로 상호작용
page.locator('#mobile-menu-button').click()
browser.close()
최고의 실천 방법
웹 크롤링을 위한 최고의 실천 방법
- 프로덕션에서 헤드리스 모드 사용: 헤드리스 브라우징은 더 빠르고 자원 사용이 적습니다.
- 레이트 제한 구현: 요청 사이에 지연을 두어 대상 웹사이트를 존중하세요.
- 오류를 유연하게 처리: 네트워크 문제, 타임아웃, 선택자 변경이 발생할 수 있습니다.
- 사용자 에이전트 회전: 브라우저 지문을 변경하여 감지를 피하세요.
- robots.txt 존중: 웹사이트 크롤링 정책을 확인하고 따르세요.
- 컨텍스트 고립 사용: 병렬 크롤링을 위해 별도의 브라우저 컨텍스트를 생성하세요.
크롤링된 콘텐츠를 마크다운 형식으로 변환할 때는 LLM 기반 변환 도구나 HTML-to-Markdown 변환에 특화된 Python 라이브러리를 활용하여 더 깨끗한 출력을 얻을 수 있습니다.
테스트를 위한 최고의 실천 방법
- data-testid 속성 사용: 자주 변경되는 CSS 클래스보다 더 안정적입니다.
- 경고 대기 사용 회피: Playwright의 내장 대기 메커니즘을 사용하고
sleep()을 피하세요. - 테스트 독립성 유지: 각 테스트는 독립적으로 실행될 수 있어야 합니다.
- Fixture 사용: 테스트 간에 설정 코드를 효율적으로 공유하세요.
- 병렬 테스트 실행: Playwright의 워커 스레드를 활용하여 속도를 높이세요.
- 실패 시 추적 기록: 디버깅을 위해 추적 기록을 활성화하세요.
성능 최적화
# 불필요한 자원 비활성화
def fast_scraping():
with sync_playwright() as p:
browser = p.chromium.launch()
context = browser.new_context()
page = context.new_page()
# 이미지 및 스타일시트를 비활성화하여 크롤링 속도를 높이세요
async def block_resources(route):
if route.request.resource_type in ['image', 'stylesheet', 'font']:
await route.abort()
else:
await route.continue_()
page.route('**/*', block_resources)
page.goto('https://example.com')
browser.close()
Playwright와 대안 비교
Playwright vs Selenium
Playwright의 장점:
- 내장 자동 대기로 불안정한 테스트를 제거합니다.
- 현대적인 아키텍처 덕분에 더 빠른 실행 속도를 제공합니다.
- 더 강력한 네트워크 중개 및 모킹 기능을 제공합니다.
- 더 우수한 디버깅 도구(Trace Viewer)를 제공합니다.
- 더 간단한 API와 더 적은 보일러플레이트를 제공합니다.
- 단일 설치로 여러 브라우저 지원
Selenium의 장점:
- 더 성숙한 생태계와 광범위한 커뮤니티 지원
- 더 많은 프로그래밍 언어 지원
- 더 오래된 버전 포함의 더 넓은 브라우저 호환성
Playwright vs Puppeteer
Playwright의 장점:
- 진정한 크로스 브라우저 지원 (Firefox, WebKit, Chromium)
- Puppeteer의 교훈을 바탕으로 더 우수한 API 설계
- 더 강력한 디버깅 도구
- 마이크로소프트의 후원 및 활발한 개발
Puppeteer의 장점:
- 약간 더 작은 크기
- Chrome DevTools Protocol 전문 지식
대부분의 새로운 프로젝트에서 Playwright는 현대적인 아키텍처와 포괄적인 기능 세트 덕분에 추천되는 선택입니다. Go 대신 Python 또는 JavaScript를 사용하고 웹 크롤링 기능이 필요한 경우, Go 생태계에서의 BeautifulSoup 대안을 확인해 보세요.
일반적인 사용 사례
AI/LLM 애플리케이션을 위한 데이터 추출
Playwright는 AI 모델의 훈련 데이터 수집이나 웹 검색 기능 구현에 탁월합니다. MCP (Model Context Protocol) 서버를 구축할 때 Playwright는 웹 크롤링 컴포넌트를 처리하고 LLM이 추출된 콘텐츠를 처리할 수 있습니다.
CI/CD에서의 자동화 테스트
Playwright 테스트를 CI/CD 파이프라인에 통합하세요:
# .github/workflows/playwright.yml
name: Playwright Tests
on:
push:
branches: [ main, master ]
pull_request:
branches: [ main, master ]
jobs:
test:
timeout-minutes: 60
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18
- name: 의존성 설치
run: npm ci
- name: Playwright 브라우저 설치
run: npx playwright install --with-deps
- name: Playwright 테스트 실행
run: npx playwright test
- uses: actions/upload-artifact@v3
if: always()
with:
name: playwright-report
path: playwright-report/
retention-days: 30
웹사이트 모니터링
프로덕션 웹사이트의 가동 상태와 기능성을 모니터링하세요:
import schedule
import time
def monitor_website():
with sync_playwright() as p:
try:
browser = p.chromium.launch()
page = browser.new_page()
page.goto('https://your-site.com', timeout=30000)
# 주요 요소 확인
assert page.is_visible('.header')
assert page.is_visible('#main-content')
print("✓ 웹사이트가 건강합니다")
except Exception as e:
print(f"✗ 웹사이트 문제 감지됨: {e}")
# 경고 전송
finally:
browser.close()
# 5분마다 실행
schedule.every(5).minutes.do(monitor_website)
while True:
schedule.run_pending()
time.sleep(1)
일반적인 문제 해결
브라우저 설치 문제
브라우저가 다운로드 실패하는 경우:
# 사용자 지정 다운로드 위치 설정
PLAYWRIGHT_BROWSERS_PATH=/custom/path playwright install
# 캐시 지우고 다시 설치
playwright uninstall
playwright install
타임아웃 오류
느린 네트워크 또는 복잡한 페이지에 대해 타임아웃을 늘리세요:
page.goto('https://slow-site.com', timeout=60000) # 60초
page.wait_for_selector('.element', timeout=30000) # 30초
선택자 찾기 실패
Playwright Inspector를 사용하여 올바른 선택자를 식별하세요:
PWDEBUG=1 pytest test_file.py
이 명령어는 Inspector를 열어 요소를 호버하여 선택자를 볼 수 있도록 합니다.
결론
Playwright는 브라우저 자동화 기술의 최첨단을 대표하며, 강력한 기능과 탁월한 개발자 경험을 결합하고 있습니다. 웹 크롤링 파이프라인을 구축하거나 포괄적인 테스트 커버리지를 구현하거나 자동화 워크플로우를 생성하려는 경우, Playwright는 필요한 도구와 신뢰성을 제공합니다.
자동 대기 메커니즘은 불안정한 테스트를 제거하고, 크로스 브라우저 지원은 애플리케이션이 모든 곳에서 작동하도록 보장하며, 강력한 디버깅 도구는 문제 해결을 간단하게 만듭니다. 웹 애플리케이션이 복잡성이 증가하면서 Playwright의 현대적인 아키텍처와 활발한 개발은 브라우저 자동화의 모든 요구사항에 적합한 선택입니다.
Python 개발자가 데이터 파이프라인 또는 웹 크롤링 프로젝트를 작업 중이라면, Playwright는 현대적인 패키지 관리자와 함께 시원하게 통합되고, pandas, requests, 다른 데이터 과학 도구와 함께 탁월하게 작동합니다. 복잡한 현대 웹사이트에서 구조화된 데이터를 추출할 수 있는 능력은 AI 애플리케이션, 연구 프로젝트, 비즈니스 인텔리전스에 매우 유용합니다. HTML-to-Markdown 변환 도구와 적절한 콘텐츠 처리와 결합하면 Playwright는 대규모로 웹 데이터를 추출, 변환, 활용하는 완전한 솔루션으로 작동합니다.
유용한 링크
- Python Cheatsheet
- uv - Python 패키지, 프로젝트 및 환경 관리자
- LLM과 Ollama를 사용하여 HTML 콘텐츠를 Markdown으로 변환
- Word 문서를 Markdown으로 변환: 완전한 가이드
- Python을 사용한 HTML에서 Markdown으로의 변환: 포괄적인 가이드
- Python에서 MCP 서버 구축: 웹검색 및 크롤링
- Go에서 BeautifulSoup 대안