크롤러 / 스크래퍼

로그인 세션 및 쿠키 영구화

2단계 인증 사이트의 로그인 세션 보존, Playwright storage_state 활용, 만료 자동 감지 및 갱신 파이프라인.

업데이트: 2026-10-02

회원 전용 도매몰 가격, 사내 인트라넷 게시판, 또는 SMS OTP 등 2단계 인증이 걸려 있는 웹사이트의 데이터를 수집할 때 매번 스크립트에서 아이디와 비밀번호를 입력하고 로그인하는 것은 매우 비효율적이며 보안상으로도 위험합니다.

Playwright의 storage_state 세션 상태 보존 기능을 활용하면, 1회 인증으로 획득한 세션 쿠키와 localStorage 토큰 토큰을 DashCloud의 영구 디스크에 저장해 두고, 서버가 재시작되어도 로그인 상태를 유지하며 즉각 수집을 재개할 수 있습니다.


세션 영구화의 핵심 동작 메커니즘

[ 1회성 초기 인증 (내 로컬 PC) ]
개발자가 브라우저를 열고 로그인 (SMS/OTP 직접 입력)
  │
  ▼
context.storage_state(path="session.json") 호출
  │ (쿠키 + localStorage 토큰이 JSON 파일로 추출됨)
  ▼
대시보드 또는 CLI로 DashCloud 영구 볼륨(/app/data/session.json)에 업로드
 
─────────────────────────────────────────────────────────────
 
[ 24/7 무중단 수집 (DashCloud 런타임) ]
browser.new_context(storage_state="/app/data/session.json")
  │ (로그인 페이지를 거치지 않고 인증 완료 상태로 즉시 브라우저 로드)
  ▼
회원 전용 마이페이지 또는 특가 페이지로 다이렉트 이동

1단계: 로컬에서 1회성 세션 파일 생성하기

내 로컬 컴퓨터에서 브라우저 창(headless=False)을 띄우고 사람이 직접 로그인을 완료한 뒤 세션 파일을 추출하는 1회용 파이썬 스크립트입니다.

generate_session.py (로컬 실행용)
from pathlib import Path
from playwright.sync_api import sync_playwright
 
SESSION_FILE = Path("session.json")
 
def create_initial_session():
    with sync_playwright() as p:
        # 사람이 직접 로그인할 수 있도록 화면에 브라우저 표시
        browser = p.chromium.launch(headless=False)
        context = browser.new_context()
        page = context.new_page()
 
        print("로그인 페이지로 이동합니다. 브라우저에서 직접 로그인을 완료하세요...")
        page.goto("https://target-service.com/login")
 
        # 로그인 완료 후 이동하는 메인 페이지 URL 또는 특정 요소가 뜰 때까지 대기
        # 예: 마이페이지 링크나 로그아웃 버튼이 보일 때까지 대기 (최대 120초)
        page.wait_for_selector("a[href='/logout']", timeout=120000)
        print("로그인이 감지되었습니다! 세션 상태를 저장합니다.")
 
        # [핵심] 쿠키와 로컬스토리지 전체를 session.json으로 덤프
        context.storage_state(path=str(SESSION_FILE))
        print(f"세션 파일 저장 완료: {SESSION_FILE.resolve()}")
 
        context.close()
        browser.close()
 
if __name__ == "__main__":
    create_initial_session()

생성된 session.json 파일을 DashCloud 프로젝트의 /app/data/session.json 위치에 업로드합니다 (웹 대시보드의 파일 메뉴에서 드래그앤드롭 가능).


2단계: DashCloud에서 세션 주입하여 24시간 수집하기

저장된 session.json 파일이 존재할 경우 자동으로 브라우저 컨텍스트에 주입하여 로그인 과정을 건너뛰고 바로 타깃 페이지를 수집하는 프로덕션 코드입니다.

authenticated_crawler.py
import os
import logging
from pathlib import Path
from playwright.sync_api import sync_playwright
 
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("AuthCrawler")
 
SESSION_PATH = Path("/app/data/session.json")
 
def run_authenticated_crawler():
    if not SESSION_PATH.exists():
        logger.error(f"세션 파일({SESSION_PATH})이 존재하지 않습니다! 먼저 세션을 업로드하세요.")
        return
 
    with sync_playwright() as p:
        browser = p.chromium.launch(headless=True)
 
        # [핵심] 저장된 세션 상태를 주입하여 컨텍스트 생성
        context = browser.new_context(
            storage_state=str(SESSION_PATH),
            viewport={"width": 1920, "height": 1080}
        )
 
        page = context.new_page()
        try:
            logger.info("회원 전용 페이지로 직접 이동합니다...")
            page.goto("https://target-service.com/wholesale/products", timeout=30000)
 
            # 1. 세션 만료 여부 검증 (로그아웃 버튼 존재 확인)
            if not page.is_visible("a[href='/logout']"):
                logger.warning("⚠️ 세션이 만료되어 비로그인 상태로 리다이렉트되었습니다!")
                handle_session_expired()
                return
 
            # 2. 회원 전용 데이터 수집 수행
            items = page.query_selector_all(".wholesale-item")
            logger.info(f"성공: {len(items)}개 도매 상품 정보를 수집했습니다.")
 
        except Exception as e:
            logger.error(f"크롤링 도중 예외 발생: {e}")
        finally:
            context.close()
            browser.close()
 
def handle_session_expired():
    # 텔레그램이나 디스코드로 관리자에게 세션 만료 알림 전송
    logger.critical("관리자 텔레그램으로 재로그인 요청 알림을 발송합니다.")
 
if __name__ == "__main__":
    run_authenticated_crawler()

3단계: 세션 자동 갱신 파이프라인

일반적인 웹사이트는 마지막 접속 후 7일~30일이 경과하면 보안 정책상 세션을 강제 만료시킵니다.

세션 만료를 방지하고 최신 쿠키로 유지하려면, 주기적으로 메인 페이지를 방문하고 갱신된 쿠키를 다시 storage_state로 덮어쓰는 Keep-Alive 루프를 구성해야 합니다.

keep_alive.py
def refresh_and_save_session(page, context, session_path: Path):
    """주기적으로 페이지를 방문하여 갱신된 세션 토큰을 영구 디스크에 저장"""
    try:
        page.goto("https://target-service.com/my-account", timeout=20000)
        # 최신 세션 상태를 다시 디스크에 덮어쓰기
        context.storage_state(path=str(session_path))
        logger.info("세션 상태가 성공적으로 갱신되었습니다.")
    except Exception as e:
        logger.warning(f"세션 갱신 실패: {e}")

보안 주의사항

  • session.json 파일에는 내 계정의 실제 로그인 쿠키가 포함되어 있으므로 절대 깃허브 공개 저장소에 커밋하지 마세요.
  • 반드시 프로젝트의 .gitignore 파일에 data/ 및 *.json을 추가하세요.
  • DashCloud는 /app/data 디렉터리를 외부와 격리된 전용 물리 볼륨에 안전하게 저장하므로 안전성이 보장됩니다.

다음 추천 문서