본문으로 건너뛰기

© 2026 Molayo

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

HTML 파싱이 실패할 때: LLM을 사용하여 지저분한 웹 데이터 추출하기

요약

전통적인 HTML 파싱 방식(BeautifulSoup, Scrapy 등)이 구조 변화에 취약할 때, LLM을 활용하여 비정형 웹 데이터에서 필요한 정보를 추출하는 효과적인 방법을 소개합니다.

핵심 포인트

  • CSS 셀렉터와 정규 표현식의 구조 의존성 문제 해결
  • 데이터의 위치가 아닌 데이터의 형태(Schema)를 정의하여 추출
  • GPT-4o 등 긴 컨텍스트를 지원하는 LLM 활용 권장
  • 예측 불가능한 HTML 구조에 대한 강력한 대응책

저는 수년 동안 웹사이트를 스크레이핑(Scraping)해 왔습니다. BeautifulSoup, Scrapy, Playwright — 이 모든 것들을 사용해 봤죠. 하지만 지난달에 벽에 부딪혔습니다.

A 클라이언트가 수십 개의 이커머스(e-commerce) 사이트에서 제품 상세 정보를 추출해 달라고 요청했습니다. 대부분은 간단했습니다. 적절한 CSS 셀렉터(CSS selectors)를 찾고, 페이지네이션(pagination)을 처리하면 끝이었죠. 하지만 특정 사이트 하나가 악몽 같았습니다. HTML은 중첩된 div, 인라인 스타일(inline styles), 그리고 속성(attributes), 텍스트 노드(text nodes), 심지어 JavaScript 변수에 흩어져 있는 데이터들로 엉망진창이었습니다. 레이아웃은 매주 바뀌었습니다. 제가 정성껏 만든 셀렉터들은 끊임없이 깨졌습니다.

저는 이틀 동안 수정과 리팩토링(refactoring)에 매달렸습니다. 다 해결했다고 생각할 때마다 사이트가 업데이트되었고 제 파이프라인(pipeline)은 다시 망가졌습니다. 클라이언트에게 불가능하다고 말하려던 참이었습니다.

그때 한 동료가 말했습니다: “그냥 가공되지 않은 HTML을 LLM에 전달하고 필요한 것을 추출해 달라고 요청하면 어때요?”

처음에는 웃음이 나왔습니다. LLM은 환각(hallucinate)을 일으키고, 느리고, 비용이 많이 들지 않나요? 하지만 저는 절박했습니다. 그래서 프로토타입(prototype)을 만들어 보기로 했습니다.

시도했지만 실패했던 것들

AI 경로를 택하기 전에, 저는 전통적인 접근 방식들을 모두 소진했습니다:

  • 정규 표현식(Regex) 및 문자열 파싱(string parsing): 데이터에 일관된 패턴이 없었습니다. 가격은 통화 기호가 있거나 없는 상태로 나타났고, 때로는 data-price 속성에, 때로는 중첩된 <span> 태그 안에 있었습니다.
  • CSS 셀렉터를 사용한 BeautifulSoup: 지독하게 취약했습니다. 한 사이트가 클래스 이름을 product-price에서 price-info로 바꾸자 제 스크립트 전체가 죽어버렸습니다.
  • 동적 콘텐츠를 위한 Playwright: JavaScript로 렌더링되는 페이지에는 도움이 되었지만, 파싱 로직은 여전히 취약했습니다. 여전히 깨지기 쉬운 XPath나 CSS 셀렉터를 작성해야 했습니다.
  • Item loaders를 사용한 Scrapy: 이 사이트 하나에는 과했습니다(overkill). 그리고 여전히 정확한 HTML 구조를 알고 있어야 했습니다.

근본적인 문제: HTML 구조가 예측 불가능했다는 점입니다. 사람은 페이지를 보고 “저게 가격이네”라고 말할 수 있습니다. 하지만 CSS 셀렉터는 구조에 의존하기 때문에 그렇게 할 수 없습니다.

결국 성공한 방법: LLM 기반 추출

저는 가공되지 않은 HTML을 가져와서 LLM (저는 OpenAI의 GPT-4o를 사용했지만, 긴 컨텍스트 (long contexts)를 처리할 수 있는 어떤 모델이든 사용할 수 있습니다)에 보내고, 제가 정의한 스키마 (schema)에 따라 JSON 객체를 반환하도록 요청하는 작은 스크립트를 만들었습니다.

핵심적인 통찰: 컴퓨터에게 데이터가 어디에 있는지 가르치는 대신, 데이터가 어떻게 생겼는지를 가르치는 것입니다. 저는 설명을 제공하고 LLM이 매핑 (mapping)을 파악하도록 합니다.

다음은 단순화된 버전입니다:

import openai
from bs4 import BeautifulSoup

...

이것이 전부입니다. 문제가 되었던 사이트의 경우, 이 방법은 단 한 번의 시도만에 성공했습니다. 셀렉터 (selectors), XPath, 정규 표현식 (regular expressions)도 필요 없었습니다. 저는 단지 제가 원하는 것을 설명했을 뿐이고, LLM이 나머지를 해결했습니다.

