본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 05. 31. 17:07

AI 파싱을 통해 마침내 그 불가능했던 사이트를 스크래핑한 방법

요약

기존의 셀렉터 기반 웹 스크래핑 방식이 가진 한계를 LLM을 활용한 AI 파싱 기술로 극복한 사례를 다룹니다. 동적 클래스명과 봇 탐지 기술로 인해 무너지는 전통적인 파싱 방식 대신, HTML 구조를 직접 이해하는 AI를 통해 안정적인 데이터 추출 방법을 제시합니다.

핵심 포인트

  • 전통적인 셀렉터 기반 방식은 프런트엔드 변경에 매우 취약함
  • LLM을 활용하면 복잡한 XPath나 CSS 셀렉터 없이도 데이터 추출 가능
  • HTML 소스를 직접 입력하여 구조적 변화에 유연하게 대응
  • Playwright와 LLM을 결합하여 동적 콘텐츠 추출 효율 극대화

저는 수년 동안 웹 스크래핑 (Web Scraping)을 해왔습니다. 하지만 지난달, 제가 가진 모든 도구 세트에 의문을 갖게 만드는 벽에 부딪혔습니다.

현대적인 이커머스 (e-commerce) 사이트에서 제품 데이터—가격, 설명, 평점 등 일반적인 것들—를 추출해야 했습니다. 간단해 보이죠? 저는 이런 일을 수백 번도 더 해봤습니다. 개발자 도구 (DevTools)를 열고, API 엔드포인트 (endpoint)를 찾고, 토큰을 복사한 뒤 JSON을 가져오면 됩니다. 하지만 이 사이트는 달랐습니다.

HTML은 React 프레임워크에 의해 생성된, 무작위 클래스 이름을 가진 divspan의 바다였습니다. 모든 페이지는 JavaScript를 통해 로드되었고, 실제 데이터는 스크립트 안에 숨겨져 있거나 초기 렌더링 (render) 이후에 호출되었습니다. Requests + BeautifulSoup 조합은 뼈대만 반환했습니다. Selenium은 작동했지만, 사이트에 공격적인 속도 제한 (rate-limiting)이 걸려 있었습니다. 요청을 다섯 번 정도 보내자 제 IP가 차단되었습니다. 프록시 (proxy)를 교체하고, 지연 시간을 추가하고, 심지어 브라우저 핑거프린트 (browser fingerprints)를 흉내 내기도 했습니다. 하지만 수십 페이지를 지나자 도처에서 CAPTCHA가 나타났습니다. 실제 로직을 작성하는 것보다 봇 탐지 (bot detection)와 싸우는 데 더 많은 노력을 쏟고 있었습니다.

효과가 없었던 시도들

저는 일반적인 플레이북을 따라가 보았습니다:

  • Requests + BeautifulSoup – 동적 콘텐츠 (dynamic content)를 전혀 가져오지 못했습니다.
  • Selenium + headless Chrome – 작동은 했지만, 느리고 취약했습니다. CSS 클래스 하나만 바뀌어도 셀렉터 (selector)가 깨졌습니다.
  • Playwright – Selenium보다 빠르지만, 여전히 안티 디텍션 (anti-detection) 기술(user-agent, viewport 등)이 필요했습니다.
  • Scrapy with Splash – 이 프로젝트에는 과했고, Splash의 JavaScript 엔진은 모든 비동기 렌더링 (async renderings)을 처리하지 못했습니다.
  • Apify / 스크래핑 API (scraping APIs) – 작동은 했지만 비용이 빠르게 쌓였고, 여전히 수동 셀렉터가 필요했습니다.

모든 접근 방식은 사이트의 프런트엔드 (front-end) 변화를 계속 따라가야 한다는 점을 요구했습니다. 클래스 순서가 바뀌거나 새로운 마이크로 프런트엔드 (micro-frontend)가 추가되면 제 파서 (parser)는 망가졌습니다. 저는 XPath 표현식과 정규 표현식 (regex)으로 이루어진 취약한 탑을 유지보수하고 있었습니다.

전환점

한 동료가 셀렉터를 전혀 사용하지 않고 HTML에서 직접 데이터를 추출하기 위해 LLM을 사용하는 것에 대해 언급했습니다. 처음에는 웃음이 나왔습니다. "생 HTML을 GPT에 밀어 넣고 JSON을 달라고 하라는 건가요?" 하지만 저는 절박했습니다. 그래서 작은 샘플로 한번 시도해 보았습니다.

저는 제품 페이지의 전체 innerHTML (약 30KB)을 가져와 간단한 프롬프트와 함께 OpenAI의 gpt-4o에 입력했고, 결과로 완벽한 JSON을 돌려받았습니다. 셀렉터(selector)도, JavaScript를 기다릴 필요도 없었습니다. 단 한 번의 초기 requests 호출로 가져올 수 있는 가공되지 않은 소스(raw source)만 있으면 되었습니다(서버 사이드 렌더링된 스켈레톤(skeleton)에 <script> 태그 형태로 제품 데이터가 포함되어 있었습니다). 데이터가 지연 로딩(lazy-loading)되는 경우에도, Playwright에서 전체 DOM이 로드될 때까지 기다린 후 outerHTML을 저장하여 LLM에 전송했습니다.

작동 원리

