크롤링이 403으로 막힐 때 확인할 순서: UA·robots·속도

크롤링이 403으로 막힐

크롤링이 403으로 막히면 다들 제일 먼저 프록시부터 바꾸는데, 사실 원인의 상당수는 IP가 아니라 요청 헤더와 속도에 있어요. User-Agent 하나만 제대로 채워도 뚫리는 경우가 많고, robots.txt를 무시한 경로만 골라 요청했다가 걸리는 경우도 흔합니다. 이 글에서는 403의 흔한 원인 네 가지를 실제 점검 순서대로 정리하고, 어디까지가 “고치면 되는 문제”이고 어디부터는 우회가 아니라 포기해야 하는 영역인지 구분해 드릴게요.

403이면 프록시부터 바꾸는 습관, 순서가 잘못됐습니다

403은 서버가 “누구인지는 알겠는데 이 요청은 거부한다”는 의미예요. IP 차단으로 흔히 오해하지만, IP 차단이면 보통 연결 자체가 안 되거나 타임아웃이 나는 경우가 더 많고, 403은 헤더·요청 패턴을 보고 판단한 결과인 경우가 많습니다. 그래서 프록시를 바꿔도 같은 User-Agent와 같은 패턴으로 요청하면 다시 403이 뜨는 일이 자주 생겨요.

점검 순서를 바꿔서, IP보다 먼저 요청 헤더와 요청 간격을 확인하는 게 시간을 훨씬 아끼는 방법입니다. 실제로 파이썬 requests 라이브러리는 기본 User-Agent가 python-requests/2.x.x 형태로 그대로 노출되는데, 이 값 자체를 차단 목록에 넣어둔 사이트가 적지 않습니다.

User-Agent 헤더, 어디까지 바꿔야 통과될까요?

가장 간단한 첫 단계는 실제 브라우저의 User-Agent 문자열을 그대로 복사해서 넣는 거예요. 크롬 개발자도구의 Network 탭에서 아무 요청이나 눌러 Request Headers를 보면 현재 브라우저의 UA 값을 확인할 수 있습니다.

import requests

url = "https://example.com/page"
headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
                  "AppleWebKit/537.36 (KHTML, like Gecko) "
                  "Chrome/124.0.0.0 Safari/537.36",
    "Accept-Language": "ko-KR,ko;q=0.9,en;q=0.8",
}

res = requests.get(url, headers=headers, timeout=10)
print(res.status_code)

터미널 화면에서 HTTP 오류 코드를 확인하는 개발자

기본 User-Agent로 요청하면 403이 뜨다가, 위처럼 브라우저 UA와 Accept-Language를 채운 뒤 200으로 바뀌는 사이트가 실제로 많습니다. 다만 이건 “UA 문자열만 검사하는” 비교적 단순한 방어를 쓰는 사이트에서만 통하는 이야기이고, UA를 바꿨는데도 여전히 403이라면 다음 단계인 robots.txt와 속도 문제로 넘어가야 합니다.

robots.txt는 법이 아니지만, 무시하면 차단 근거가 됩니다

robots.txt는 강제 기술이 아니라 크롤러가 자발적으로 지키기로 약속한 표준이에요. IETF가 2022년에 RFC 9309로 정식 표준화하면서 robots.txt 명세가 크롤러가 따라야 할 규칙을 명확히 정의했습니다. Disallow로 막힌 경로를 계속 긁으면 사이트 운영자 입장에서는 “약속을 어긴 요청”으로 분류할 근거가 생기고, 이후 UA나 IP 단위로 더 강하게 막을 확률이 올라가요.

파이썬 표준 라이브러리로 특정 경로가 허용되는지 코드로 먼저 확인하는 습관을 들이면 불필요한 차단을 줄일 수 있습니다.

from urllib.robotparser import RobotFileParser

rp = RobotFileParser()
rp.set_url("https://example.com/robots.txt")
rp.read()

ua = "MyCrawler/1.0"
print(rp.can_fetch(ua, "https://example.com/page"))
print(rp.crawl_delay(ua))