트레이드오프 (그리고 매우 많습니다)

저는 이 접근 방식을 몇 주 동안 사용해 왔으며, 여기서 배운 점은 다음과 같습니다:

장점 (Pros)

  • 회복 탄력성 (Resilience): 사이트의 레이아웃이 변경되어도 LLM은 구조가 아닌 의미론 (semantics)을 이해하기 때문에 여전히 정확하게 추출하는 경우가 많습니다.
  • 빠른 프로토타이핑 (Rapid prototyping): 새로운 사이트에 대한 추출 설정을 몇 시간이 아닌 몇 분 만에 완료할 수 있습니다.
  • 지저분한 HTML 처리: LLM은 제가 예상했던 것보다 노이즈 (noise)를 더 잘 무시합니다. 마치 인간 큐레이터가 있는 것과 비슷합니다.

단점 (Cons)

  • 비용 (Cost): LLM API 호출에는 비용이 듭니다. 대량의 스크래핑 (scraping)의 경우, 이 비용은 빠르게 누적됩니다. GPT-4o를 기준으로 페이지당 $0.01–$0.03 정도라고 추정합니다. GPT-4o-mini와 같은 더 저렴한 모델은 비용을 줄일 수 있지만 정확도가 떨어질 수 있습니다.
  • 지연 시간 (Latency): 각 추출에는 1~3초가 소요됩니다. 수백 페이지의 경우, 정규 표현식 (regex)에 비해 느립니다.
  • 환각 (Hallucination): 때때로 LLM은 HTML에서 데이터를 찾을 수 없으면 데이터를 지어내기도 합니다. 출력값을 주의 깊게 검증해야 합니다.
  • 컨텍스트 길이 (Context length): HTML이 매우 큰 경우, 이를 자르거나 청크 (chunk)로 나누어야 하며, 이 과정에서 데이터가 손실될 수 있습니다.
  • 제3자 API 의존성 (Dependency on third-party API): OpenAI가 다운되면 스크래퍼 (scraper)도 멈춥니다. 폴백 (fallback) 모델이나 로컬 LLM (예: Ollama를 통한 Llama 3)을 고려하십시오.

이 접근 방식을 사용하지 말아야 할 때

  • 수백만 개의 페이지를 저렴하게 추출해야 하는 경우 – 전통적인 파싱 (Traditional parsing) 방식을 고수하십시오.
  • 데이터 형식이 엄격하고 안정적인 경우 – CSS 선택자 (CSS selectors)가 더 빠르고 비용이 들지 않습니다.
  • HTML을 외부 API로 보낼 수 없는 경우 (예: 민감한 데이터) – 이럴 때는 로컬 LLM을 사용하거나 AI 사용을 아예 피하십시오.

다음에 제가 다르게 시도할 점

저는 두 가지 접근 방식을 결합하겠습니다. 안정적인 사이트에는 전통적인 파서 (Parsers)를 사용하고, 까다로운 사이트에 대해서만 LLM으로 전환(fallback)하겠습니다. 또한, 검증 (Validation) 단계를 구현하겠습니다. 추출된 가격이 가격 형태인지, 평점이 범위 내에 있는지 등을 확인하는 식입니다. 만약 검증에 실패하면, 다른 프롬프트나 더 강력한 모델을 사용하여 다시 실행하겠습니다.

또 다른 개선 사항은, 모호한 필드에 대한 정확도를 높이기 위해 LLM에 몇 가지 예시를 제공하는 것 (Few-shot prompting)입니다.

도움이 된 도구들

저만 이런 일을 하는 것은 아닙니다. 이제 이 아이디어를 멋진 API로 구현해 놓은 서비스들이 있습니다. 예를 들어, 유사한 추출 API를 제공하는 InterWest AI를 발견했습니다. 광범위하게 사용해 보지는 않았지만, 이러한 패턴이 제품화되는 것을 보는 것은 흥미롭습니다.

마치며

LLM 기반 추출이 만능 해결책 (Silver bullet)은 아닙니다. 비용이 많이 들고 느립니다. 하지만 레이아웃 변경, 일관성 없는 HTML, 혹은 단순히 귀찮음 등으로 인해 전통적인 파싱이 실패하는 10%의 사례에서는 구원 투수가 됩니다.

저는 여전히 마음이 복잡합니다. 한편으로는 예전에 우아한 코드가 필요했던 문제에 AI를 던져 넣음으로써 속임수를 쓰고 있다는 기분이 듭니다. 하지만 다시 생각해보면, 사이트 레이아웃은 매주 바뀌고, 저는 선택자 (Selectors)를 업데이트하는 것보다 더 중요한 일을 해야 합니다.

지저분한 웹사이트에서 데이터를 추출할 때 여러분의 경험은 어떠신가요? AI 기반 파싱을 시도해 보셨나요, 아니면 여전히 XPath와 CSS의 정밀함을 선호하시나요? 다른 분들은 이 문제를 어떻게 다루는지 정말 궁금합니다.

AI 자동 생성 콘텐츠

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

원문 바로가기
0

댓글

0