빠른 답변
스크래퍼는 403, 429 및 CAPTCHA 응답을 어떻게 처리해야 하나요?
이들을 장애물이 아니라 신호로 처리하세요. 429 응답이 오면 Retry-After 만큼 기다리고 해당 호스트에서 요청 속도를 낮추세요. 403 응답이 오면 잠시 멈춘 뒤 한 번 재시도하고, 이후에는 중단 후 사이트 규칙을 확인하세요. CAPTCHA가 나오면 해당 호스트를 중단하세요: 자동으로 해결하지 마십시오. CrawlRelay 레지덴셜 프록시는 US$0.50/GB에서 제공하며, 부하 분산과 위치 설정용이지 사이트 결정 우회용이 아닙니다. KYC 없이 사용 가능하며 크립토로 결제하세요.
세 가지 신호 한눈에 보기
| 429 너무 많은 요청 | 당신이 너무 빠릅니다. Retry-After를 존중하고, 물러서며, 호스트별 동시성 낮추기 |
|---|---|
| 403 금지됨 | 이 요청은 거부되었습니다. 잠시 멈춘 후 한 번 재시도하고, 이후 중단하고 검토하세요 |
| CAPTCHA 또는 챌린지 페이지 | 사이트가 당신이 인간인지 확인하려고 합니다. 호스트를 중단하고 속도를 늦추세요 |
| 크롤링 규칙의 표준 | robots.txt, RFC 9309; Crawl-delay는 흔히 쓰이는 확장 |
| 지원하지 않는 항목 | CAPTCHA 해결, 스텔스 플러그인, 지문 위조 |
각 신호가 내 크롤러에게 무엇을 말하나요?
- 429는 가장 공손한 신호로, 콘텐츠가 있지만 당신의 속도로는 불가능하다는 뜻입니다. RFC 6585에서 정의하며, 흔히
Retry-After헤더에 언제 재시도할지 명시합니다. - 403은 이 요청에 대한 거부입니다. 원인에 따라 해결 가능(잘못된 국가, 누락된 헤더, 과도한 속도)하거나 최종 결정일 수 있습니다(사이트가 해당 콘텐츠의 자동 접근을 금지).
- CAPTCHA 또는 챌린지 페이지는 보통 403 또는 503 상태와 함께 제공되고, 사이트의 봇 관리가 트래픽을 의심한다는 뜻입니다. 이는 무시된 429 후 다음 단계입니다.
세 가지 신호는 모두 증가 신호입니다. 429를 무시하는 사이트는 403, 챌린지, IP 범위 전체 차단 단계로 escalattion하며, 이는 모든 이용자에게 피해를 줍니다.
각 신호에 대해 내 크롤러는 무엇을 해야 하나요?
| 신호 | 첫 반응 | 지속 시 | 중단 조건 |
|---|---|---|---|
| 429 | Retry-After를 기다리거나 지터를 포함한 지수 백오프 적용 | 해당 호스트의 동시성 절반으로 감소 | Retry-After가 커지거나 429가 한 번 요청으로 지속 |
| 403 | 한 번 재시도 후 중단, 동일 국가 | 같은 국가 브라우저와 비교, robots.txt 및 약관 확인 | 동일 URL 패턴에 두 번째 403 |
| CAPTCHA / 챌린지 | 호스트를 몇 분간 일시 중단 | 훨씬 낮은 속도로 재개 | 챌린지가 낮은 속도에서도 나타날 때: 자동 접근 불가 |
| 503과 Retry-After | 요청된 만큼 대기 | 호스트 일시 중단 | 대부분 요청이 몇 분 이상 실패 |
올바르게 반응하는 크롤러를 어떻게 구현하나요?
규칙을 각 스파이더에 넣지 말고, fetcher에 넣으세요. 이 fetcher는 호스트별로 한 번에 하나씩 요청을 보내고 robots.txt와 Crawl-delay 을 준수하며, Retry-After 만큼 기다리고 같은 호스트에서 3회 연속 거부 시 요청을 중지합니다. RFC 9309에 따라 4xx는 허용으로, 5xx는 완전 거부로 처리합니다.
import time
import urllib.robotparser
from urllib.parse import urlsplit
import requests
PROXY = "http://USERNAME-country-us:[email protected]:7777"
USER_AGENT = "ExampleCrawler/1.0 (+https://example.com/crawler-info)"
class PoliteFetcher:
"""One request at a time per host; robots.txt, Crawl-delay and Retry-After honoured."""
def __init__(self, min_interval=1.0, max_refusals=3):
self.min_interval, self.max_refusals = min_interval, max_refusals
self.http = requests.Session()
self.http.proxies = {"http": PROXY, "https": PROXY}
self.http.headers["User-Agent"] = USER_AGENT
self.robots, self.next_time, self.refusals = {}, {}, {}
def _robots(self, root):
if root not in self.robots:
rp = urllib.robotparser.RobotFileParser(root + "/robots.txt")
try:
r = self.http.get(root + "/robots.txt", timeout=(10, 30))
if r.status_code >= 500:
rp.parse(["User-agent: *", "Disallow: /"]) # unreachable: assume disallow
else:
rp.parse(r.text.splitlines() if r.status_code == 200 else []) # 4xx: allowed
except requests.RequestException:
rp.parse(["User-agent: *", "Disallow: /"])
self.robots[root] = rp
return self.robots[root]
def get(self, url):
scheme, host = urlsplit(url)[:2]
if self.refusals.get(host, 0) >= self.max_refusals:
return None # this host said no: stop asking
rp = self._robots("%s://%s" % (scheme, host))
if not rp.can_fetch(USER_AGENT, url):
return None
delay = max(self.min_interval, rp.crawl_delay(USER_AGENT) or 0)
wait = self.next_time.get(host, 0) - time.monotonic()
if wait > 0:
time.sleep(wait)
r = self.http.get(url, timeout=(10, 30))
pause = delay
if r.status_code in (429, 503):
retry_after = r.headers.get("Retry-After", "")
pause = max(delay, int(retry_after) if retry_after.isdigit() else 60)
refused = r.status_code in (403, 429) or "captcha" in r.text[:5000].lower()
self.refusals[host] = self.refusals.get(host, 0) + 1 if refused else 0
self.next_time[host] = time.monotonic() + pause
return r
fetcher = PoliteFetcher()
resp = fetcher.get("https://example.com/")
print(resp.status_code if resp is not None else "skipped")403을 합법적으로 해결하는 방법은?
403의 여러 원인은 사용자 측에 있으며, 이를 해결하는 것은 합리적입니다:
- 잘못된 국가. 많은 사이트가 특정 지역만 제공합니다.
-country-<code>4G 모바일 프록시를 사용해 해당 국가를 지정하세요. - 불완전한 헤더.
Accept,Accept-Language및 연락처 URL이 포함된 정직한 유저 에이전트를 보내세요. 브라우저와 유사한 유저 에이전트를 순수 HTTP 클라이언트에 적용하면 일관성이 없고 거부될 수 있습니다. - 당신의 속도 문제. N번 요청 후 발생하는 403은 속도 문제입니다; 속도를 늦추세요.
고쳐지지 않는 것은 많은 사람처럼 보이려고 유저 에이전트를 계속 바꾸거나, 브라우저 지문 위조, 또는 사이트가 의도적으로 차단한 IP 회전을 시도하는 것입니다. 사이트가 호스팅 범위를 필터링해 자동화 트래픽을 차단한다면, 레지덴셜 프록시 IP라고 해서 접근 허용되는 것은 아닙니다.
왜 굳이 CAPTCHA를 풀려고 하나요?
CAPTCHA는 사이트가 질문을 던지고 사용자가 거짓으로 답할 가능성이 있는 상황입니다. 자동으로 푸는 것은 예의 바른 크롤러를 접근 통제를 우회하려는 도구로 전환시켜, 법적 위험을 높입니다: 2026년 미국의 검색 스크래핑 관련 소송들인 Google 대 SerpApi, Reddit 대 SerpApi 등은 DMCA의 반우회 규정에 따라 피고가 봇 방지 시스템을 우회했다는 주장을 중심으로 진행됩니다. 법원은 아직 명확한 판결을 내리지 않았지만, 위험은 실재합니다.
CrawlRelay는 CAPTCHA 풀기나 봇 탐지 회피를 지원하지 않으며, 수용 가능한 이용 정책]에서도 이를 게이트웨이에서 금지하고 있습니다. 챌린지 뒤의 데이터가 필요하다면, 사이트에 접근 권한을 요청하거나 API 활용, 혹은 데이터 라이선스를 이용하세요.
언제 사이트에 연락해야 하나요?
기본 설정 이상이 필요할 때입니다. 많은 사이트들이 API, 데이터 피드, 파트너 프로그램을 제공하거나 자신의 목적을 밝히고 식별하는 크롤러에는 더 높은 한도를 줍니다. 사용자 에이전트에 연락 URL을 넣고, 크롤러 및 옵트아웃 방법을 설명하는 페이지를 유지하며, 남용 신고에 신속히 답변하세요. 연락 가능한 크롤러는 차단 전에 경고를 받습니다.
403, 429 그리고 CAPTCHA: 질문들
레지덴셜 프록시가 CAPTCHA를 막아주나요?
기대하지 마세요. 레지덴셜 프록시로 IP가 바뀌어도 사이트에 보이는 IP만 바뀔 뿐 크롤러 행위는 바뀌지 않습니다: 속도나 행동으로 인한 CAPTCHA는 계속 유지되고, 이는 의도된 것입니다. 저속 요청에도 계속 챌린지가 있다면, 그 콘텐츠에 자동 접근을 원하지 않는다는 뜻입니다.
Retry-After가 없는 429 에러 후 얼마나 기다려야 하나요?
초반에는 몇 초 정도 기다리고, 이후 429 에러가 반복될 때마다 대기 시간을 두 배로 늘리며 약간의 랜덤 여유와 최대 1분 정도를 제한 시간으로 하세요. 요청이 다시 성공하면 429가 발생했던 속도보다 낮게 시작해, 에러율이 안정적으로 유지될 때까지 서서히 올리세요.
비율 제한을 피하려고 IP 회전을 사용하는 것이 허용되나요?
로드를 여러 레지덴셜 프록시 종료 지점에 분산시키고 위치 선택을 가능케 하는 것은 정상입니다. 하지만 사이트가 거부한 속도를 유지하며 각 IP별 제한 아래로 머무르려고 회전하는 것은 우회입니다. 대부분 이용 약관과 저희 수용 가능한 이용 정책에서 금지합니다.
크롤러가 보내야 할 사용자 에이전트는 무엇인가요?
자신만의 사용자 에이전트를 사용하세요: 이름, 버전, 크롤러 설명과 연락처 URL이 포함된 형태로 ExampleCrawler/1.0 (+https://example.com/crawler-info)와 같이 만들어야 합니다. Googlebot이나 GPTBot 같은 다른 크롤러 이름을 사용하지 마세요.
출처 (4)
- RFC 9309: 로봇 배제 프로토콜 — IETF (2022-09)
- RFC 6585: 추가 HTTP 상태 코드 (429) — IETF (2012-04)
- Reddit 대 SerpApi LLC — Loeb & Loeb (2026-08)
- Google 대 SerpApi: 법원이 우리 기각 신청을 승인함 — SerpApi (2026-07-21)
예의 바른 크롤러를 위한 레지덴셜 프록시
US$1.50는 1 GB용, US$0.50/GB는 50 GB에서 제공합니다.