본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 05. 28. 10:07

내가 웹 스크래핑(Web Scraping)을 위해 CSS 선택자(CSS Selectors)를 작성하는 것을 그만둔 이유

요약

기존의 CSS 선택자나 XPath를 이용한 웹 스크래핑 방식의 취약성을 지적하며, LLM을 활용해 가공되지 않은 HTML에서 직접 구조화된 데이터를 추출하는 새로운 접근법을 제안합니다.

핵심 포인트

  • CSS 선택자 방식은 웹사이트 레이아웃 변경에 매우 취약함
  • LLM은 HTML의 구문이 아닌 의미론적 구조를 이해함
  • LLM을 활용하면 사이트별 맞춤형 파서를 만들 필요가 없음
  • Raw HTML과 명확한 스키마만으로 데이터 추출 가능

저는 수년 동안 웹사이트를 스크래핑해 왔습니다. 주로 이커머스(e-commerce) 상품 데이터, 채용 공고 같은 것들이었죠. 그리고 오랫동안 저의 작업 흐름은 다음과 같았습니다:

  1. Chrome DevTools에서 페이지를 엽니다.
  2. 가격, 제목, 설명을 안정적으로 가져올 수 있는 고유한 CSS 선택자(CSS selector)나 XPath를 찾습니다.
  3. BeautifulSoup 또는 Selenium 스크립트를 작성합니다.
  4. 한 번 실행합니다. 아주 잘 작동합니다.
  5. 일주일 후에 다시 실행합니다. 사이트가 상품 카드 디자인을 변경했기 때문에 모든 것이 망가져 있습니다.

저는 HTML 구조를 가지고 두더지 잡기 게임을 하는 것에 지쳤습니다. 그래서 다른 접근 방식을 실험하기 시작했습니다. 바로 대규모 언어 모델(LLMs)을 사용하여 가공되지 않은 HTML(raw HTML)에서 구조화된 데이터(structured data)를 직접 추출하는 방식입니다.

한계점 (The Breaking Point)

작년에 가격 비교 도구를 만들기 위해 50개 이상의 서로 다른 이커머스 사이트에서 상품 데이터를 스크래핑해야 했습니다. 각 사이트는 저마다의 HTML 레이아웃을 가지고 있었습니다. 어떤 곳은 자바스크립트 렌더링(JavaScript rendering)을 사용했고, 어떤 곳은 안티 봇(anti-bot) 조치를 취하고 있었습니다. 저는 각 사이트를 위한 맞춤형 선택자를 작성하는 데 2주를 보냈고, 사이트들이 업데이트될 때마다 이를 수정하는 데 또 다른 2주를 보냈습니다.

Selenium을 이용한 헤드리스 브라우저(headless browsers)도 시도해 보았습니다. 그것은 작동했지만, 느리고 여전히 취약했습니다. 클래스 이름(class name) 하나만 바뀌어도 제 스크립트는 가격에 대해 None을 반환했습니다. 가공되지 않은 HTML에 정규 표현식(regex)을 적용해 보기도 했습니다. 그것은 악몽이었습니다.

제게 필요했던 것은 단순히 구문(syntax)이 아니라 페이지의 의미론(semantics)을 이해하는 무언가였습니다.

깨달음의 순간: 범용 파서(Universal Parser)로서의 LLMs

저는 텍스트 생성을 위해 GPT-3.5를 사용해 보고 있었는데, 문득 이런 생각이 들었습니다. '만약 가공되지 않은 HTML을 입력으로 주고 특정 필드를 추출해 달라고 요청하면 어떻게 될까?' LLM은 클래스 이름에 신경 쓰지 않습니다. 대신 "Price:" 옆에 있는 "$19.99"가 가격이라는 것을 이해합니다.

저는 지저분한 상품 페이지에서 이를 테스트했습니다. 다음과 같은 프롬프트(prompt)와 함께 HTML을 문자열로 보냈습니다:

이 HTML에서 다음 필드를 추출하세요:
- product_name
- price (숫자로)
...

그것은 작동했습니다. 완벽하지는 않았지만, 놀라울 정도로 잘 작동했습니다. 핵심적인 통찰은 이것입니다: LLM (Large Language Models)은 수십억 개의 웹 페이지를 통해 학습되었습니다. 그들은 가능한 모든 HTML 구조를 이미 보았습니다. 그들에게는 선택자 (Selector)가 필요하지 않습니다. 그저 가공되지 않은 마크업 (Raw markup)과 명확한 스키마 (Schema)만 있으면 됩니다.

코드 (The Code)

이 작업을 수행하기 위해 제가 작성한 Python 함수입니다. OpenAI의 API를 사용하지만, 어떤 LLM 제공업체로도 교체할 수 있습니다.

