Python Selenium으로 네이버 로그인 후 쿠키 추출하여 requests에서 재사용하기 (2026년 3월 업데이트)

2026년 3월 13일
조회수 24
코멘트0

목차

네이버 관련 크롤링이나 API 호출을 하려면 로그인된 세션이 필요합니다. 하지만 네이버는 봇 감지가 강력하여 단순 자동 로그인이 어렵고, CAPTCHA가 수시로 나타납니다. 이 글에서는 Selenium으로 네이버에 로그인하고, CAPTCHA가 나오면 자동 감지 후 사용자가 해결할 때까지 대기하고, 로그인 성공 후 쿠키를 추출하여 일반 requests 라이브러리에서 재사용하는 전체 흐름을 실제 코드와 함께 설명합니다.

핵심 전략: Selenium은 로그인 + CAPTCHA 처리용으로만 사용하고, 실제 데이터 수집은 가벼운 requests로 수행합니다. Chrome 프로필 저장으로 2회차부터는 CAPTCHA 없이 자동 로그인됩니다.

전체 흐름 요약

단계 도구 설명
1 Selenium Chrome 프로필 로드 → 네이버 로그인 페이지 접속
2 Selenium CAPTCHA 감지 시 사용자 수동 해결 대기 (3초 간격 폴링)
3 Selenium 로그인 완료 확인 → 쿠키/세션 Chrome 프로필에 자동 저장
4 Selenium driver.get_cookies()로 쿠키 추출
5 requests 추출한 쿠키를 requests.Session에 주입하여 API 호출

NaverLogin 클래스 - 전체 코드

아래 클래스는 네이버 로그인 → CAPTCHA 처리 → 쿠키 추출 → requests 세션 생성까지 모든 과정을 담고 있습니다.

"""
네이버 로그인 및 쿠키 관리 클래스
Selenium으로 로그인 → 쿠키 추출 → requests에서 재사용
"""
import os
import json
import time
from datetime import datetime
from typing import Optional, Dict, Tuple

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.options import Options
from selenium.common.exceptions import NoSuchElementException
import requests


