본문으로 건너뛰기

© 2026 Molayo

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

고장 난 파서와 싸우는 것을 그만두었습니다 — LLM을 사용하여 웹 데이터를 안정적으로 추출하는 방법

요약

기존의 CSS 선택자나 정규 표현식을 이용한 웹 스크래핑의 취약성을 LLM의 함수 호출(Function Calling) 기능을 통해 해결하는 방법을 소개합니다. HTML 구조 변화에 관계없이 데이터의 의미를 파악하여 구조화된 JSON으로 추출하는 효율적인 워크플로우를 제안합니다.

핵심 포인트

  • CSS 선택자 기반 스크래핑은 웹사이트 레이아웃 변경에 매우 취약함
  • LLM의 함수 호출 기능을 사용하면 스키마에 맞는 JSON 추출 가능
  • 데이터의 구조가 아닌 '의미'를 추출하는 것이 핵심
  • 다양한 레이아웃을 가진 이질적인 소스 처리에 매우 효과적임

몇 달 전, 저는 한정판 스니커즈 가격 추적기를 만들고 있었습니다. 50개 이상의 상점 URL 목록이 있었고, 제품명, 가격, 재고 여부, 사이즈 옵션을 추출해야 했습니다. 전형적인 스크래핑(Scraping) 작업이었죠, 맞죠?

저는 CSS 선택자(CSS selectors)로 시작했습니다. BeautifulSoup + requests 조합이었죠. 일주일 정도는 잘 작동했습니다. 그러다 한 사이트가 클래스 이름(class names)을 변경했습니다. 다른 사이트는 동적 로더(dynamic loader)를 추가했습니다. 세 번째 사이트는 DOM을 변화시키는 광고를 삽입했습니다. 데이터를 실제로 사용하는 시간보다 선택자를 수정하는 데 더 많은 시간을 보냈습니다.

원시 HTML(raw HTML)에 정규 표현식(regex)을 시도해 보았습니다. 그것은 재앙이었습니다. 취약하고 가독성이 떨어졌죠. Playwright를 사용한 헤드리스 브라우저(headless browsers)로 특정 요소가 나타날 때까지 기다리는 방식도 시도했습니다. 레이아웃이 변경되면 여전히 작동이 멈췄습니다.

문제는 근본적이었습니다. 저는 프레젠테이션 레이어(presentation layer)를 역공학(reverse-engineer)하려고 노력하고 있었습니다. 하지만 제가 정말로 원했던 것은 콘텐츠의 **의미(meaning)**였습니다. 즉, CSS 클래스가 아니라 제품의 가격이었습니다.

전환점: 구조화된 추출을 위한 LLM

저는 텍스트 생성용으로 GPT를 사용해 왔지만, 데이터 추출용으로는 고려하지 않았습니다. 그러다 함수 호출(function calling) 데모를 보게 되었습니다. LLM에게 스키마(schema)에 맞는 JSON 객체를 반환하도록 요청할 수 있는 기능입니다. 그것이 바로 제가 필요로 했던 것이었습니다. 모델에 원시 HTML(또는 단순화된 텍스트 버전)을 제공하고, 원하는 필드를 정의한 다음, 모델이 매핑(mapping)을 스스로 파악하게 하는 것입니다.

핵심 아이디어는 다음과 같습니다:

  1. 페이지를 가져오기 (또는 단순화된 텍스트 버전 사용)
  2. 원하는 데이터에 대한 스키마(schema) 정의
  3. 페이지 콘텐츠와 스키마를 함께 LLM 호출
  4. 구조화된 JSON 반환

이것은 마법이 아닙니다. 트레이드오프(trade-offs)도 존재합니다. 하지만 저의 즉각적인 문제를 아주 아름답게 해결해 주었습니다.

OpenAI 함수 호출을 사용한 코드 예시

구체적인 예시를 보여드리겠습니다. Python과 OpenAI API를 사용하겠습니다. 핵심은 모델에게 어떤 JSON 형태를 반환해야 하는지 정확히 알려주는 functions 파라미터입니다.

import openai
from pydantic import BaseModel
import json
...

이렇게 하면 다음과 같은 결과가 반환됩니다:

