크롤링 모니터링 자동화 — 데이터 품질을 24시간 지키는 법
크롤러를 만드는 건 프로젝트의 20%입니다. 나머지 80%는 운영입니다.
"잘 돌아가던 크롤러가 어느 날 갑자기 빈 데이터를 뱉기 시작했는데, 아무도 몰랐다." — 크롤링 시스템을 운영해 본 사람이라면 한 번쯤 겪어봤을 겁니다. 이 글에서는 크롤러가 조용히 깨지는 패턴들과, 이를 자동으로 감지하고 복구하는 방법을 정리합니다.
목차
크롤러가 조용히 깨지는 5가지 패턴
크롤러의 가장 위험한 실패는 에러 없이 잘못된 데이터를 반환하는 것입니다. HTTP 200이 돌아오지만, 실제 데이터는 비어 있거나 엉뚱한 값이 들어오는 경우죠.
1. HTML 구조 변경
대상 사이트가 리뉴얼하거나 A/B 테스트를 진행하면, CSS 셀렉터가 맞지 않아 데이터 추출이 실패합니다. 에러는 나지 않지만 결과는 None 또는 빈 문자열입니다.
2. 봇 차단 강화
IP 차단, CAPTCHA, Cloudflare 보호 등이 갑자기 적용됩니다. 응답 코드는 200이지만, 반환되는 건 "접근이 제한되었습니다" 같은 차단 페이지입니다.
3. 타임아웃 & 네트워크 오류
대상 서버의 응답이 느려지거나, 특정 시간대에만 간헐적으로 실패합니다. 재시도 로직이 없으면 데이터가 빠집니다.
4. 데이터 스키마 변경
가격 필드가 price에서 salePrice로 바뀌거나, 날짜 포맷이 변경됩니다. 크롤러는 정상 동작하지만, 후속 파이프라인(DB 적재, 분석 등)에서 문제가 발생합니다.
5. 페이지네이션/무한스크롤 변경
"다음 페이지" 버튼이 사라지거나, 무한스크롤 API 엔드포인트가 변경됩니다. 첫 페이지만 수집하고 끝나는 상황이 생깁니다.
이 다섯 가지 패턴의 공통점? 로그만 보면 정상처럼 보인다는 것입니다.
모니터링해야 할 핵심 지표 4가지
크롤러가 "정상인 척" 깨지는 걸 잡으려면, 단순 에러 로그가 아닌 데이터 수준의 모니터링이 필요합니다.
1. 성공률 (Success Rate)
단순 HTTP 200이 아니라, 실제로 유효한 데이터를 반환한 비율을 추적합니다.
# 성공률 모니터링 예시
from datetime import datetime
def monitor_crawl_success(results):
total = len(results)
valid = sum(1 for r in results if r.get("title") and r.get("price"))
success_rate = valid / total * 100 if total > 0 else 0
# 성공률이 임계값 이하면 알림
if success_rate < 90:
send_alert(
level="warning" if success_rate >= 70 else "critical",
message=f"크롤링 성공률 저하: {success_rate:.1f}% ({valid}/{total})",
timestamp=datetime.now().isoformat()
)
return {"success_rate": success_rate, "total": total, "valid": valid}
2. 응답 시간 (Response Time)
평균 응답 시간이 갑자기 2~3배 늘어나면, 차단이 시작되거나 대상 서버에 문제가 있다는 신호입니다.
3. 데이터 완전성 (Completeness)
필수 필드가 모두 채워졌는지 확인합니다. "가격" 필드가 있는 결과의 비율, "이미지 URL"이 있는 비율 등을 추적합니다.
def check_data_completeness(results, required_fields):
"""필수 필드 완전성 체크"""
if not results:
return {field: 0.0 for field in required_fields}
completeness = {}
for field in required_fields:
filled = sum(1 for r in results if r.get(field))
completeness[field] = filled / len(results) * 100
# 특정 필드 완전성이 급격히 떨어지면 스키마 변경 의심
for field, rate in completeness.items():
if rate < 80:
send_alert(
level="warning",
message=f"필드 '{field}' 완전성 {rate:.1f}%로 하락 — 스키마 변경 확인 필요"
)
return completeness
4. 스키마 변경 감지
수집 데이터의 구조를 주기적으로 비교합니다. 새로운 필드가 생기거나, 기존 필드의 값 형식이 바뀌면 알림을 보냅니다.
알림 자동화 설정
모니터링 지표를 감시하고 있어도, 사람이 대시보드를 24시간 보고 있을 순 없습니다. 알림 자동화가 필수입니다.
import requests
import smtplib
from email.mime.text import MIMEText
def send_slack_alert(webhook_url, message, level="warning"):
"""Slack 웹훅으로 알림 전송"""
emoji = "" if level == "warning" else ""
payload = {
"text": f"{emoji} *크롤링 모니터링 알림*\n{message}",
"username": "Crawl Monitor",
}
requests.post(webhook_url, json=payload)
def send_email_alert(to_email, subject, body):
"""이메일 알림 전송"""
msg = MIMEText(body)
msg["Subject"] = f"[크롤링 알림] {subject}"
msg["From"] = "monitor@your-domain.com"
msg["To"] = to_email
with smtplib.SMTP("smtp.gmail.com", 587) as server:
server.starttls()
server.login("your-email", "app-password")
server.send_message(msg)
알림 설정 팁:
- 단계별 알림: 성공률 90% 이하 → Slack 경고, 70% 이하 → 이메일 + PagerDuty
- 플래핑 방지: 3회 연속 실패 시에만 알림 (일시적 오류 무시)
- 알림 피로 관리: 동일 이슈는 1시간에 최대 1회만 알림
- 복구 알림: 문제 해결 시 "정상 복구" 알림도 보내서 안심하게
자동 복구 전략
알림만으로는 부족합니다. 흔한 실패 패턴은 자동으로 복구할 수 있습니다.
1. 지수 백오프 재시도 (Exponential Backoff)
import time
import random
def crawl_with_retry(url, max_retries=3):
"""지수 백오프 재시도 — 일시적 오류 자동 복구"""
for attempt in range(max_retries):
try:
result = crawl_page(url)
if result and result.get("data"):
return result
except Exception:
pass
# 재시도 간격: 1초 → 2초 → 4초 (+ 랜덤 지터)
wait = (2 ** attempt) + random.uniform(0, 1)
time.sleep(wait)
return None # 재시도 모두 실패 → 알림으로 넘김
2. 프록시 로테이션
IP 차단 감지 시 자동으로 다른 프록시로 전환합니다.
def crawl_with_proxy_rotation(url, proxies):
"""프록시 로테이션 — IP 차단 시 자동 전환"""
for proxy in proxies:
try:
response = requests.get(url, proxies={"http": proxy, "https": proxy}, timeout=10)
if response.status_code == 200 and not is_block_page(response.text):
return response
except requests.RequestException:
continue
send_alert(level="critical", message=f"모든 프록시에서 {url} 차단됨")
return None
3. Fallback 전략
주요 크롤링 방식이 실패하면, 대안 경로로 전환합니다.
- CSS 셀렉터 실패 → XPath로 시도
- API 엔드포인트 변경 → 모바일 버전 페이지로 전환
- 특정 IP 대역 차단 → 다른 리전의 프록시 사용
현실은? 이 모든 걸 직접 구축하고 유지하는 데 상당한 엔지니어링 리소스가 들어갑니다.
직접 운영 vs 관리형 서비스 — 비용 비교
크롤링 모니터링/운영 시스템을 직접 구축하는 비용을 솔직하게 따져보겠습니다.
| 항목 | 직접 운영 | 관리형 서비스 (해시스크래퍼) |
|---|---|---|
| 초기 구축 | 2~4주 개발 시간 | 설정 후 바로 시작 |
| 프록시 비용 | 월 $100~500+ | 포함 |
| 모니터링 | 직접 구축 필요 | 내장 |
| 장애 대응 | 개발자가 직접 | 자동 복구 + 전담 대응 |
| 사이트 변경 대응 | 수동 업데이트 | 자동 감지 + 수정 |
| 인건비 | 엔지니어 시간 (가장 큰 비용) | 서비스 비용에 포함 |
직접 운영할 때 가장 큰 비용은 보이지 않는 비용입니다. 새벽에 크롤러가 깨져서 대응하는 시간, 사이트 구조 변경을 파악하고 셀렉터를 수정하는 시간, 프록시를 관리하는 시간. 이 시간들이 쌓이면 제품 개발에 쏟아야 할 리소스가 빠져나갑니다.
해시스크래퍼는 이런 운영 부담을 대신 집니다. 모니터링, 장애 대응, 사이트 변경 추적이 서비스에 내장되어 있어서, 개발팀은 크롤링 데이터를 활용하는 것에만 집중할 수 있습니다.
마무리
크롤링 시스템은 만드는 순간이 아니라 운영하는 매일매일이 진짜 전쟁입니다.
모니터링 없는 크롤러는 시한폭탄입니다. 지금 잘 돌아간다고 해서 내일도 잘 돌아간다는 보장이 없습니다. 대상 사이트는 매일 변하고, 봇 차단은 점점 정교해집니다.
직접 모니터링 시스템을 구축하든, 관리형 서비스를 활용하든, 핵심은 하나입니다: 크롤러가 깨졌을 때 즉시 알 수 있는 구조를 만드세요.
크롤링 운영에 지치셨다면, 해시스크래퍼에 문의해 보세요. 모니터링과 유지보수가 내장된 관리형 크롤링 서비스로, 데이터 품질을 24시간 지켜드립니다.