class NaverLogin:
    """
    네이버 로그인 후 쿠키를 추출하여 requests에서 재사용하는 클래스

    사용 예시:
        with NaverLogin() as naver:
            # 로그인 (CAPTCHA 나오면 자동 대기)
            if naver.login():
                # 방법 1: 쿠키 딕셔너리 직접 사용
                cookies = naver.get_cookies()
                resp = requests.get('https://naver.com', cookies=cookies)

                # 방법 2: requests.Session 사용 (권장)
                session = naver.get_requests_session()
                resp = session.get('https://naver.com')
    """

    NAVER_LOGIN_URL = 'https://nid.naver.com/nidlogin.login'
    NAVER_MY_URL = 'https://nid.naver.com/user2/help/myInfo'

    def __init__(self, profile_dir='chrome_profile', headless=False):
        self.profile_dir = os.path.join(os.getcwd(), profile_dir)
        self.headless = headless
        self.driver = None
        self._cookies = None

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.close()

    # ── Chrome 드라이버 설정 ──────────────────────────────

    def _setup_driver(self):
        """봇 감지 우회 설정이 적용된 Chrome 드라이버 생성"""
        opts = Options()

        # Chrome 프로필 저장 (쿠키/세션 유지)
        if not os.path.exists(self.profile_dir):
            os.makedirs(self.profile_dir)
        opts.add_argument(f'--user-data-dir={self.profile_dir}')
        opts.add_argument('--profile-directory=Default')

        if self.headless:
            opts.add_argument('--headless')

        # 봇 감지 우회 옵션
        opts.add_argument('--disable-blink-features=AutomationControlled')
        opts.add_argument('--no-sandbox')
        opts.add_argument('--disable-dev-shm-usage')
        opts.add_argument('--disable-gpu')
        opts.add_argument('--window-size=1920,1080')
        opts.add_argument(
            'user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) '
            'AppleWebKit/537.36 (KHTML, like Gecko) '
            'Chrome/131.0.0.0 Safari/537.36'
        )
        opts.add_experimental_option('excludeSwitches', ['enable-automation'])
        opts.add_experimental_option('useAutomationExtension', False)

        driver = webdriver.Chrome(options=opts)

        # navigator.webdriver 속성 제거
        driver.execute_cdp_cmd('Page.addScriptToEvaluateOnNewDocument', {
            'source': '''
                Object.defineProperty(navigator, 'webdriver', {
                    get: () => undefined
                });
                window.chrome = { runtime: {} };
                Object.defineProperty(navigator, 'plugins', {
                    get: () => [1, 2, 3, 4, 5]
                });
                Object.defineProperty(navigator, 'languages', {
                    get: () => ['ko-KR', 'ko', 'en-US', 'en']
                });
            '''
        })

        self.driver = driver
        return driver

    # ── CAPTCHA 감지 ──────────────────────────────────────

    def _check_captcha(self) -> Tuple[bool, Optional[str]]:
        """페이지에서 CAPTCHA/보안 확인 여부를 감지"""
        if not self.driver:
            return False, None

        try:
            page_source = self.driver.page_source.lower()

            # 키워드 기반 감지
            keywords = [
                '보안 확인', '보안확인', 'security check',
                '자동입력 방지', 'captcha', 'recaptcha',
                '로봇이 아닙니다', '실제 사용자임을 확인'
            ]
            for kw in keywords:
                if kw in page_source:
                    return True, kw

            # 페이지 제목 확인
            title = self.driver.title.lower()
            if any(k in title for k in ['보안', 'security', 'captcha']):
                return True, f'title: {title}'

            # DOM 요소 확인
            xpaths = [
                "//iframe[contains(@src, 'recaptcha')]",
                "//*[contains(@class, 'captcha')]",
                "//*[contains(@id, 'captcha')]",
            ]
            for xp in xpaths:
                try:
                    self.driver.find_element(By.XPATH, xp)
                    return True, f'element: {xp}'
                except NoSuchElementException:
                    continue

            return False, None

        except Exception as e:
            print(f'  CAPTCHA 감지 오류: {e}')
            return False, None

    # ── CAPTCHA 대기 ──────────────────────────────────────

    def _wait_for_captcha(self, keyword: str, timeout: int = 300):
        """CAPTCHA 해결을 3초 간격으로 폴링하며 대기"""
        print(f'\n{"="*60}')
        print(f'🚨 보안 확인 감지: {keyword}')
        print(f'{"="*60}')
        print('브라우저에서 CAPTCHA를 해결하면 자동으로 진행됩니다.')
        print(f'최대 {timeout}초 대기...\n')

        elapsed = 0
        while elapsed < timeout:
            time.sleep(3)
            elapsed += 3

            is_blocked, _ = self._check_captcha()
            if not is_blocked:
                print(f'✅ 보안 확인 해제! ({elapsed}초 소요)')
                return True

            if elapsed % 30 == 0:
                print(f'   ⏳ 대기 중... ({elapsed}/{timeout}초)')

        print(f'⚠️ 시간 초과 ({timeout}초)')
        return False

    # ── 로그인 확인 ───────────────────────────────────────

    def _is_logged_in(self) -> bool:
        """현재 네이버에 로그인되어 있는지 확인"""
        try:
            self.driver.get(self.NAVER_MY_URL)
            time.sleep(2)

            # 로그인 페이지로 리다이렉트되면 미로그인
            current = self.driver.current_url
            if 'nidlogin' in current or 'login' in current.split('?')[0]:
                return False

            return True
        except:
            return False

    # ── 로그인 실행 ───────────────────────────────────────

    def login(self) -> bool:
        """
        네이버 로그인 수행

        1. Chrome 프로필에서 기존 세션 확인
        2. 미로그인 시 로그인 페이지로 이동 → 사용자 수동 로그인
        3. CAPTCHA 감지 시 자동 대기
        4. 로그인 성공 시 쿠키 저장

        Returns:
            bool: 로그인 성공 여부
        """
        if not self.driver:
            self._setup_driver()

        # 1. 기존 세션 확인
        print('🔍 기존 로그인 세션 확인 중...')
        if self._is_logged_in():
            print('✅ 이미 로그인되어 있습니다! (Chrome 프로필에서 복원)')
            self._extract_cookies()
            return True

        # 2. 로그인 페이지 이동
        print('🔐 네이버 로그인이 필요합니다.')
        print('   브라우저에서 로그인해주세요...\n')
        self.driver.get(self.NAVER_LOGIN_URL)
        time.sleep(2)

        # 3. 로그인 완료 대기 (폴링)
        max_wait = 300
        elapsed = 0

        while elapsed < max_wait:
            time.sleep(3)
            elapsed += 3

            # CAPTCHA 체크
            is_captcha, kw = self._check_captcha()
            if is_captcha:
                self._wait_for_captcha(kw)

            # 로그인 완료 확인
            current = self.driver.current_url
            if 'nidlogin' not in current and 'login' not in current.split('?')[0]:
                # 로그인 페이지를 벗어남 → 로그인 성공
                print('✅ 로그인 성공!')
                print('💾 Chrome 프로필에 세션이 저장되었습니다.')
                self._extract_cookies()
                return True

            if elapsed % 30 == 0:
                print(f'   ⏳ 로그인 대기 중... ({elapsed}/{max_wait}초)')

        print('⚠️ 로그인 시간 초과')
        return False

    # ── 쿠키 추출 ─────────────────────────────────────────

    def _extract_cookies(self):
        """Selenium 드라이버에서 쿠키를 추출하여 저장"""
        if not self.driver:
            return

        selenium_cookies = self.driver.get_cookies()
        self._cookies = {}
        for c in selenium_cookies:
            self._cookies[c['name']] = c['value']

        print(f'🍪 쿠키 {len(self._cookies)}개 추출 완료')

    def get_cookies(self) -> Dict[str, str]:
        """
        추출한 쿠키를 딕셔너리로 반환

        Returns:
            Dict[str, str]: {쿠키이름: 쿠키값} 형태
        """
        if not self._cookies:
            self._extract_cookies()
        return self._cookies or {}

    def get_requests_session(self) -> requests.Session:
        """
        추출한 쿠키가 주입된 requests.Session 반환

        User-Agent도 동일하게 설정되어 바로 사용 가능

        Returns:
            requests.Session: 네이버 로그인 세션
        """
        session = requests.Session()

        # 쿠키 주입
        cookies = self.get_cookies()
        for name, value in cookies.items():
            session.cookies.set(name, value)

        # 동일한 User-Agent 설정
        session.headers.update({
            'User-Agent': (
                'Mozilla/5.0 (Windows NT 10.0; Win64; x64) '
                'AppleWebKit/537.36 (KHTML, like Gecko) '
                'Chrome/131.0.0.0 Safari/537.36'
            ),
            'Referer': 'https://www.naver.com/',
        })

        return session

    def save_cookies(self, filepath: str = 'naver_cookies.json'):
        """쿠키를 JSON 파일로 저장"""
        cookies = self.get_cookies()
        with open(filepath, 'w', encoding='utf-8') as f:
            json.dump(cookies, f, ensure_ascii=False, indent=2)
        print(f'💾 쿠키 저장: {filepath}')

    @staticmethod
    def load_cookies(filepath: str = 'naver_cookies.json') -> Dict[str, str]:
        """JSON 파일에서 쿠키 로드"""
        with open(filepath, 'r', encoding='utf-8') as f:
            return json.load(f)

    def close(self):
        """브라우저 종료"""
        if self.driver:
            self.driver.quit()
            self.driver = None

