본문으로 건너뛰기

© 2026 Molayo

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

웹 스크래핑을 위해 정규표현식(Regex) 작성을 중단했습니다 — 대신 제가 사용하는 방법은 다음과 같습니다

요약

기존의 정규표현식과 CSS 선택자를 이용한 웹 스크래핑 방식의 유지보수 어려움을 LLM 기반 데이터 추출 방식으로 해결하는 방법을 소개합니다. HTML 스니펫을 프롬프트와 함께 LLM에 전달하여 JSON 형식으로 데이터를 추출하는 실무적인 접근법과 트레이드오프를 다룹니다.

핵심 포인트

  • 기존 선택자 방식은 웹사이트 디자인 변경 시 유지보수가 매우 어려움
  • LLM을 활용하면 자연어로 데이터 추출 규칙을 정의할 수 있음
  • OpenAI API 등을 활용해 HTML에서 직접 JSON 데이터를 추출 가능
  • 비용과 지연 시간(Latency)은 대규모 스크래핑 시 고려해야 할 핵심 요소

저는 수년 동안 웹 스크래핑을 해왔습니다. 항상 똑같은 사이클이 반복되었습니다: 사이트를 찾고, 몇 개의 CSS 선택자(CSS selectors)를 작성하고, 데이터를 가져온 뒤, 2주 후에 사이트 디자인이 바뀌면 제 스크래퍼는 작동을 멈춥니다. 예전에는 정규표현식(Regex) 패턴과 XPath 표현식을 수정하는 데 몇 시간씩을 소비하곤 했습니다. 마치 웹 자체와 싸우고 있는 기분이었습니다.

그러다 문득 궁금해졌습니다. 만약 내가 원하는 것을 그냥 평이한 영어(Plain English)로 컴퓨터에게 말한다면 어떻게 될까? 그때부터 데이터 추출(Data extraction)을 위해 LLM(대규모 언어 모델)을 실험하기 시작했습니다.

문제점: 취약한 선택자 (Fragile selectors)

지난달에 저는 수십 개의 서로 다른 이커머스 사이트에서 제품 정보를 가져와야 했습니다. 각 사이트마다 HTML 구조가 달랐습니다. 어떤 곳은 <div class="product-name">를 사용했고, 다른 곳은 <h2 itemprop="name">를 사용했으며, 세 번째 곳은 동적 클래스 이름을 가진 <span> 안에 이름이 숨겨져 있었습니다. 저의 BeautifulSoup 스크립트는 조건부 로직의 미로처럼 보였습니다:

import requests
from bs4 import BeautifulSoup

...

이 방식은 한동안 작동했지만, 유지보수가 악몽 같았습니다. 사이트가 업데이트될 때마다 폴백 체인(Fallback chain)을 다시 작성해야 했습니다. 저는 다른 접근 방식이 필요했습니다.

제가 시도했던 것들 (그리고 싫어했던 것들)

처음에는 미들웨어(Middlewares)를 포함한 Scrapy와 같은 더 정교한 스크래핑 프레임워크를 사용해 보았습니다. 하지만 여전히 똑같은 선택자 문제였습니다. 그다음에는 Octoparse와 같은 시각적 스크래핑 도구를 살펴보았지만, 이들은 GUI가 필요했고 코드상에서 확장성(Scale)이 좋지 않았습니다. 심지어 제품 필드를 인식하도록 작은 ML(머신러닝) 모델을 학습시키려고 시도하기도 했지만, 이는 과잉 대응(Overkill)이었고 레이블이 지정된 데이터(Labeled data)가 필요했습니다.

포기하려던 참에 한 친구가 말했습니다. "그냥 원본 HTML을 GPT에 넣고 필요한 것을 추출해달라고 하면 안 돼?" 저의 첫 반응은 이랬습니다. "그건 미친 짓이야 — 너무 느리고 비용이 많이 들어." 하지만 저는 한번 시도해 보기로 했습니다.

실제로 효과가 있었던 것: LLM 기반 추출 (LLM-powered extraction)

아이디어는 간단합니다. 깨지기 쉬운 선택자를 작성하는 대신, 작은 HTML 스니펫(또는 페이지 전체)을 원하는 데이터를 JSON 형식으로 설명하는 프롬프트(Prompt)와 함께 LLM에 보내는 것입니다. 그러면 LLM이 패턴을 파악합니다.

다음은 OpenAI API를 사용한 최소한의 예시입니다:

import openai
from bs4 import BeautifulSoup
import json
...