import os
import json
from openai import OpenAI
...
```

json" in result_text:
        result_text = result_text.split("

```json")[1].split("```

")[0].strip()
    elif "

```" in result_text:
        result_text = result_text.split("

```")[1].split("

```")[0].strip()
    return json.loads(result_text)

# 사용 예시 (Example usage)
...

이것은 단순화된 버전입니다. 실제 운영 환경 (Production)에서는 재시도 (Retries), 검증 (Validation), 그리고 비용 제한 (Cost limits)을 처리해야 합니다.

배운 점 (What I Learned)

장점 (Pros):

  • 회복탄력성 (Resilience): 동일한 프롬프트 (Prompt)가 수정 없이도 서로 다른 사이트에서 작동합니다. 클래스 이름 (Class name)이 바뀌었다고요? 상관없습니다. LLM은 여전히 가격을 찾아냅니다.
  • 개발 속도 (Speed of development): 몇 시간이 걸리던 새로운 사이트 추가 작업을 몇 분 만에 끝낼 수 있습니다.
  • JavaScript 처리: 렌더링된 HTML (Playwright 또는 유사한 도구 사용)을 입력하면 동일하게 잘 작동합니다.

단점 (Cons):

  • 비용 (Cost): API 호출마다 비용이 발생합니다. 수백 페이지 정도는 괜찮지만, 수백만 페이지를 처리하려면 최적화 (캐싱 (Caching), 더 저렴한 모델, 배치 처리 (Batching))가 필요합니다.
  • 지연 시간 (Latency): LLM 호출에는 1~3초가 소요됩니다. 실시간 스크래핑 (Real-time scraping)에는 적합하지 않습니다.
  • 정확도 (Accuracy): 100%는 아닙니다. 때때로 가격을 환각 (Hallucinate)하거나 필드를 놓치기도 합니다. 검증 레이어 (Validation layer)가 필요합니다.
  • 토큰 제한 (Token limits): HTML은 장황합니다 (Verbose). 페이지를 청크 (Chunk)로 나누거나 더 큰 컨텍스트 (Context)를 가진 모델을 사용해야 할 수도 있습니다.

이 방식을 사용하지 말아야 할 때 (When NOT to Use This)

  • 구조가 잘 잡힌 단일 사이트를 스크래핑하는 경우라면, 그냥 선택자 (Selector)를 작성하세요. 그것이 더 빠르고 무료입니다.
  • 실시간 결과가 필요한 경우 (예: 실시간 가격 업데이트), LLM은 너무 느립니다.
  • 데이터가 민감한 경우, 이를 제3자 API로 전송하는 것은 허용되지 않을 수 있습니다.

하지만 지저분하고 이질적인 (Heterogeneous) 사이트들을 상대해야 한다면? 이 접근 방식은 구원 투수와 같습니다.

다음에 내가 다르게 시도할 것들

나는 하이브리드 접근 방식 (Hybrid approach)으로 시작할 것입니다. 먼저 간단한 CSS 선택자 (CSS Selector)를 시도해 보고, 실패할 경우 LLM으로 전환하는 방식입니다. 그렇게 하면 쉬운 페이지에서는 선택자의 속도를 얻을 수 있고, 어려운 페이지에서는 AI의 회복 탄력성 (Resilience)을 얻을 수 있습니다.

또한, API 비용을 피하고 데이터 프라이버시를 유지하기 위해 Ollama를 통해 Llama 3와 같은 로컬 모델 (Local model)을 사용할 것입니다. 추출 작업에 있어서 그 품질은 GPT-3.5와 유사합니다.

이 아이디어에 영감을 준 도구

이것을 만드는 동안, 관리형 API (Managed API)로서 정확히 이러한 방식의 LLM 기반 추출을 수행하는 Interwest AI (https://ai.interwestinfo.com/)라는 서비스를 우연히 발견했습니다. 실제 운영 환경 (Production)에서 사용해 보지는 않았지만, 다른 사람들도 저와 같은 방식으로 생각하고 있다는 것을 확인했습니다.

여러분의 의견은 어떠신가요?

저는 여전히 이 접근 방식을 다듬고 있습니다. 여러분은 매주 HTML이 바뀌는 사이트의 스크래핑을 어떻게 처리하시나요? 추출을 위해 LLM을 사용해 보셨나요, 아니면 전통적인 선택자 (Selectors)를 고수하시나요? 여러분에게 효과적이었던 방법이 무엇인지 듣고 싶습니다.

이 기사는 저의 개인적인 경험을 바탕으로 작성되었습니다. 언급된 도구는 해당 접근 방식의 한 가지 예시일 뿐이며, 보증을 의미하지 않습니다.

AI 자동 생성 콘텐츠

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

원문 바로가기
0

댓글

0