코드 핵심 설명

1. Chrome 프로필로 쿠키 자동 유지

네이버에 직접 ID/PW를 입력하는 코드를 작성하면 봇으로 탐지됩니다. 대신 Chrome 프로필 디렉토리(chrome_profile/)를 지정하면, Chrome이 쿠키, 세션, 로컬 스토리지를 해당 폴더에 자동 저장합니다.

opts.add_argument(f'--user-data-dir={self.profile_dir}')
opts.add_argument('--profile-directory=Default')

처음 한 번 수동 로그인하면, 이후 실행 시 저장된 세션이 자동으로 복원되어 CAPTCHA 없이 바로 쿠키를 받아올 수 있습니다.

2. 봇 감지 우회

Selenium이 실행한 Chrome에는 navigator.webdriver = true라는 속성이 설정됩니다. 네이버는 이 속성을 확인하여 봇을 탐지합니다. CDP 명령으로 이 속성을 제거합니다:

driver.execute_cdp_cmd('Page.addScriptToEvaluateOnNewDocument', {
    'source': '''
        Object.defineProperty(navigator, 'webdriver', {
            get: () => undefined
        });
    '''
})

3. CAPTCHA 감지 및 폴링 대기

CAPTCHA 감지는 3가지 방법을 조합합니다:

  • 키워드 검색: 페이지 소스에서 '보안 확인', 'captcha' 등 탐색
  • 페이지 제목: 제목에 '보안', 'security' 포함 여부
  • DOM 요소: reCAPTCHA iframe, captcha 관련 클래스/ID 존재 여부

CAPTCHA가 감지되면 input() 대신 3초 간격 자동 폴링으로 해결을 대기합니다. 사용자가 브라우저에서 CAPTCHA를 풀면 자동으로 감지하여 다음 단계로 진행합니다. 이 방식은 GUI 환경(PyQt5 등)에서도 호환됩니다.

4. 쿠키 추출 - Selenium에서 requests로

로그인 성공 후 driver.get_cookies()가 반환하는 쿠키 리스트를 딕셔너리로 변환합니다:

def _extract_cookies(self):
    selenium_cookies = self.driver.get_cookies()
    # [{'name': 'NID_AUT', 'value': 'xxx', 'domain': '.naver.com', ...}, ...]
    self._cookies = {}
    for c in selenium_cookies:
        self._cookies[c['name']] = c['value']
    # {'NID_AUT': 'xxx', 'NID_SES': 'yyy', ...}

