본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 06. 05. 17:28

웹 스크래핑을 잘 안다고 생각했다 — JavaScript를 마주하기 전까지는

요약

동적 웹사이트 스크래핑의 어려움을 해결하기 위해 기존의 CSS 선택자 방식 대신 Playwright와 Vision AI를 결합한 새로운 접근법을 제안합니다. JavaScript로 렌더링되는 복잡한 구조에서도 AI가 시각적/텍스트적 정보를 바탕으로 구조화된 데이터를 추출할 수 있음을 보여줍니다.

핵심 포인트

  • 동적 콘텐츠 렌더링으로 인한 기존 스크래핑 방식의 한계
  • CSS 선택자 유지보수의 어려움과 불안정성 문제
  • Playwright와 Vision AI를 결합한 새로운 스크래핑 패러다임
  • 자연어 지침을 통한 구조화된 데이터 추출의 효율성

저는 수년 동안 웹사이트를 스크래핑해 왔습니다. 깔끔한 CSS Selector가 있는 정적 HTML 페이지를 준다면, 10분 안에 BeautifulSoup 스크립트를 실행할 수 있습니다. 식은 죽 먹기죠.

하지만 최근 웹 환경이 변했습니다. 모두가 React, Vue 또는 콘텐츠를 클라이언트 사이드 (client-side)에서 렌더링하는 다른 프레임워크를 사용하고 있습니다. 제가 원하는 데이터는 초기 HTML에 들어있지 않습니다. API 호출 뒤에 숨겨져 있으며, 페이지가 로드된 후에 div 요소들을 채웁니다.

몇 주 전, 저는 현대적인 이커머스 사이트에서 제품 상세 정보를 추출해야 했습니다. URL도 있었고, 구조도 알고 있었습니다 (적어도 그렇다고 생각했습니다). 제가 신뢰하던 requests + BeautifulSoup 스크립트를 작성하여 실행했지만... 아무것도 얻지 못했습니다. 빈 컨테이너, 도처에 널린 None 값뿐이었습니다.

그때 진짜 문제에 직면했습니다: 바로 **동적 콘텐츠 (dynamic content)**였습니다.

내가 시도했던 것들 (그리고 실패한 것들)

먼저, 네트워크 탭 (network tab)을 파헤쳤습니다. XHR 요청을 찾아냈고, 저만의 헤더와 쿠키를 사용하여 이를 모방했습니다. 잠시 동안은 작동했지만, 곧 사이트에서 CSRF 토큰과 속도 제한 (rate-limiting)을 추가했습니다. 제 스크립트는 다시 망가졌습니다.

다음으로, Playwright (headless Chrome)를 시도했습니다. 페이지를 로드하고, Selector를 기다린 뒤, 텍스트를 추출할 수 있었습니다. 하지만 Selector가 불안정했습니다. 배포될 때마다 클래스(class)가 바뀌었습니다. 매주 Selector를 다시 작성해야 했습니다. 유지보수는 악몽 같았습니다.

심지어 원본 JavaScript 번들에서 정규 표현식 (regex)을 사용하여 스크립트 태그에 임베디드된 JSON을 찾는 실험도 했습니다. 그것은 정확히 앱의 한 버전에서만 작동했습니다. 그 후 그들은 모든 것을 코드 분할 (code-split)하고 미니파이 (minified)했습니다.

저는 막혔습니다. 모든 접근 방식이 끊임없는 관리 (babysitting)를 요구했습니다.

마침내 성공한 접근 방식

그때 한 동료가 다른 사고방식을 제안했습니다: HTML을 파싱하려고 하지 마라. 브라우저가 페이지를 렌더링하게 두고, 그 다음에 AI에게 무엇이 보이는지 물어봐라.

아이디어는 간단합니다: headless 브라우저를 사용하여 페이지를 로드하고 JavaScript가 완료될 때까지 기다립니다. 스크린샷을 찍거나 (또는 렌더링된 DOM을 가져오거나), 그 시각적 또는 텍스트적 표현을 시각 능력을 갖춘 언어 모델 (vision-capable language model)에 전달합니다. 모델은 자연어 지침을 기반으로 구조화된 데이터를 추출할 수 있습니다.

이것은 게임의 판도를 완전히 바꿉니다. 취약한 CSS 선택자(CSS selectors)를 사용하거나 API를 역공학(reverse-engineering)하는 대신, 다음과 같이 말하면 됩니다: "메인 콘텐츠 영역에서 제품 이름, 가격, 설명을 찾아줘." 그러면 AI가 레이아웃을 파악합니다.

