프록시가 잘못된 데이터를 반환하는 경우: 원인 및 해결 방법
파서를 설정하고 데이터 수집을 시작했는데, 결과는 다른 지역의 가격, 오래된 콘텐츠 또는 완전히 다른 페이지입니다. 프록시가 잘못된 데이터를 반환하는 이유와 이를 수정하는 방법을 분석해 보겠습니다.
1. 프록시 측 캐싱
오래된 데이터의 가장 흔한 원인은 캐싱입니다. 일부 프록시 서버는 부하를 줄이고 속도를 높이기 위해 웹사이트 응답을 저장합니다. 결과적으로 최신 데이터 대신 일주일 전 데이터를 받게 됩니다.
문제 식별 방법
- 반복 요청 시에도 데이터가 변경되지 않음
- 가격이나 재고 현황이 실제와 일치하지 않음
- 응답 헤더의
Age값이 큰 값을 표시함
해결 방법
캐싱을 금지하는 헤더를 추가하세요:
import requests
headers = {
'Cache-Control': 'no-cache, no-store, must-revalidate',
'Pragma': 'no-cache',
'Expires': '0'
}
response = requests.get(
'https://example.com/prices',
proxies={'http': proxy, 'https': proxy},
headers=headers
)
공급업체가 계속 캐싱하는 경우 URL에 임의의 매개변수를 추가하세요:
import time
url = f'https://example.com/prices?_nocache={int(time.time())}'
2. 지리적 위치 불일치
독일 프록시를 요청했는데 루블 가격이 표시됩니다. 또는 러시아 데이터가 필요한데 웹사이트가 미국 콘텐츠를 보여줍니다. 이는 몇 가지 이유로 발생합니다.
지리적 위치가 일치하지 않는 이유
| 원인 | 설명 |
|---|---|
| 오래된 GeoIP 데이터베이스 | IP가 최근에 다른 지역으로 이전되었지만 데이터베이스가 아직 업데이트되지 않음 |
| 웹사이트 자체 데이터베이스 사용 | 대상 웹사이트가 프록시 공급업체와 다르게 지리적 위치를 결정함 |
| 이전 세션의 쿠키 | 웹사이트가 이전 방문 기록을 통해 사용자의 지역을 기억함 |
| Accept-Language 헤더 | 언어 헤더가 프록시의 지리적 위치와 일치하지 않음 |
해결 방법
요청의 모든 매개변수를 원하는 지리적 위치와 동기화하세요:
# 독일 웹사이트 크롤링용
headers = {
'Accept-Language': 'de-DE,de;q=0.9,en;q=0.8',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)...'
}
# 쿠키가 없는 깨끗한 세션
session = requests.Session()
session.cookies.clear()
response = session.get(
'https://example.de/preise',
proxies={'http': german_proxy, 'https': german_proxy},
headers=headers
)
크롤링 전에 프록시의 실제 지리적 위치를 확인하세요:
def check_proxy_geo(proxy):
response = requests.get(
'http://ip-api.com/json/',
proxies={'http': proxy, 'https': proxy},
timeout=10
)
data = response.json()
return data.get('country'), data.get('city')
3. IP 로테이션 문제
자동 IP 로테이션이 있는 레지덴셜 프록시를 사용할 때, IP는 요청마다 변경됩니다. 이는 제한을 우회하는 데 유용하지만, 데이터의 일관성이 필요할 때는 문제가 발생합니다.
일반적인 증상
- 페이지네이션 시 중복 항목이 반환되거나 항목이 건너뜀
- 요청 사이에 장바구니가 비워짐
- 세션 중간에 인증이 해제됨
- A/B 테스트에서 다른 버전의 페이지가 표시됨
해결 방법: 고정 세션 (Sticky Sessions)
대부분의 프록시 공급업체는 특정 시간 동안 IP를 유지하는 '고정 세션'을 지원합니다. 이는 일반적으로 연결 문자열의 매개변수를 통해 설정됩니다.
# 세션 ID가 포함된 형식 (구문은 공급업체에 따라 다름)
proxy = 'http://user-session-abc123:pass@gate.provider.com:7777'
# 동일한 세션 ID를 사용하는 모든 요청은 동일한 IP를 통해 전송됨
for page in range(1, 10):
response = requests.get(
f'https://example.com/catalog?page={page}',
proxies={'http': proxy, 'https': proxy}
)
중요: 고정 세션은 보통 1~30분 동안 지속됩니다. 관련 요청들이 이 시간 내에 완료되도록 데이터 수집 계획을 세워야 합니다.
4. 세션 및 쿠키 무결성 문제
현대 웹사이트는 개인화를 위해 쿠키를 적극적으로 사용합니다. 파서가 쿠키를 올바르게 처리하지 못하면 잘못된 데이터를 받거나 아예 차단될 수 있습니다.
자주 발생하는 오류
- Set-Cookie 무시 — 웹사이트가 세션을 추적할 수 없음
- 다른 IP와 쿠키 재사용 — 의심스러운 동작으로 간주됨
- 초기 요청 부재 — 메인 페이지를 통해 '로그인'하지 않고 내부 페이지로 바로 이동
올바른 접근 방식
import requests
def create_browser_session(proxy):
session = requests.Session()
session.proxies = {'http': proxy, 'https': proxy}
# 첫 번째 방문을 시뮬레이션하여 쿠키 획득
session.get('https://example.com/', headers={
'User-Agent': 'Mozilla/5.0...',
'Accept': 'text/html,application/xhtml+xml...',
'Accept-Language': 'en-US,en;q=0.9'
})
# 이제 유효한 세션으로 크롤링 가능
return session
session = create_browser_session(proxy)
data = session.get('https://example.com/api/prices').json()
5. 인코딩 및 압축 오류
데이터 자체는 올바르지만 인코딩 또는 압축 문제로 인해 잘못 표시되는 경우가 있습니다. 이는 특히 키릴 문자나 아시아 언어를 다룰 때 중요합니다.
증상
- 텍스트 대신 깨진 문자 표시:
Цена(러시아어 '가격' 대신) - gzip이 활성화된 상태에서 빈 응답 수신
- HTML 대신 바이너리 쓰레기 데이터
해결 방법
import requests
response = requests.get(url, proxies=proxies)
# 방법 1: 인코딩 자동 감지
response.encoding = response.apparent_encoding
text = response.text
# 방법 2: 인코딩 강제 지정
text = response.content.decode('utf-8')
# 방법 3: 압축 비활성화 (프록시가 gzip을 손상시키는 경우)
headers = {'Accept-Encoding': 'identity'}
response = requests.get(url, proxies=proxies, headers=headers)
6. 숨겨진 차단 및 캡차
모든 차단이 명확한 것은 아닙니다. 웹사이트는 HTTP 200을 반환하지만 실제 데이터 대신 자리 표시자, 오래된 캐시 또는 일반 HTML 내부에 캡차를 삽입할 수 있습니다.
숨겨진 차단 징후
- 응답 크기가 의심스러울 정도로 작거나 여러 페이지에서 동일함
- HTML 내부에 captcha, challenge, blocked, access denied와 같은 단어가 포함되어 있음
- 예상되는 요소(가격, 설명, 버튼)가 없음
- JavaScript 리디렉션으로 다른 페이지로 이동
응답 유효성 검사
def is_valid_response(response, expected_markers):
"""응답에 실제 데이터가 포함되어 있는지 확인"""
text = response.text.lower()
# 차단 확인
block_signals = ['captcha', 'blocked', 'access denied',
'rate limit', 'try again later']
for signal in block_signals:
if signal in text:
return False, f'Blocked: {signal}'
# 예상 데이터 포함 여부 확인
for marker in expected_markers:
if marker.lower() not in text:
return False, f'Missing: {marker}'
# 크기 확인 (너무 작으면 자리 표시자)
if len(response.content) < 5000:
return False, 'Response too small'
return True, 'OK'
# 사용 예시
valid, reason = is_valid_response(response, ['price', 'add to cart'])
if not valid:
print(f'Invalid response: {reason}')
# 프록시 변경, 대기 후 재시도
심각한 봇 방지 기능이 있는 웹사이트의 경우, 데이터센터 프록시보다 고정 세션을 지원하는 모바일 프록시가 더 높은 신뢰도를 보여줍니다.
7. 단계별 진단
프록시가 잘못된 데이터를 반환할 때, 원인을 찾기 위해 다음 알고리즘을 사용하세요.
1단계: 문제 격리
# 프록시 없음 vs 프록시 사용 시 응답 비교
def compare_responses(url, proxy):
direct = requests.get(url)
proxied = requests.get(url, proxies={'http': proxy, 'https': proxy})
print(f'Direct: {len(direct.content)} bytes, status {direct.status_code}')
print(f'Proxied: {len(proxied.content)} bytes, status {proxied.status_code}')
# 비교를 위해 두 응답 모두 저장
with open('direct.html', 'w') as f:
f.write(direct.text)
with open('proxied.html', 'w') as f:
f.write(proxied.text)
2단계: 응답 헤더 확인
response = requests.get(url, proxies=proxies)
# 진단에 중요한 헤더
important_headers = ['content-type', 'content-encoding',
'cache-control', 'age', 'x-cache',
'cf-ray', 'server']
for header in important_headers:
value = response.headers.get(header, 'not set')
print(f'{header}: {value}')
3단계: 점검 목록
| 점검 항목 | 명령/방법 |
|---|---|
| 프록시의 실제 IP | curl -x proxy:port ifconfig.me |
| IP 지리적 위치 | ip-api.com/json |
| 캐싱 여부 | Age, X-Cache 헤더 |
| 차단 여부 | HTML에서 'captcha', 'blocked' 검색 |
| 인코딩 | Content-Type charset |
4단계: 전체 진단 스크립트
import requests
import json
def diagnose_proxy(proxy, target_url):
report = {}
# 1. 작동 여부 확인
try:
r = requests.get('http://httpbin.org/ip',
proxies={'http': proxy, 'https': proxy},
timeout=15)
report['proxy_ip'] = r.json().get('origin')
report['proxy_works'] = True
except Exception as e:
report['proxy_works'] = False
report['error'] = str(e)
return report
# 2. 지리적 위치 확인
r = requests.get('http://ip-api.com/json/',
proxies={'http': proxy, 'https': proxy})
geo = r.json()
report['country'] = geo.get('country')
report['city'] = geo.get('city')
# 3. 대상 사이트 요청
r = requests.get(target_url,
proxies={'http': proxy, 'https': proxy},
timeout=30)
report['status_code'] = r.status_code
report['content_length'] = len(r.content)
report['cached'] = 'age' in r.headers or 'x-cache' in r.headers
# 4. 차단 여부 확인
block_words = ['captcha', 'blocked', 'denied', 'cloudflare']
report['possibly_blocked'] = any(w in r.text.lower() for w in block_words)
return report
# 사용 예시
result = diagnose_proxy('http://user:pass@proxy:port', 'https://target-site.com')
print(json.dumps(result, indent=2))
결론
프록시에서 잘못된 데이터를 반환하는 문제는 거의 항상 해결 가능합니다. 대부분의 경우 원인은 캐싱, 지리적 위치 불일치 또는 세션 처리 오류입니다. 이 문서의 진단 스크립트를 사용하여 문제의 원인을 신속하게 찾아보세요.
지리적 위치의 정확성과 낮은 차단 비율이 중요한 작업의 경우, 고정 세션을 지원하는 레지덴셜 프록시가 최적입니다. 자세한 내용은 proxycove.com에서 확인하세요.