can_fetch가 False를 반환하는 경로는 처음부터 요청 목록에서 빼는 게 안전하고, crawl_delay 값이 있다면 그 초 단위 간격을 요청 사이에 그대로 반영하는 게 다음 항목인 속도 제한 문제를 예방하는 가장 확실한 방법이에요.

헤더도 robots.txt도 다 지켰는데 갑자기 막히는 이유

초반 몇 번은 200이 오다가 요청을 반복할수록 403이나 429로 바뀌는 패턴이라면, UA 문제가 아니라 초 단위 요청 빈도가 원인일 가능성이 큽니다. 같은 IP·같은 세션에서 짧은 간격으로 반복 요청이 들어오면 WAF(웹 방화벽)나 리버스 프록시 단계에서 자동으로 차단 규칙이 걸리는 구조예요.

웹 방화벽에 차단된 크롤러 봇 개념 이미지

이럴 땐 요청 사이에 time.sleep()으로 1~3초 정도 간격을 두고, 가능하면 무작위 지연을 섞는 게 도움이 됩니다. 아래는 흔한 원인과 대응을 규격 없이 유형별로 정리한 표예요.

원인 흔한 증상 대응
기본 UA 노출 첫 요청부터 즉시 403 브라우저 UA·Accept-Language 헤더 추가
robots.txt Disallow 경로 요청 특정 경로만 골라서 403 can_fetch로 사전 확인 후 제외
요청 속도 초과 초반엔 200, 반복하면 403/429 요청 간 지연, 동시 연결 수 제한
세션·쿠키·Referer 누락 로그인·이전 페이지 필요한 곳에서 403 세션 유지, Referer 헤더 추가
TLS·JS 지문 기반 차단 위 조건을 다 지켜도 403 유지 헤드리스 브라우저 또는 공식 API로 전환

이 방법이 안 통하는 경우: TLS 지문과 JS 챌린지

UA도 맞추고 robots.txt도 지키고 속도도 늦췄는데 계속 403이 뜬다면, 헤더 수준이 아니라 TLS 핸드셰이크 지문(JA3 등)이나 JavaScript 챌린지로 클라이언트를 검증하는 방어 계층일 가능성이 큽니다. MDN의 403 Forbidden 문서에서도 서버가 요청을 이해했지만 권한 문제로 거부한다고 설명하는데, 이 “권한 판단”이 헤더 값이 아니라 클라이언트의 통신 특성 자체를 보는 경우가 여기 해당해요.

이 단계까지 오면 requests나 urllib 같은 일반 HTTP 클라이언트로는 사실상 우회가 어렵고, 무리하게 헤더를 계속 조작하는 건 대상 사이트 이용약관과도 충돌할 수 있습니다. 이럴 땐 우회 시도보다 사이트가 공식 API를 제공하는지, 또는 robots.txt에 명시된 sitemap이나 허용 경로만으로 목표를 달성할 수 있는지부터 다시 확인하는 게 현실적인 선택이에요.

막히면 이 순서로 점검하고, TLS 단계면 멈추세요

정리하면 403을 만났을 때는 프록시가 아니라 ① 브라우저 UA 헤더 ② robots.txt의 can_fetch 결과 ③ 요청 간 지연 시간, 이 세 가지를 순서대로 점검하는 게 가장 빠릅니다. 이 세 가지를 다 맞췄는데도 막힌다면 원인이 TLS 지문이나 JS 챌린지 단계로 넘어간 것이니, 더 공격적으로 우회하기보다는 공식 API나 허용된 경로가 있는지부터 다시 찾아보시길 권해요.

바로 해볼 수 있는 다음 단계는, 지금 막히고 있는 URL의 robots.txt를 위 코드로 먼저 파싱해서 해당 경로가 애초에 허용된 경로인지 확인하는 거예요. 허용된 경로인데도 막힌다면 그때 UA와 요청 간격을 순서대로 조정해 보시면 됩니다.

Windows에서 파이썬 cp949 오류 없애기: 로그·파일·서브프로세스 3곳 총정리

Leave a Comment