본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 06. 22. 03:36

Python을 사용하여 Gemini 2.5 및 Playwright 기반의 WAF 내성 및 셀렉터 프리(Selector-Free) 웹 스크래퍼를

요약

Gemini 2.5 Flash와 Playwright를 활용하여 WAF 차단을 우회하고 CSS 셀렉터 없이도 데이터를 추출할 수 있는 AI 기반 웹 스크래퍼 구축 방법을 소개합니다. HTML을 Markdown으로 변환하여 토큰 효율을 높이고, Pydantic을 통해 구조화된 JSON 출력을 보장하는 아키텍처를 다룹니다.

핵심 포인트

  • Playwright를 이용한 스텔스 크롤링으로 WAF 및 안티 봇 시스템 우회
  • HTML을 Markdown으로 변환하여 LLM 토큰 사용량 및 지연 시간 최적화
  • Pydantic의 create_model을 활용한 런타임 동적 스키마 컴파일
  • Gemini의 구조화된 출력을 통한 검증된 JSON 데이터 추출

웹 스크래퍼를 구축해 본 개발자라면 누구나 다음과 같은 고통을 알고 있습니다:

취약한 CSS 셀렉터(Selectors)/XPath: 대상 웹사이트가 Tailwind 클래스를 업데이트하거나 React 컴포넌트 트리를 변경하면 데이터 파이프라인이 중단됩니다.
웹 애플리케이션 방화벽 (WAFs): Cloudflare, DataDome, Akamai 등이 에지(edge)에서 요청을 차단하여 403 Forbidden 또는 챌린지 페이지를 반환합니다.
우리는 셀렉터를 완전히 우회하고 안티 봇(anti-bot) 시스템에 탄력적으로 대응하는 스크래핑 엔진을 구축하고 싶었습니다.

다음은 FastAPI, Playwright, 그리고 Gemini 2.5 Flash를 사용하여 QueryScrape AI를 구축하는 데 사용한 정확한 아키텍처입니다.

🛠️ 아키텍처 스택
우리의 스크래퍼는 3단계 파이프라인에 의존합니다:

스텔스 크롤러 (Playwright): 기본 안티 봇 차단기를 우회하고 클라이언트 측 JavaScript를 실행하기 위해 사용자 에이전트(user-agents), 화면 크기, 브라우저 플래그를 맞춤 설정한 헤드리스 Chromium 인스턴스를 실행합니다.
DOM 클리너 (BeautifulSoup & html2text): 노이즈가 심한 스크립트, 스타일, 헤더, 푸터를 제거하여 원본 HTML을 토큰 효율적인 Markdown으로 변환합니다.
동적 Pydantic 스키마 컴파일러 및 Gemini 구조화된 출력 (structured output): API 요청에 제출된 필드를 런타임에 Pydantic 모델로 컴파일하여, LLM이 해당 모델과 일치하는 검증된 JSON을 출력하도록 강제합니다.

POST URL + 스키마

페이지 가져오기 (Fetch Page)

HTML 소스

클린 Markdown

필드 컴파일

출력 검증 스키마

검증된 JSON 출력

API 사용자

FastAPI 서버

Playwright 스텔스 크롤러

DOM 클리너

Gemini 2.5 Flash

동적 Pydantic 모델

💻 내부 동작 원리: 핵심 코드

  1. 원본 HTML을 Markdown으로 클리닝
    수천 줄의 HTML이 포함된 원본 DOM 트리를 LLM에 전달하는 것은 토큰을 낭비하고 지연 시간(latency)을 폭증시킵니다. 우리는 콘텐츠가 아닌 태그를 제거하고 HTML 구조를 Markdown으로 변환합니다:

from bs4 import BeautifulSoup
import html2text

def clean_html(html_content: str) -> str:
soup = BeautifulSoup(html_content, "html.parser")

# script/style 보일러플레이트 및 노이즈가 심한 비콘텐츠 요소를 제거합니다
for element in soup(["script", "style", "nav", "footer", "header", "svg", "noscript", "iframe"]):
    element.decompose()
...
  1. 런타임 시 Pydantic 스키마 (schemas) 동적 컴파일: 사용자가 API를 호출할 때, 다음과 같이 추출하고자 하는 필드 배열을 전달합니다:

[
{"name": "title", "type": "string", "description": "상품의 제목"},
{"name": "price", "type": "float", "description": "USD 기준 숫자 가격"}
]

우리는 Pydantic의 create_model 함수를 사용하여 이러한 필드들을 검증 가능한 Pydantic 클래스로 동적으로 컴파일합니다:

from pydantic import BaseModel, Field, create_model
from typing import List, Dict, Any, Type

TYPE_MAP = {
"string": str, "integer": int, "float": float, "boolean": bool
}

def compile_schema(fields: List[Dict[str, Any]], is_list: bool = False) -> Type[BaseModel]:
pydantic_fields = {}
for f in fields:
name = f.get("name")
type_str = f.get("type", "string").lower()
desc = f.get("description", f"{name}에 대해 추출된 값")

    py_type = TYPE_MAP.get(type_str, str)
    pydantic_fields[name] = (py_type, Field(description=desc))

...
  1. 구조화된 스키마 강제 적용을 통한 Gemini 호출: 우리는 최신 google-genai SDK를 활용합니다. 생성 설정 (generation configuration)의 response_schema에 컴파일된 Pydantic 스키마 클래스를 직접 전달함으로써, Gemini는 출력 형식을 보장하며, 이를 통해 취약한 정규 표현식 (regex) 재시도 루프를 작성해야 하는 번거로움을 덜어줍니다:

from google import genai
from google.genai import types

async def extract_structured_data(content: str, fields: list, is_list: bool = False):
client = genai.Client(api_key=YOUR_API_KEY)
target_model = compile_schema(fields, is_list)

prompt = f"Extract structured information according to the schema:\n\n{content}"

response = await client.aio.models.generate_content(
...

🛡️ 에지 방화벽 (WAF) 우회하기 (Bypassing Edge Firewalls (WAF))
이를 실제로 보여주기 위해, 저희는 랜딩 페이지에 직접 공개 WAF Shield Detector 도구를 구축했습니다.

이 도구는 두 가지 작업을 수행합니다:

  1. 단순한 정적 HTTP 연결을 수행합니다 (이는 cf-ray 또는 x-datadome-cid와 같은 WAF 응답 헤더를 쉽게 트리거하여 403 Blocked 페이지를 생성합니다).
  2. 저희의 동적 스텔스-Playwright 크롤러 (dynamic stealth-Playwright crawler)를 실행합니다 (대상 페이지를 로드하고, JS를 실행하며, 깨끗한 마크다운을 추출합니다).

저희 플레이그라운드(playground)에서의 나란한 비교는 AI 기반 크롤러가 에지 봇 방어 체계(edge bot-guards)를 우회하여 LLM에 구조화된 데이터를 직접 제공할 수 있음을 시각적으로 증명합니다.

📈 더 알아보기 및 체험하기
저희는 AWS App Runner에 완전히 작동하는 API 파이프라인을 배포했습니다.

👉 라이브 플레이그라운드를 체험하고, 대상 URL의 WAF 헤더를 확인하며, 매월 1,000회의 무료 API 추출을 받아보세요: 🔗 Live Demo Playground

서버와 플레이그라운드의 전체 소스 코드는 오픈 소스입니다. 회복 탄력성이 있는 데이터 파이프라인 (resilient data pipelines)을 구축하기 위해 생성 모델 (generative models)을 사용하는 것에 대해 어떻게 생각하시나요? 아래 댓글에서 함께 논의해 봅시다!

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0