웹 스크래핑과 세션 인증
웹 스크래핑을 진행하다 보면 특정 페이지가 로그인된 사용자에게만 접근을 허용하는 경우가 많습니다. 이러한 인증 장벽을 넘기 위해서는 브라우저가 서버와 주고받는 세션 정보, 즉 쿠키(Cookie)를 크롤러에 주입하여 로그인 상태를 유지해야 합니다.
쿠키 기반 인증의 원리
웹 서비스는 사용자의 인증 상태를 유지하기 위해 클라이언트 측에 쿠키를 발행합니다. 브라우저는 후속 요청 시 이 쿠키를 HTTP 헤더에 포함하여 서버에 전송하고, 서버는 이를 검증하여 적절한 데이터를 반환합니다. Scrapy와 같은 크롤링 프레임워크에서도 동일한 방식으로 요청 헤더에 쿠키를 실어 보냄으로써 인증된 사용자인 것처럼 위장할 수 있습니다.
브라우저에서 쿠키 추출하기
가장 먼저 타겟 웹사이트에 실제 계정으로 로그인한 후, 브라우저의 개발자 도구를 통해 쿠키 값을 추출해야 합니다.
- 타겟 사이트에 로그인하고 메인 페이지 또는 대시보드로 이동합니다.
- F12 키를 눌러 개발자 도구를 열고 'Network(네트워크)' 탭을 선택합니다.
- 페이지를 새로고침(F5)하여 네트워크 요청을 캡처합니다.
- 요청 목록에서 'Doc' 유형의 메인 페이지 요청을 클릭합니다.
- 'Headers(헤더)' 섹션의 'Request Headers(요청 헤더)'에서
Cookie필드의 값을 복사합니다.
쿠키 문자열을 딕셔너리로 파싱하기
브라우저에서 복사한 쿠키는 단일 문자열 형태이지만, Scrapy의 Request 객체는 딕셔너리(Dictionary) 형태의 쿠키 데이터를 요구합니다. 따라서 문자열을 파싱하여 딕셔너리로 변환하는 유틸리티 함수가 필요합니다.
def parse_cookie_string(cookie_string: str) -> dict:
"""
브라우저에서 추출한 쿠키 문자열을 Scrapy에서 사용 가능한 딕셔너리 형태로 변환합니다.
"""
parsed_cookies = {}
for item in cookie_string.split(';'):
if '=' in item:
key, value = item.split('=', 1)
parsed_cookies[key.strip()] = value.strip()
return parsed_cookies
if __name__ == "__main__":
raw_cookie = "session_id=abc123xyz; user_token=987qwerty; theme=dark"
cookie_dict = parse_cookie_string(raw_cookie)
print(cookie_dict)
Scrapy 프로젝트에 쿠키 적용하기
파싱된 쿠키 딕셔너리를 Scrapy 프로젝트의 설정 파일에 정의하거나, 스파이더 내부에서 직접 호출하여 요청에 포함시킬 수 있습니다.
settings.py
BOT_NAME = 'auth_scraper'
SPIDER_MODULES = ['auth_scraper.spiders']
NEWSPIDER_MODULE = 'auth_scraper.spiders'
ROBOTSTXT_OBEY = False
# 파싱된 쿠키 딕셔너리
TARGET_COOKIES = {
'session_id': 'abc123xyz',
'user_token': '987qwerty',
'theme': 'dark'
}
spiders/auth_spider.py
import scrapy
from scrapy.conf import settings
class DashboardSpider(scrapy.Spider):
name = "dashboard"
allowed_domains = ["example.com"]
start_urls = ["http://www.example.com/dashboard"]
# 설정 파일에서 쿠키 불러오기
auth_cookies = settings.get('TARGET_COOKIES', {})
custom_headers = {
'Connection': 'keep-alive',
'Accept-Language': 'ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36'
}
def start_requests(self):
for url in self.start_urls:
yield scrapy.Request(
url=url,
callback=self.parse_dashboard,
cookies=self.auth_cookies,
headers=self.custom_headers,
meta={
'dont_redirect': True,
'handle_httpstatus_list': [301, 302, 403]
}
)
def parse_dashboard(self, response):
# 인증된 페이지의 HTML을 로컬 파일로 저장하여 검증
filename = "authenticated_response.html"
with open(filename, "wb") as file:
file.write(response.body)
self.log(f"Successfully saved {filename}")
위 코드는 start_requests 메서드를 재정의하여 초기 요청부터 쿠키와 커스텀 헤더를 포함하도록 설계되었습니다. meta 인자를 통해 리디렉션을 방지하고 특정 HTTP 상태 코드를 직접 처리함으로써, 로그인 실패로 인한 리디렉션 문제를 디버깅하기 쉽게 만들었습니다.