{
  "name": "Air Max 2024 'Triple Black'",
  "price": 189.99,
...

함수 정의(function definition)는 모델에게 어떤 필드를 채워야 하는지 정확하게 알려줍니다. CSS도, 정규 표현식(regex)도, 취약한 셀렉터(selectors)도 필요 없습니다.

이 접근 방식이 빛을 발하는 경우

  • 빈번하게 변경되는 사이트 — HTML 구조가 자주 바뀌는 블로그, 이커머스, 뉴스 사이트
  • 이질적인 소스 (Heterogeneous sources) — 각기 다른 레이아웃을 가진 20개의 서로 다른 사이트를 스크래핑하는 경우
  • 텍스트 중심의 콘텐츠 — 태그가 아닌 텍스트 자체에 의미가 담긴 기사, 리뷰, 설명글

저는 현재 스니커즈 트래커(sneaker tracker)에 이 방식을 3개월째 사용하고 있습니다. 프롬프트(prompts)를 조정한 것은 단 두 번뿐이었습니다 (한 사이트가 가격을 이미지로 표시하기 시작했을 때였으며, 이는 이미지-투-텍스트(image-to-text) 단계를 추가하여 해결했습니다).

트레이드오프(Trade-offs) 및 한계점

솔직해집시다. 이것은 만능 해결책(silver bullet)이 아닙니다.

비용 (Cost): 추출할 때마다 아주 적은 비용이 발생합니다. 하루에 한 번씩 확인하는 50개의 사이트 기준으로 한 달에 약 1.50달러 정도입니다. 괜찮은 수준이죠. 하지만 수백만 개의 페이지를 스크래핑한다면 비용은 빠르게 불어납니다.

지연 시간 (Latency): API 호출은 페이지당 1~3초가 소요됩니다. 수백 페이지 정도라면 괜찮지만, 실시간 스크래핑에는 적합하지 않습니다.

환각 (Hallucination): 데이터가 존재하지 않을 경우 모델이 데이터를 지어낼 수 있습니다. 존재하지 않는 가격을 반환하는 것을 본 적이 있습니다. 완화 방법으로는 신뢰도 점수(confidence scores)를 요청하거나 소스 텍스트 증거(source text evidence)를 요구하는 방법이 있습니다. 하지만 이는 복잡성을 더합니다.

컨텍스트 윈도우 (Context window): 페이지 전체를 한꺼번에 넣을 수는 없습니다. 저는 8,000자로 잘라서(truncate) 사용하지만, 때로는 가격 정보가 잘려 나간 HTML 부분에 포함되어 있을 때가 있습니다. 해결책은 trafilatura와 같은 도구를 사용하여 주요 콘텐츠 영역을 추출하도록 HTML을 전처리하는 것입니다.

개인정보 보호 (Privacy): 민감하거나 내부용 사이트를 스크래핑하는 경우, 데이터를 API로 전송하는 것이 문제가 될 수 있습니다. 로컬 모델(예: 함수 호출(function calling) 기능이 있는 Ollama)을 사용할 수도 있지만, 결과의 신뢰도는 떨어집니다.

고려할 만한 대안들

  • 전통적인 스크래핑 + ML (Traditional scraping + ML): readabilipy와 같은 라이브러리를 사용하여 주요 콘텐츠를 추출한 다음, 가격에 대한 개체명 인식 (NER)을 위해 더 작은 모델을 적용합니다. 비용은 저렴하지만 설정 과정이 더 복잡합니다.
  • 시각적 스크래핑 도구 (Visual scraping tools): Diffbot 또는 Browse AI와 같은 서비스입니다. 작동은 하지만 가격이 비싸고 때때로 여전히 오류가 발생합니다.
  • 프롬프트 전용 추출 (Prompt-only extraction): 함수 호출 (function calling) 없이 단순히 JSON을 요청합니다. 모델이 스키마 (schema)에서 벗어날 수 있기 때문에 신뢰도가 낮습니다.

다음에 제가 다르게 할 일

저는 하이브리드 접근 방식으로 시작할 것입니다. 일반적인 패턴(예: $ 기호가 포함된 가격)을 추출하기 위해 가벼운 HTML 파서 (parser)를 폴백 (fallback) 용도로 사용합니다. 만약 실패하면, 그때 LLM을 호출합니다. 이렇게 하면 표준 형식을 가진 80%의 페이지에 대해 비용을 절감하고 속도를 높일 수 있습니다.

또한, 작은 검증 단계를 구축할 것입니다. 추출 후에 가격이 타당해 보이는지(양수인지, 예상 범위 내에 있는지) 확인하고, 그렇지 않다면 다른 프롬프트로 재시도합니다.

마치며

LLM은 단순히 채팅이나 콘텐츠 생성만을 위한 것이 아닙니다. 당신이 원하는 것을 정확하게 정의하기만 한다면, 지저분하고 비정형화된 데이터를 깔끔한 JSON으로 변환하는 데 놀라울 정도로 뛰어납니다. HTML 변경 사항을 쫓아다니는 데 지쳤다면, 이 기술을 시도해 보세요. 적은 수의 페이지로 시작하여 정확도를 측정하고, 트레이드오프 (trade-offs)가 본인에게 맞는지 확인해 보시기 바랍니다.

길들여지지 않은 거친 웹 페이지에서 구조화된 데이터를 추출하는 당신만의 접근 방식은 무엇인가요? 다른 분들은 이 문제를 어떻게 다루는지 정말 듣고 싶습니다.

AI 자동 생성 콘텐츠

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

원문 바로가기
0

댓글

0