이 딕셔너리를 requests.Session에 주입하면 Selenium 없이도 로그인 상태로 HTTP 요청을 보낼 수 있습니다:

def get_requests_session(self):
    session = requests.Session()
    for name, value in self.get_cookies().items():
        session.cookies.set(name, value)
    session.headers.update({
        'User-Agent': 'Mozilla/5.0 ...',  # Selenium과 동일한 UA
        'Referer': 'https://www.naver.com/',
    })
    return session
주의: User-Agent를 Selenium에서 설정한 것과 동일하게 맞춰야 합니다. UA가 다르면 네이버가 세션을 무효화할 수 있습니다.

사용 예시

기본 사용법

with NaverLogin() as naver:
    if naver.login():
        # requests.Session으로 네이버 API 호출
        session = naver.get_requests_session()

        # 예: 네이버 카페 API 호출
        resp = session.get('https://cafe.naver.com/ArticleList.nhn', params={
            'search.clubid': '12345678',
            'search.page': 1
        })
        print(resp.status_code)  # 200
        print(resp.text[:500])

쿠키 저장 후 나중에 재사용

# 1. 로그인 후 쿠키를 파일로 저장
with NaverLogin() as naver:
    if naver.login():
        naver.save_cookies('naver_cookies.json')

# 2. 나중에 Selenium 없이 쿠키만 로드하여 사용
cookies = NaverLogin.load_cookies('naver_cookies.json')
session = requests.Session()
for name, value in cookies.items():
    session.cookies.set(name, value)
session.headers['User-Agent'] = 'Mozilla/5.0 ...'

resp = session.get('https://naver.com')
print('로그인 상태:', 'MY' in resp.text)  # True면 로그인됨

쿠키 딕셔너리 직접 사용

with NaverLogin() as naver:
    if naver.login():
        cookies = naver.get_cookies()
        # {'NID_AUT': '...', 'NID_SES': '...', 'NID_JKL': '...', ...}

        # requests.get에 cookies 파라미터로 직접 전달
        resp = requests.get(
            'https://mail.naver.com/json/list/',
            cookies=cookies,
            headers={'User-Agent': 'Mozilla/5.0 ...'}
        )

실행 흐름 정리

최초 실행
  1. 프로그램 실행 → Chrome 브라우저 열림
  2. 기존 로그인 세션 확인 → 없음
  3. 네이버 로그인 페이지로 이동
  4. 사용자가 브라우저에서 수동 로그인 (ID/PW 입력)
  5. CAPTCHA 나오면 → 자동 감지 → 사용자 해결 대기 (3초 폴링)
  6. 로그인 성공 → Chrome 프로필에 쿠키 자동 저장
  7. get_cookies()로 쿠키 추출 → requests에서 사용
2회차 이후
  1. 프로그램 실행 → Chrome 프로필 자동 로드
  2. 기존 로그인 세션 확인 → 이미 로그인됨!
  3. CAPTCHA 없이 즉시 쿠키 추출
  4. requests에서 바로 사용 가능

자주 묻는 질문

질문 답변
쿠키 유효기간은? 네이버 세션 쿠키(NID_SES)는 보통 수 시간~수일 유효합니다. 만료되면 다시 login()을 호출하면 Chrome 프로필 덕분에 자동 복원됩니다.
Chrome 프로필을 삭제하면? 저장된 세션이 모두 초기화됩니다. 다음 실행 시 수동 로그인이 필요합니다.
headless 모드에서 가능한가요? 최초 로그인은 headless=False로 해야 합니다 (CAPTCHA 수동 해결 필요). 2회차부터는 세션이 유지되므로 headless=True도 가능합니다.
여러 네이버 계정을 사용하려면? profile_dir를 계정별로 다르게 지정하면 됩니다. 예: NaverLogin(profile_dir='profile_account1')
requests 세션이 차단되면? User-Agent가 Selenium과 다르면 차단될 수 있습니다. get_requests_session()은 동일한 UA를 자동 설정하므로 이 메서드를 사용하세요.

마치며

이 방식의 핵심은 Selenium은 로그인 전용, 실제 작업은 requests로 분리하는 것입니다. Selenium은 무겁고 느리지만 CAPTCHA 처리가 가능하고, requests는 가볍고 빠르지만 CAPTCHA 처리가 불가능합니다. 두 도구의 장점을 조합하고, Chrome 프로필 저장으로 재로그인 횟수를 최소화하면 안정적이고 효율적인 네이버 크롤링 환경을 구축할 수 있습니다.

필요한 패키지: pip install selenium requests — ChromeDriver는 Selenium 4.6+에서 자동 관리됩니다.
댓글 0