[▶] cURL 파싱 완료 — https://new.land.naver.com/api/articles/complex/22205
단지 22205 / 파라미터 23개 / 토큰 Bearer eyJhbGci...
[▶] 22205 조회 수집 시작 (요청 파라미터 23개)
[!] 429 — 3초 대기 후 재시도
[!] 429 — 6초 대기 후 재시도
[!] 429 — 9초 대기 후 재시도
[!] 재시도 실패 — 수집분만 저장합니다
[✔] 총 0건
2단계. 흔히 말하는 우회법을 전부 시도
차단 우회로 흔히 권장되는 방법은 크게 세 가지입니다.
실제 브라우저와 동일한 User-Agent·Accept 헤더를 붙인다
응답에서 받은 쿠키(PROP_TEST_KEY, PROP_TEST_ID 등)를 이후 요청에 실어 보낸다
차단되면 쿠키·토큰을 새로 받아 재시도한다
AI에게 이렇게 요청했습니다.
네이버 부동산 API가 429로 막혀. 브라우저와 동일한 User-Agent, Accept 헤더를
붙이고, 응답 쿠키를 파싱해서 다음 요청에 넘기는 구조로 바꿔줘.
차단되면 쿠키를 자동으로 갱신해서 재시도하게 해줘.
결과는 이랬습니다.
시도한 것
결과
토큰 새로 발급 (유효시간 3시간 중 177분 남음)
429
쿠키 새로 복사 (PROP_TEST_KEY/PROP_TEST_ID 포함)
429
로그인 → 비로그인 → 다시 로그인
429
단지 3곳으로 변경
429
70분 대기 후 재시도
429
7시간 뒤 재시도
429
"토큰 만료"가 아니었습니다. 새 토큰으로도 똑같이 막혔으니까요.
3단계. 우연히 발견한 결정적 단서
포기하기 전에 브라우저 화면을 다시 봤습니다. 그런데 Network 탭에 이렇게 찍혀 있었습니다.
1250?realEstateType=APT%3AABYG%3AJGC...page=1... 200 xhr 7.4 kB 62 ms
📸 [이미지 2: Network 탭에서 API 요청이 Status 200, Size 7.4 kB로 정상 표시된 화면]
같은 IP, 같은 쿠키, 같은 시각에 브라우저는 200을 받고 있었습니다.
이 순간 가설이 무너졌습니다. IP가 차단됐다면 브라우저도 막혔어야 하고, rate limit이라면 시간이 지나면 풀렸어야 합니다. 둘 다 아니었습니다.
💡 에러 코드가 말하는 것을 곧이곧대로 믿으면 안 됩니다. TOO_MANY_REQUESTS라고 적혀 있어도, 실제 원인은 "요청이 너무 많아 서"가 아닐 수 있습니다. 확인해야 할 건 "같은 조건에서 되는 경우가 있는가" 입니다.
4단계. 원인 추적 — 딱 한 번의 진단
브라우저와 제 코드의 차이를 좁히기로 했습니다. Claude Code에 이렇게 요청했습니다.
cURL 원본 헤더를 순서까지 그대로 재현해서 딱 1회만 요청해줘.
accept-encoding까지 포함해서. 200이면 헤더 문제 확정이고,
429면 더 깊은 층위의 문제야.
결과는 429였습니다. 헤더를 완벽히 맞춰도 막힌 겁니다.
이걸로 원인이 하나로 좁혀졌습니다.
항목
브라우저
파이썬(requests)
HTTP 버전
HTTP/2
HTTP/1.1
TLS 지문
Chrome
OpenSSL
헤더
동일
동일 (맞춰줌)
결과
200
429
💡 헤더는 요청의 내용물이고, TLS 지문은 연결 방식입니다. 흔한 우회법은 내용물만 다루는데, 네이버는 연결 방식까지 보고 있었습니다. requests든 httpx든 파이썬 HTTP 클라이언트로는 이 층위를 넘을 수 없습니다.
5단계. 해결 — 그럼 진짜 브라우저를 쓰자!
답은 의외로 가까이 있었습니다. 웹 자동화에는 보통 두 갈래가 있습니다.
데이터만 필요할 때 → API 호출 (Copy as cURL)
로그인·클릭·입력 같은 행동이 필요할 때 → 브라우저 자동화 (Playwright)
저는 앞쪽을 골랐고 그게 막혔으니, 뒤쪽 도구를 앞쪽 목적에 쓰면 되겠다는 생각이었습니다.
핵심 아이디어는 이겁니다.
Playwright로 단지 페이지를 연다
→ 그 페이지 안에서 fetch()로 API를 호출한다
→ 요청이 진짜 Chrome에서 나가므로 네이버가 구분할 방법이 없다
Claude Code에 이렇게 요청했습니다.
requests로는 헤더를 완벽히 맞춰도 429가 나. 브라우저는 200이고.
원인은 TLS 지문/HTTP2 차이인 것 같아.
Playwright로 단지 페이지를 열고, 그 페이지 컨텍스트 안에서
fetch()로 API를 호출하는 방식으로 수집기를 만들어줘.
- 쿠키는 cURL에서 파싱해서 브라우저에 주입
- 페이지가 발급하는 최신 토큰을 가로채서 사용
- isMoreData가 false일 때까지 자동 페이징, 요청 간격 0.8초