이 코드는 놀라울 정도로 잘 작동합니다. 저는 10개의 서로 다른 제품 페이지에서 테스트를 진행했으며, 약 80%의 확률로 필드들을 정확하게 추출해냈습니다. 실패 사례는 주로 페이지가 너무 길어 잘리거나(truncated), 필드 이름이 모호한 경우(예: "price"가 할인 가격인지 원래 가격인지 불분명한 경우)에 발생했습니다.

트레이드오프(Trade-offs) 다루기

LLM 추출이 만능 해결책(Silver bullet)은 아닙니다. 제가 겪었던 문제들은 다음과 같습니다:

  • 비용 (Cost): gpt-3.5-turbo 기준으로 요청당 약 $0.002일 때, 10,000개의 페이지를 스크래핑하면 $20가 소요됩니다. 일회성 작업에는 괜찮지만, 수백만 개의 페이지를 지속적으로 스크래핑하는 데는 적합하지 않습니다.
  • 지연 시간 (Latency): 각 요청은 1~3초가 소요됩니다. 대량의 스크래핑을 수행하려면 배치(Batching) 처리나 비동기(Async) 호출이 필요할 것입니다.
  • 환각 (Hallucinations): LLM이 데이터를 지어낼 수 있습니다 (예: 가격이 존재하지 않는데 가격을 추측함). 항상 출력값을 검증해야 합니다.
  • 개인정보 보호 (Privacy): 페이지 전체 콘텐츠를 제3자 API로 전송하는 것은 서비스 약관이나 데이터 보호법을 위반할 수 있습니다. 민감한 데이터의 경우 로컬 모델(Local model)을 사용하는 것이 좋습니다.

저는 자체 호스팅(Self-hosted) 가능한 대안을 찾기 시작했습니다. 그러던 중 구조화된 추출(Structured extraction)에 초점을 맞춰 LLM을 래핑(Wrap)한 서비스들을 발견했습니다. 그중 하나인 Interwest Info는 유사한 API를 제공하면서도 내장된 검증(Validation) 및 재시도(Retries) 기능을 갖추고 있습니다. 사이드 프로젝트에 사용해 보았는데 추출을 안정적으로 처리했습니다. 하지만 접근 방식은 동일합니다. 원하는 것을 설명하고, JSON을 돌려받는 것입니다.

배운 점들 (Lessons learned)

  • 단순하게 시작하세요. 추출 로직을 작성하기 전에, 페이지 텍스트를 LLM에 먼저 보내보세요. 생각보다 훨씬 잘 해내는 모습에 놀랄 수도 있습니다.
  • 까다로운 부분에만 사용하세요. 저는 이제 안정적인 필드(URL이나 ID 등)에는 전통적인 셀렉터(Selectors)를 사용하고, 지저분한 텍스트 필드에 대해서는 LLM을 보조적으로 사용하는 방식을 결합합니다.
  • 공격적으로 캐싱(Cache)하세요. 불필요한 API 호출을 피하기 위해 변경되지 않은 페이지의 결과는 저장해 두세요.
  • 예산을 설정하세요. 비용이 낮더라도 통제되지 않는 요청은 비용이 쌓일 수 있습니다. 지출 한도를 설정하십시오.

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

다음에 수많은 다양한 사이트를 스크래핑해야 한다면, 저는 다음과 같은 간단한 파이프라인 (pipeline)을 구축할 것입니다. 먼저 캐시된 응답 (cached response)을 시도하고, 그다음 LLM 추출 엔드포인트 (LLM extraction endpoint) (OpenAI 또는 Interwest Info와 같은 호스팅 서비스 여부와 상관없이)를 사용하며, 마지막으로 예외적인 케이스 (edge cases)에 대해서는 수동 검토 (manual review)로 전환하는 방식입니다. 또한, 잘림 문제 (truncation issues)를 방지하기 위해 큰 페이지들을 미리 청크 (pre-chunk)로 나눌 것입니다.

마무리

정규표현식 (Regex)과 CSS 선택자 (CSS selectors)는 여전히 제 역할이 있습니다. 그것들은 빠르고, 예측 가능하며, 비용이 들지 않습니다. 하지만 이질적인 (heterogeneous) 웹 데이터를 다룰 때는, 컴퓨터에게 원하는 것을 영어로 말하는 것이 놀라울 정도로 효과적입니다. 완벽하지는 않지만, 변화하는 HTML 구조 때문에 정신을 잃는 상황으로부터 저를 구해 주었습니다.

여러분의 다음 스크래핑 프로젝트에서 한 번 시도해 보세요. 작은 샘플로 시작하여 여러분에게 잘 맞는지 확인해 보시기 바랍니다.

엉망인 웹 데이터를 다루기 위한 여러분만의 비밀 병기는 무엇인가요?

AI 자동 생성 콘텐츠

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

원문 바로가기
0

댓글

0