파이썬 Playwright 브라우저 자동화: 동적 크롤링과 스크린샷
파이썬 Playwright 설치부터 동적 요소 대기, 클릭·스크롤 자동화, 텍스트 추출과 전체 페이지·특정 요소 스크린샷 저장 방법까지 설명합니다.

자바스크립트 실행 뒤에야 내용이 나타나는 동적 웹페이지는 requests만으로 원하는 데이터를 얻기 어려울 수 있습니다. 이때 파이썬 Playwright로 실제 브라우저를 실행해 페이지 이동, 요소 대기, 클릭, 텍스트 추출과 스크린샷 저장을 자동화하면 됩니다. 핵심은 임의의 sleep()을 남발하지 않고 Locator와 Playwright의 자동 대기 기능을 활용하는 것입니다.
Playwright Python 공식 설치 안내 확인하기
핵심 요약
pip install playwright후playwright install로 브라우저 바이너리를 설치합니다.- 동적 요소는 Locator로 찾고, 표시·클릭 가능 상태를 자동으로 기다리게 합니다.
- 전체 페이지는
full_page=True, 특정 요소는 Locator의screenshot()으로 저장합니다. - 브라우저와 Context는 반드시 닫고, 타임아웃·재시도·실패 화면을 기록합니다.
- robots.txt, 이용약관, 접근 빈도, 개인정보와 저작권을 확인한 범위에서만 수집합니다.
Playwright가 필요한 웹페이지
Playwright는 Chromium, Firefox, WebKit 계열 렌더링 엔진을 제어할 수 있는 브라우저 자동화 도구입니다. 동기식과 비동기식 Python API를 모두 제공하며, 화면이 보이지 않는 headless 모드와 실제 브라우저 창을 띄우는 headed 모드를 지원합니다.
| 페이지 유형 | 우선 도구 | 판단 기준 |
|---|---|---|
| 서버가 완성한 정적 HTML | requests + BeautifulSoup | 응답 HTML에 필요한 데이터가 있음 |
| 자바스크립트 렌더링 | Playwright | 브라우저 실행 뒤 데이터가 나타남 |
| 클릭·스크롤 후 추가 로딩 | Playwright | 사용자 동작이 있어야 다음 내용 표시 |
| 공개 JSON API 제공 | requests | 브라우저 없이 구조화 데이터 획득 가능 |
브라우저 자동화는 메모리와 CPU 사용량이 크므로 모든 페이지에 적용할 필요는 없습니다. 먼저 개발자 도구의 네트워크 탭에서 공개 API가 있는지 확인하고, 정적 HTML로 충분하면 더 단순한 방식이 유리합니다.
설치와 첫 브라우저 실행
라이브러리 설치와 브라우저 바이너리 설치는 별도 단계입니다. 가상환경을 활성화한 뒤 다음 명령을 실행합니다.
pip install playwright
playwright install
특정 브라우저만 필요하면 설치 범위를 줄일 수 있습니다. 배포 서버나 컨테이너에서는 운영체제 의존 패키지가 추가로 필요할 수 있으므로 공식 설치 문서의 현재 지원 환경을 확인해야 합니다.
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
page = browser.new_page()
page.goto(
"https://example.com",
wait_until="domcontentloaded",
timeout=30_000,
)
print(page.title())
browser.close()
page는 브라우저 탭 하나에 해당합니다. BrowserContext는 쿠키와 저장소가 분리된 독립 세션이므로 계정이나 작업 단위로 환경을 격리할 때 유용합니다. 사용이 끝난 브라우저는 반드시 닫아야 백그라운드 프로세스가 남지 않습니다.
동적 요소를 기다리고 데이터 추출하기
동적 페이지에서 가장 흔한 실패 원인은 요소가 나타나기 전에 읽으려는 것입니다. 고정된 시간 동안 무조건 쉬는 방식은 네트워크가 빠르면 시간을 낭비하고 느리면 여전히 실패합니다. Playwright Locator는 클릭이나 텍스트 읽기 같은 작업 전에 대상 상태를 확인하고 필요한 조건을 자동으로 기다립니다.
page.goto("https://example.com/products")
cards = page.locator("article.product-card")
cards.first.wait_for(state="visible")
results = []
for index in range(cards.count()):
card = cards.nth(index)
results.append({
"name": card.locator(".name").inner_text().strip(),
"price": card.locator(".price").inner_text().strip(),
})
선택자는 화면 구조가 조금 바뀌어도 유지될 수 있도록 작성해야 합니다. 테스트 목적이라면 역할과 접근 가능한 이름을 이용한 get_by_role(), 폼 라벨을 이용한 get_by_label() 같은 사용자 관점 Locator를 우선할 수 있습니다. 데이터 수집에서는 안정적인 속성이나 사이트가 제공한 식별자를 검토하되, 자동 생성 클래스에 과도하게 의존하지 않는 편이 좋습니다.
렌더링된 HTML을 별도로 파싱하는 방법
브라우저에서 상호작용과 렌더링을 끝낸 뒤 page.content()로 현재 HTML을 받아 BeautifulSoup에 넘길 수도 있습니다. 브라우저 제어와 데이터 정제 책임을 분리할 수 있어, 복잡한 반복 파싱에는 편리합니다.
from bs4 import BeautifulSoup
html = page.content()
soup = BeautifulSoup(html, "html.parser")
titles = [
node.get_text(" ", strip=True)
for node in soup.select("article .title")
]
클릭·스크롤·페이지 이동 자동화
더 보기 버튼이나 다음 페이지가 있는 경우 버튼이 보이는지 확인한 뒤 클릭하고, 새 항목이 실제로 추가됐는지 기다려야 합니다. 클릭 직후 고정 시간만 기다리면 중복 수집이나 누락이 생길 수 있습니다.
items = page.locator(".result-item")
while True:
more = page.get_by_role("button", name="더 보기")
if more.count() == 0 or not more.is_visible():
break
before = items.count()
more.click()
page.wait_for_function(
"(n) => document.querySelectorAll('.result-item').length > n",
before,
)
무한 스크롤에서는 페이지 하단으로 이동한 뒤 항목 수가 늘어나는지를 확인하고, 연속으로 변화가 없으면 종료하는 기준을 둡니다. 최대 반복 횟수와 최대 수집 건수도 함께 설정해야 사이트 오류로 반복문이 끝나지 않는 문제를 막을 수 있습니다.
로그인, CAPTCHA, 접근 차단을 우회하는 자동화는 서비스 약관이나 보안 정책을 위반할 수 있습니다. 접근 권한이 있는 업무 시스템이라도 공식 API나 제공된 자동화 인터페이스가 있는지 먼저 확인하십시오.
페이지와 특정 요소 스크린샷 저장
현재 보이는 화면은 page.screenshot(), 문서 전체 길이는 full_page=True로 저장합니다. 이미지 형식은 파일 확장자 또는 옵션으로 결정할 수 있습니다.
page.screenshot(path="viewport.png")
page.screenshot(path="full-page.png", full_page=True)
chart = page.locator("#sales-chart")
chart.wait_for(state="visible")
chart.screenshot(path="sales-chart.png")
| 목적 | 방법 | 주의점 |
|---|---|---|
| 현재 화면 기록 | page.screenshot(path=…) | 뷰포트 크기에 따라 결과가 달라짐 |
| 전체 페이지 | full_page=True | 매우 긴 페이지는 이미지가 커짐 |
| 특정 요소 | locator.screenshot() | 요소가 보일 때까지 대기 |
| 메모리 저장 | path 없이 bytes 반환 | 직접 파일·스토리지 처리 필요 |
결과 비교가 목적이라면 뷰포트, 기기 배율, 언어, 시간대와 글꼴 환경을 고정해야 합니다. 움직이는 배너나 실시간 시각이 포함되면 매번 다른 이미지가 생성될 수 있으므로 캡처 대상과 시점을 명확히 정하십시오.
실패를 줄이는 수집 구조
- URL과 수집 시각, 작업 ID를 먼저 기록합니다.
- Context와 Page를 만들고 페이지 이동 타임아웃을 설정합니다.
- 핵심 요소를 Locator로 기다린 뒤 상호작용합니다.
- 필수 필드를 추출하고 빈 값·중복을 검증합니다.
- 실패 시 오류 메시지와 화면 캡처를 저장합니다.
- 브라우저를 닫고 결과를 CSV나 데이터베이스에 기록합니다.
from pathlib import Path
from playwright.sync_api import sync_playwright, TimeoutError
def collect(url):
Path("errors").mkdir(exist_ok=True)
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page(viewport={"width": 1440, "height": 900})
try:
page.goto(url, wait_until="domcontentloaded", timeout=30_000)
title = page.locator("h1").inner_text(timeout=10_000)
return {"url": url, "title": title.strip()}
except TimeoutError:
page.screenshot(path="errors/timeout.png", full_page=True)
raise
finally:
browser.close()
여러 페이지를 병렬 처리하면 속도는 빨라지지만 서버와 내 컴퓨터 양쪽의 부하가 커집니다. 작은 동시성으로 시작하고 응답 상태, 메모리 사용량, 오류율을 관찰하면서 조정하십시오. 사이트가 제시한 접근 제한과 robots.txt, 이용약관을 우선해야 합니다.
실수 방지 체크리스트
- 필요한 데이터가 공개 API나 정적 HTML에 이미 있는지 먼저 확인합니다.
- Playwright 패키지와 브라우저 바이너리를 모두 설치합니다.
- 고정 sleep 대신 Locator와 명확한 완료 조건을 사용합니다.
- 선택자가 실제 페이지의 의미 있는 구조를 가리키는지 확인합니다.
- 페이지 이동, 요소 탐색, 전체 작업에 각각 제한 시간을 둡니다.
- 더 보기와 무한 스크롤에는 최대 반복 횟수를 설정합니다.
- 실패 URL, 오류 종류, 스크린샷을 남겨 재현 가능하게 만듭니다.
- 브라우저와 Context를 예외 상황에서도 닫습니다.
- 개인정보·저작권·이용약관·robots.txt와 접근 빈도를 확인합니다.
자주 묻는 질문
1. Selenium 대신 Playwright를 사용해야 하나요?
둘 다 브라우저 자동화가 가능합니다. 새 프로젝트라면 자동 대기, Context 격리, 여러 렌더링 엔진과 네트워크 제어 등 필요한 기능을 비교해 선택하십시오.
2. Playwright로 크롤링하는 것은 합법인가요?
도구 자체와 별개로 대상 사이트의 이용약관, robots.txt, 접근 권한, 개인정보와 저작권, 지역별 법규를 확인해야 합니다.
3. headless와 headed의 차이는 무엇인가요?
headless는 화면을 띄우지 않고 실행하며, headed는 실제 브라우저 창을 보여줍니다. 개발 중 동작 확인에는 headed가 편리하고 운영에는 headless가 일반적입니다.
4. playwright install을 꼭 실행해야 하나요?
네. Python 패키지 설치와 브라우저 실행 파일 설치는 별도 단계이므로 사용할 브라우저 바이너리를 설치해야 합니다.
5. time.sleep()을 쓰면 안 되나요?
항상 금지되는 것은 아니지만 페이지 준비를 보장하지 못합니다. 요소 상태나 항목 수 변화처럼 실제 완료 조건을 기다리는 방식이 더 안정적입니다.
6. 전체 페이지 스크린샷은 어떻게 찍나요?
page.screenshot(path="page.png", full_page=True)처럼 호출합니다. 페이지가 매우 길면 파일 크기와 메모리 사용량을 확인하십시오.
7. 특정 요소만 이미지로 저장할 수 있나요?
Locator로 요소를 선택한 뒤 locator.screenshot(path=...)을 사용하면 됩니다. 먼저 요소가 표시됐는지 확인하십시오.
8. 로그인 상태를 재사용할 수 있나요?
권한이 있는 서비스라면 저장된 인증 상태를 Context에 적용할 수 있습니다. 인증 파일에는 민감한 쿠키가 포함될 수 있으므로 비밀정보처럼 관리해야 합니다.
9. 여러 페이지를 동시에 처리해도 되나요?
가능하지만 대상 서버와 로컬 자원에 부담을 줍니다. 낮은 동시성부터 시작하고 제한, 재시도, 오류 기록을 함께 설계하십시오.
10. Playwright로 가져온 HTML을 BeautifulSoup에 넣어도 되나요?
네. 동적 렌더링을 끝낸 뒤 page.content() 결과를 전달하면 익숙한 CSS Selector 기반 파싱을 이어갈 수 있습니다.
공식 출처
공식 문서 확인일: 2026년 8월 30일. 설치 명령, 지원 운영체제와 API는 버전에 따라 달라질 수 있으므로 실제 적용 시 최신 공식 문서를 다시 확인하시기 바랍니다.