핵심 아이디어는 다음과 같습니다. 깨지기 쉬운 추출 규칙(extraction rules)을 작성하는 대신, 데이터 모델을 설명하고 LLM이 HTML을 구조화된 출력(structured output)으로 파싱하게 만드는 것입니다. 사이트의 특정 마크업(markup)을 이해할 필요가 없습니다. LLM은 전체 블록을 보고, 가격 정보가 무작위적인 <span class="a1b2c3"> 안에 있더라도 그 위치를 "이해"합니다.

이를 안정적으로 만들기 위해서는 함수 호출 (function calling) (도구 사용, tool use)을 사용하여 JSON 스키마 (JSON schema)를 강제하십시오. 그러면 LLM은 당신이 요청한 필드와 지정한 타입을 정확하게 출력할 것입니다.

코드 예시

다음은 OpenAI API를 사용하여 HTML에서 제품의 이름, 가격, 설명 및 평점을 추출하는 Python 함수입니다. 이 코드는 이미 원본 HTML(fetch 또는 헤드리스 브라우저를 통해 가져온 것)을 가지고 있다고 가정합니다.

import json
from openai import OpenAI

...

이 접근 방식은 정보가 HTML에 존재하기만 한다면, 사이트별 설정 없이도 수십 개의 서로 다른 사이트에서 작동하는 단일 함수를 제공합니다.

저는 곧 여러 호스팅 도구들이 이 패턴을 자동화하고 있다는 사실을 발견했습니다. 그중 하나는 LLM 기반 추출을 API로 래핑(wrap)한 Interwest Info AI입니다. 하지만 이 기술 자체는 완전히 공개되어 있습니다. 도구 사용(tool use)을 지원하는 어떤 LLM으로도 직접 구현할 수 있습니다.

교훈 및 트레이드오프 (trade-offs)

이 접근 방식이 만능 해결책(silver bullet)은 아닙니다. 제가 배운 점은 다음과 같습니다:

  • 비용 (Cost): 추출당 약 $0.01–0.05가 소요됩니다 (HTML 크기와 모델에 따라 다름). 수백 페이지 정도라면 괜찮지만, 수백만 페이지라면 더 저렴한 대체 수단(fallback)이 필요할 것입니다.
  • 지연 시간 (Latency): LLM 호출에 2~5초가 걸립니다. 실시간 스크래핑(real-time scraping)이 필요하다면 너무 느립니다. 저는 오프라인 배치 작업(offline batch jobs)에 사용합니다.
  • 정확도 (Accuracy): 일반적인 필드에 대해서는 95% 이상의 정확도를 보이지만, HTML이 혼란스러운 경우(예: 여러 개의 가격 정보가 있는 경우) 환각(hallucinate) 현상이 발생할 수 있습니다. 항상 출력값을 검증해야 합니다.
  • 토큰 제한 (Token limits): HTML 페이지가 크면 모델의 컨텍스트 창(context window)을 초과할 수 있습니다. 전송하기 전에 관련 없는 부분(긴 스크립트 등)을 제거해야 할 수도 있습니다.
  • 안티 봇 탐지 (Anti-bot detection)는 여전히 존재함: 원본 HTML을 가져오는 과정에는 여전히 Cloudflare 등을 우회하는 과정이 필요합니다. 이 기술은 추출 계층(extraction layer)을 대체할 뿐, 가져오기 계층(fetching layer)을 대체하는 것이 아닙니다.
  • 사용하지 말아야 할 때: 사이트가 깔끔한 API나 JSON-LD를 제공한다면 그것을 사용하세요. 매일 수천 페이지를 스크래핑해야 한다면 전통적인 파서(traditional parser)가 더 저렴하고 빠릅니다. 또한, 데이터가 이미지나 PDF에 있다면 다른 접근 방식(멀티모달 LLM)이 필요합니다.

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

가장 간단한 것부터 시작하겠습니다. LLM을 사용하기 전에 임베디드된 JSON(<script type="application/ld+json">)이 있는지 먼저 확인하겠습니다. 그것이 실패하면 그때 LLM을 시도하겠습니다. 또한 토큰 사용량과 비용을 줄이기 위해 데이터가 포함될 가능성이 낮은 요소(푸터, 네비게이션 또는 긴 스크립트 등)를 제거하도록 HTML을 전처리하겠습니다.

대량 작업의 경우 하이브리드 방식을 사용하겠습니다. 필드의 90%는 저렴한 전통적 선택자(traditional selectors)를 사용하고, 까다로운 필드(임의의 설명 등)에 대해서만 LLM을 사용하는 방식입니다.

마치며

LLM 기반 추출은 웹 스크래핑에 대한 저의 사고 모델을 바꾸어 놓았습니다. DOM과 싸우는 대신, 제가 원하는 것을 설명하기만 하면 모델이 구조를 파악하도록 하는 것입니다. 모든 작업에 적합한 도구는 아니지만, XPath가 다섯 번째로 깨질 때 이 방식은 구원 투수가 되어줍니다.

궁금합니다. HTML을 파싱하기 위해 LLM을 사용해 보신 적이 있나요? 사이트가 저항할 때 여러분이 즐겨 사용하는 해킹 방법은 무엇인가요?

AI 자동 생성 콘텐츠

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

원문 바로가기
0

댓글

0