웹 스크래핑은 웹에서 데이터를 추출하는 효과적인 방법입니다. 많은 개발자가 웹 스크래핑 프로젝트를 수행할 때 간단하고 효과적인 Python 요청 라이브러리를 선호합니다. 하지만 요청 라이브러리에는 그 장점만큼이나 한계도 있습니다. 웹 스크래핑에서 발생할 수 있는 일반적인 문제 중 하나는 요청 실패이며, 이는 종종 불안정한 데이터 추출로 이어집니다. 이 글에서는 HTTP 오류를 처리하고 웹 스크래핑 스크립트를 안정적이고 신뢰할 수 있게 유지할 수 있도록 Python에서 요청 재시도를 구현하는 과정을 살펴보겠습니다.
먼저 환경을 설정해 보겠습니다. Python과 원하는 IDE가 설치되어 있는지 확인하세요. 그런 다음 요청 라이브러리가 설치되어 있지 않은 경우 설치합니다.
pip install requests
설치가 완료되면 Python의 요청 모듈을 사용하여 example.com으로 요청을 전송해 보겠습니다. 다음은 이를 수행하는 간단한 함수입니다:
import requests
def send_request(url):
"""
지정된 URL로 HTTP GET 요청을 전송하고 응답 상태 코드를 인쇄합니다.
Parameters:
url (str): 요청을 보낼 URL입니다.
"""
response = requests.get(url)
print('Response Status Code: ', response.status_code)
send_request('https://example.com')
코드 출력은 아래와 같습니다:
HTTP 상태 코드를 자세히 살펴보고 더 잘 이해해 보겠습니다.
서버는 요청의 결과를 나타내는 상태 코드와 함께 HTTP 요청에 응답합니다. 간단히 요약하면 다음과 같습니다:
이 예에서 상태 코드 200은 https://example.com 에 대한 요청이 완료되었음을 의미합니다. 이는 서버가 "모든 것이 정상입니다, 요청이 성공했습니다"라고 말하는 방식입니다.
이러한 상태 코드는 봇을 탐지하고 봇과 유사한 행동으로 인해 액세스가 제한되는 시기를 표시하는 데에도 중요한 역할을 할 수 있습니다.
다음은 주로 봇 탐지 및 인증 문제로 인해 발생하는 HTTP 오류 코드에 대한 간략한 요약입니다.
이제 요청 라이브러리로 HTTP GET 요청을 하는 간단한 재시도 메커니즘을 파이썬으로 작성해 보겠습니다. 네트워크 문제나 서버 과부하로 인해 네트워크 요청이 실패하는 경우가 있습니다. 따라서 요청이 실패하면 이러한 요청을 다시 시도해야 합니다.
send_request_with_basic_retry_mechanism 함수는 네트워크 또는 연결 오류와 같은 요청 예외가 발생한 경우에만 재시도하는 기본 재시도 메커니즘을 사용하여 지정된 URL로 HTTP GET 요청을 보냅니다. 이 메커니즘은 요청 최대 재시도 횟수를 최대로 재시도합니다. 이러한 예외로 인해 모든 시도가 실패하면 마지막으로 발생한 예외를 발생시킵니다.
import requests
import time
def send_request_with_basic_retry_mechanism(url, max_retries=2):
"""
기본 재시도 메커니즘을 사용하여 URL에 HTTP GET 요청을 보냅니다.
Parameters:
url (str): 요청을 보낼 URL입니다.
max_retries (int): 요청을 다시 시도할 수 있는 최대 횟수입니다.
Raises:
requests.RequestException: 모든 재시도가 실패하면 마지막 예외를 발생시킵니다.
"""
for attempt in range(max_retries):
try:
response = requests.get(url)
print('Response status: ', response.status_code)
break # 요청 성공 시 루프 종료
except requests.RequestException as error:
print(f"Attempt {attempt+1} failed:", error)
if attempt < max_retries - 1:
print(f"Retrying...")
time.sleep(delay) # 다시 시도하기 전에 대기
else:
print("Max retries exceeded.")
# 최대 재시도에 도달하면 마지막 예외를 다시 발생시킵니다.
raise
send_request_with_basic_retry_mechanism('https://example.com')
이제 스크래핑하려는 웹사이트가 차단을 초래할 수 있는 봇 탐지 메커니즘을 구현하는 시나리오를 처리하기 위해 기본 재시도 메커니즘을 조정해 보겠습니다. 이러한 시나리오를 해결하려면 봇 탐지 차단뿐만 아니라 네트워크 또는 서버 문제 때문일 수도 있으므로 요청을 여러 번 부지런히 재시도해야 합니다.
아래 send_request_with_advance_retry_mechanism 함수는 재시도 횟수 및 재시도 지연을 선택적으로 지정한 URL로 HTTP GET 요청을 전송합니다. 지정된 시도 횟수만큼 여러 번 요청을 전송하고 요청이 성공적으로 응답을 받으면 응답 상태 코드를 출력합니다. 요청 작업 중 오류가 발생하면 오류 메시지를 출력하고 재시도합니다. 각 시도 사이에 지정된 재시도 지연 시간을 기다립니다. 지정된 재시도 횟수 이후에도 요청이 실패하면 마지막으로 발생한 예외를 발생시킵니다.
지연 매개변수는 가까운 간격으로 여러 요청이 서버에 폭격을 가하는 것을 방지하기 때문에 중요합니다. 대신 서버가 요청을 처리할 충분한 시간을 확보할 때까지 기다리므로 서버는 봇이 아닌 사람이 요청을 하는 것으로 간주합니다. 따라서 재시도 메커니즘은 봇 방지 메커니즘을 트리거할 수 있는 서버 과부하 또는 느린 서버 응답을 피하기 위해 지연되어야 합니다.
import requests
import time
def send_request_with_advance_retry_mechanism(url, max_retries=3, delay=1):
"""
고급 재시도 메커니즘을 사용하여 지정된 URL로 HTTP GET 요청을 보냅니다.
Parameters:
url (str): 요청을 보낼 URL입니다.
max_retries (int): 요청을 다시 시도할 수 있는 최대 횟수입니다. 기본값은 3입니다.
delay (int): 재시도 사이의 지연 시간(초)입니다. 기본값은 1입니다.
Raises:
requests.RequestException: 모든 재시도가 실패하면 마지막 예외를 발생시킵니다.
"""
for attempt in range(max_retries):
try:
response = requests.get(url)
# 4xx 또는 5xx 상태 코드에 대한 예외 발생
response.raise_for_status()
print('Response Status Code:', response.status_code)
except requests.RequestException as e:
# 요청이 실패하면 오류 메시지와 시도 번호를 인쇄합니다.
print(f"Attempt {attempt+1} failed:", e)
if attempt < max_retries - 1:
# 재시도 메시지를 인쇄하고 재시도하기 전에 기다립니다.
print(f"Retrying in {delay} seconds...")
time.sleep(delay)
else:
# 최대 재시도 횟수를 초과하면 메시지를 출력하고 예외를 다시 발생시킵니다.
print("Max retries exceeded.")
raise
# 사용 예시
send_request_with_advance_retry_mechanism('https://httpbin.org/status/404')
다음은 수정된 코드와 단점을 설명하는 댓글입니다:
import requests
import time
def send_request_with_advance_retry_mechanism(url, max_retries=3, delay=1, min_content_length=10):
"""
고급 재시도 메커니즘을 사용하여 지정된 URL로 HTTP GET 요청을 보냅니다.
Parameters:
url (str): 요청을 보낼 URL입니다.
max_retries (int): 요청을 다시 시도할 수 있는 최대 횟수입니다. 기본값은 3입니다.
delay (int): 재시도 사이의 지연 시간(초)입니다. 기본값은 1입니다.
min_content_length (int): 유효한 것으로 간주할 응답 콘텐츠의 최소 길이입니다. 기본값은 10입니다.
Raises:
requests.RequestException: 모든 재시도가 실패하면 마지막 예외를 발생시킵니다.
"""
for attempt in range(max_retries):
try:
response = requests.get(url)
# 4xx 또는 5xx 상태 코드에 대한 예외 발생
response.raise_for_status()
# 응답 상태 코드가 404인지 확인
if response.status_code == 404:
print("404 Error: Not Found")
break # 404 오류에 대한 루프 종료
# 응답 텍스트의 길이가 지정된 최소 콘텐츠 길이보다 짧은지 확인합니다.
if len(response.text) < min_content_length:
print("Response text length is less than specified minimum. Retrying...")
time.sleep(delay)
continue # 요청 다시 시도
print('Response Status Code:', response.status_code)
# 조건이 충족되면 루프에서 벗어나기
break
except requests.RequestException as e:
print(f"Attempt {attempt+1} failed:", e)
if attempt < max_retries - 1:
print(f"Retrying in {delay} seconds...")
time.sleep(delay)
else:
print("Max retries exceeded.")
# 최대 재시도에 도달하면 마지막 예외를 다시 발생시킵니다.
raise
# 사용 예
send_request_with_advance_retry_mechanism('https://httpbin.org/status/404')
429 너무 많은 요청과 같은 특정 오류의 경우, 로테이션 프록시를 사용하면 요청을 분산하고 속도 제한을 피하는 데 도움이 될 수 있습니다.
아래 코드는 프록시 사용과 함께 고급 재시도 전략을 구현합니다. 이렇게 하면 파이썬 요청 재시도 메커니즘을 구현할 수 있습니다. 고품질 웹 스크래핑 프록시를 사용하는 것도 중요합니다. 이러한 프록시는 프록시 로테이션을 위한 좋은 알고리즘과 안정적인 풀을 갖추고 있어야 합니다.
import requests
import time
def send_request_with_advance_retry_mechanism(url, max_retries=3, delay=1, min_content_length=10):
"""
고급 재시도 메커니즘을 사용하여 지정된 URL로 HTTP GET 요청을 보냅니다.
Parameters:
url (str): 요청을 보낼 URL입니다.
max_retries (int): 요청을 다시 시도할 수 있는 최대 횟수입니다. 기본값은 3입니다.
delay (int): 재시도 사이의 지연 시간(초)입니다. 기본값은 1입니다.
Raises:
requests.RequestException: 모든 재시도가 실패하면 마지막 예외를 발생시킵니다.
"""
proxies = {
"http": "http://USER:PASS@HOST:PORT",
"https": "https://USER:PASS@HOST:PORT"
}
for attempt in range(max_retries):
try:
response = requests.get(url, proxies=proxies, verify=False)
# 4xx 또는 5xx 상태 코드에 대한 예외 발생
response.raise_for_status()
# 응답 상태 코드가 404인지 확인합니다.
if response.status_code == 404:
print("404 Error: Not Found")
break # Exit loop for 404 errors
# 응답 텍스트의 길이가 10자 미만인지 확인합니다.
if len(response.text) < min_content_length:
print("Response text length is less than 10 characters. Retrying...")
time.sleep(delay)
continue # 요청 다시 시도
print('Response Status Code:', response.status_code)
# 조건이 충족되면 루프에서 벗어나기
break
except requests.RequestException as e:
print(f"Attempt {attempt+1} failed:", e)
if attempt < max_retries - 1:
print(f"Retrying in {delay} seconds...")
time.sleep(delay)
else:
print("Max retries exceeded.")
# 최대 재시도에 도달하면 마지막 예외를 다시 발생시킵니다.
raise
send_request_with_advance_retry_mechanism('https://httpbin.org/status/404')
Python의 요청 재시도는 효과적인 웹 스크래핑을 위해 매우 중요합니다. 재시도를 관리하는 방법은 차단을 방지하고 데이터 수집의 효율성과 안정성을 높이는 데 도움이 될 수 있습니다. 이러한 기술을 구현하면 웹 스크래핑 스크립트를 더욱 강력하게 만들고 봇 보호 시스템의 탐지에 덜 취약하게 만들 수 있습니다.
댓글: 0