다음은 Python, Playwright, 그리고 OpenAI의 GPT-4 Vision을 사용한 작동 예시입니다:

import asyncio
import base64
from playwright.async_api import async_playwright
...

이제 수십 개의 선택자를 유지 관리하는 대신, 템플릿당 하나의 자연어 지침만 유지 관리하면 됩니다. 사이트가 디자인을 변경하더라도 스크립트를 다시 실행하기만 하면 됩니다. AI가 적응하기 때문입니다.

배운 점 (Lessons Learned)

이 접근 방식이 완벽한 것은 아닙니다. 제가 발견한 트레이드오프(trade-offs)는 다음과 같습니다:

장점 (Pros):

  • 레이아웃 변경에 강함 — AI는 DOM 구조가 아닌 시각적 출력을 봅니다.
  • JS 역공학 불필요 — 단순히 페이지를 로드하고 네트워크 유휴(network idle) 상태를 기다리면 됩니다.
  • SPA, Shadow DOM, Canvas와 호환 — 인간의 눈으로 읽을 수 있는 것이라면 무엇이든 가능합니다.

단점 (Cons):

  • 비용 — 페이지를 스크래핑할 때마다 API 크레딧이 소모됩니다 (GPT-4 Vision은 이미지당 약 $0.01). 소규모 작업에는 괜찮지만, 수천 페이지를 처리할 때는 비용이 쌓입니다.
  • 지연 시간 (Latency) — 단일 스크래핑에 5~15초가 소요됩니다 (브라우저 시작 + 스크린샷 + API 호출). 실시간 스크래핑에는 적합하지 않습니다.
  • 정확도 — 모델이 숫자를 환각(hallucinate)하거나 이미지 내의 가격을 잘못 읽을 수 있습니다. 출력값에 대한 검증 로직이 필요합니다.
  • 속도 제한 (Rate limits) — 헤드리스 브라우저(headless browser)의 동시성(concurrency)과 AI 제공업체의 제한 사항 모두에 영향을 받습니다.

사용하지 말아야 할 경우:

  • 대상 사이트에 깔끔하고 버전 관리되는 API가 있는 경우 — 그냥 그것을 직접 사용하세요.
  • 수백만 페이지를 크롤링해야 하는 경우 — 비용과 시간이 감당하기 어려울 것입니다.
  • 데이터가 로그인이나 CAPTCHA 뒤에 있는 경우 — 여전히 인증 처리가 필요합니다.

다음에 다시 한다면 다르게 할 점

만약 처음부터 다시 시작해야 한다면, 먼저 공개 API나 더 간단한 데이터 소스가 있는지 확인하겠습니다. 저렴한 옵션들을 모두 소진한 후에야 비로소 비전 모델(vision model)을 사용할 것입니다.

또한, 스크린샷과 결과물을 공격적으로 캐싱(cache)하겠습니다. 만약 제품 카탈로그를 매일 스크래핑한다면, 아마 모든 제품을 다시 분석할 필요는 없을 것입니다. 그저 변경 사항만 감지하면 됩니다.

마지막으로, 비용과 지연 시간(latency)을 줄이기 위해 로컬에서 실행할 수 있는 오픈 소스 비전 모델(LLaVA 또는 Qwen-VL 등)을 탐색하겠습니다. 트레이드오프(trade-off)는 정확도가 낮아진다는 점이지만, 레이아웃이 예측 가능한 경우에는 충분할 수도 있습니다.

여러분의 차례

이 방법은 제가 매주 스크래퍼를 다시 작성해야 하는 상황을 면하게 해주었지만, 만능 해결책(silver bullet)은 아닙니다. 저는 여전히 하이브리드 접근 방식(hybrid approaches)을 실험 중입니다. 즉, 안정적인 요소에는 전통적인 셀렉터(selector)를 사용하고, 동적인 요소에는 AI를 사용하는 방식입니다.

동적 웹 스크래핑(dynamic web scraping)에서 비슷한 어려움을 겪은 적이 있으신가요? CSS 셀렉터가 실패할 때 여러분이 주로 사용하는 기술은 무엇인가요? 댓글을 통해 여러분에게 효과가 있었던(혹은 효과가 없었던) 방법들을 듣고 싶습니다.

AI 자동 생성 콘텐츠

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

원문 바로가기
0

댓글

0