{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "fae49419-6031-483f-9ffa-3cbe77bd9fe3",
   "metadata": {},
   "source": [
    "## 1단계: 셀레니움 개요 및 개발 환경 구축\n",
    "\n",
    "셀레니움 소개 및 환경 구축\n",
    "\n",
    "### 1. 간단한 내용 정리 (이론 요약)\n",
    "\n",
    "* **셀레니움(Selenium)이란?**\n",
    "* 웹 브라우저를 프로그래밍 언어(파이썬 등)로 제어할 수 있도록 지원하는 브라우저 자동화 프레임워크입니다.\n",
    "* 웹 크롤링(데이터 수집), 반복적인 업무 자동화, 웹 애플리케이션 테스트 등 다양한 분야에서 활용됩니다.\n",
    "\n",
    "\n",
    "* **환경 구축의 변화 (Selenium 4 버전 중심)**\n",
    "* **과거:** 브라우저 버전과 일치하는 크롬 드라이버(`Chromedriver`) 파일의 버전을 매번 확인하고 수동으로 다운로드하여 경로를 지정해야 했습니다.\n",
    "* **현재:** 셀레니움 내부에 웹드라이버 매니저 기능이 기본으로 포함되어, 컴퓨터에 크롬 브라우저만 설치되어 있다면 라이브러리가 드라이버를 알아서 내려받고 실행합니다.\n",
    "\n",
    "### 2. 패키지 설치 명령어\n",
    "\n",
    "```bash\n",
    "pip install selenium\n",
    "\n",
    "```\n",
    "\n",
    "### 3. 파이썬 예시 코드\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "d9247090-d155-4a65-8330-87e74c228750",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "성공적으로 접속했습니다! 페이지 제목: Google\n",
      "브라우저를 안전하게 종료했습니다.\n"
     ]
    }
   ],
   "source": [
    "from selenium import webdriver\n",
    "import time\n",
    "\n",
    "# 1. 크롬 브라우저 실행\n",
    "# Selenium 4 버전부터는 별도의 드라이버 경로 지정 없이 자동으로 실행됩니다.\n",
    "driver = webdriver.Chrome()\n",
    "\n",
    "try:\n",
    "    # 2. 원하는 웹페이지(URL)로 이동\n",
    "    target_url = \"https://www.google.com\"\n",
    "    driver.get(target_url)\n",
    "    \n",
    "    # 3. 브라우저가 열린 모습을 확인할 수 있도록 3초 동안 대기\n",
    "    time.sleep(3)\n",
    "    \n",
    "    # 4. 제대로 접속되었는지 확인하기 위해 웹페이지 제목(Title) 출력\n",
    "    page_title = driver.title\n",
    "    print(f\"성공적으로 접속했습니다! 페이지 제목: {page_title}\")\n",
    "\n",
    "except Exception as e:\n",
    "    print(f\"오류가 발생했습니다: {e}\")\n",
    "\n",
    "finally:\n",
    "    # 5. 작업이 끝나면 반드시 브라우저를 종료하여 메모리 누수를 방지합니다.\n",
    "    driver.quit()\n",
    "    print(\"브라우저를 안전하게 종료했습니다.\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "98743b6b-b003-4494-bcaf-9409b624a6a7",
   "metadata": {},
   "source": [
    "## 2단계: 웹 요소(Element) 찾기 (Locators)\n",
    "\n",
    "이 단계에서는 웹 페이지의 구성 요소를 이해하고, 자신이 원하는 버튼이나 입력창을 컴퓨터가 정확하게 찾을 수 있도록 타겟팅하는 방법을 배웁니다. 크롬 개발자 도구를 활용하는 방법과 셀레니움의 핵심 선택자(Locators) 사용법을 마스터하는 것을 목표로 합니다.\n",
    "\n",
    "### 1. 내용 정리\n",
    "\n",
    "* **웹 요소(Web Element)란?**\n",
    "* HTML 문서 내에 존재하는 텍스트 상자, 버튼, 이미지, 링크 등 웹 페이지를 구성하는 모든 개별 요소를 의미합니다.\n",
    "\n",
    "\n",
    "* **크롬 개발자 도구 (F12) 활용**\n",
    "* 자동화를 하려면 먼저 해당 요소의 HTML 태그와 속성을 알아야 합니다.\n",
    "* 단축키 `F12`를 누른 후, 왼쪽 상단의 화살표 아이콘(Element Selector)을 클릭하고 웹 페이지의 원하는 요소를 선택하면 해당 요소의 HTML 코드를 바로 확인할 수 있습니다.\n",
    "\n",
    "\n",
    "* **셀레니움의 요소 탐색 메소드**\n",
    "* Selenium 4 버전부터는 `By` 클래스를 가져와서 요소를 탐색합니다.\n",
    "* 단수 탐색: `driver.find_element(By.속성, \"값\")` -> 일치하는 첫 번째 요소를 반환합니다.\n",
    "* 복수 탐색: `driver.find_elements(By.속성, \"값\")` -> 일치하는 모든 요소를 리스트 형태로 반환합니다.\n",
    "\n",
    "\n",
    "* **주요 선택자(Locators) 종류**\n",
    "* `By.ID`: 고유한 ID 속성값으로 찾기 (가장 정확하고 빠름)\n",
    "* `By.CLASS_NAME`: 클래스 속성값으로 찾기 (스타일이 같은 여러 요소를 다룰 때 사용)\n",
    "* `By.NAME`: input 태그 등에 지정된 name 속성값으로 찾기\n",
    "* `By.CSS_SELECTOR`: CSS 선택자 문법을 사용하여 구조적으로 찾기\n",
    "* `By.XPATH`: HTML 코드의 절대적/상대적 경로를 이용해 찾기 (다른 방법으로 찾기 어려울 때 유용)\n",
    "\n",
    "\n",
    "### 2. 사전 준비 안내 (개발자 도구 실습)\n",
    "\n",
    "코드를 작성하기 전, 교육생들과 함께 특정 사이트(예: 구글 메인 페이지)를 열고 다음 과정을 먼저 진행합니다.\n",
    "\n",
    "1. 구글 메인 페이지에서 `F12`를 누릅니다.\n",
    "2. 검색창(input) 요소를 클릭하여 해당 태그의 `name`, `class`, 또는 `id` 속성이 무엇인지 직접 눈으로 확인하게 합니다. (예: 구글 검색창의 name 속성값은 대개 `\"q\"`입니다.)\n",
    "\n",
    "\n",
    "### 3. 파이썬 예시 코드\n",
    "\n",
    "구글 페이지에 접속한 후, 검색창 요소를 다양한 방법으로 찾아보고 해당 요소가 화면에 정상적으로 존재하는지 검증하는 기초 코드입니다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "411a07ac-293a-4dba-a79a-0a25fd6ed5ad",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1. NAME 선택자로 검색창 찾기 성공!\n",
      "2. CSS 선택자로 검색창 찾기 성공!\n",
      "3. XPATH 선택자로 검색창 찾기 성공!\n",
      "브라우저를 종료했습니다.\n"
     ]
    }
   ],
   "source": [
    "from selenium import webdriver\n",
    "from selenium.webdriver.common.by import By # 요소를 찾기 위해 반드시 필요한 클래스\n",
    "import time\n",
    "\n",
    "driver = webdriver.Chrome()\n",
    "\n",
    "try:\n",
    "    # 1. 구글 메인 페이지 접속\n",
    "    driver.get(\"https://www.google.com\")\n",
    "    time.sleep(2) # 페이지 로딩 잠시 대기\n",
    "    \n",
    "    # 2. NAME 속성을 이용해 검색창 찾기 (구글 검색창의 name 속성은 'q'입니다)\n",
    "    search_box_name = driver.find_element(By.NAME, \"q\")\n",
    "    print(\"1. NAME 선택자로 검색창 찾기 성공!\")\n",
    "    \n",
    "    # 3. CSS_SELECTOR 속성을 이용해 검색창 찾기\n",
    "    # input 태그이면서 name 속성이 q인 요소를 지정\n",
    "    search_box_css = driver.find_element(By.CSS_SELECTOR, \"[name='q']\")\n",
    "    print(\"2. CSS 선택자로 검색창 찾기 성공!\")\n",
    "    \n",
    "    # 4. XPATH 속성을 이용해 검색창 찾기\n",
    "    # 개발자 도구에서 우클릭 -> Copy -> Copy XPath로 가져온 값을 활용할 수 있습니다.\n",
    "    search_box_xpath = driver.find_element(By.XPATH, \"//*[@name='q']\")\n",
    "    print(\"3. XPATH 선택자로 검색창 찾기 성공!\")\n",
    "\n",
    "except Exception as e:\n",
    "    print(f\"요소를 찾는 과정에서 오류가 발생했습니다: {e}\")\n",
    "\n",
    "finally:\n",
    "    driver.quit()\n",
    "    print(\"브라우저를 종료했습니다.\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "54278832-8a0e-4e54-abdd-e13a1c1fae9d",
   "metadata": {},
   "source": [
    "## 3단계: 브라우저 및 요소 상호작용 (Actions)\n",
    "\n",
    "이 단계에서는 이전 단계에서 찾은 웹 요소(버튼, 입력창 등)에 실제로 키보드 입력을 보내거나 마우스 클릭을 수행하여, 마치 사람이 브라우저를 조작하는 것처럼 동작을 제어하는 방법을 배웁니다.\n",
    "\n",
    "### 1. 내용 정리\n",
    "\n",
    "* **웹 요소 상호작용의 기본 기능**\n",
    "* 찾은 요소에 데이터를 입력하거나 제어하기 위해 다양한 메소드(Method)를 제공합니다.\n",
    "* `.send_keys(\"텍스트\")`: 텍스트 상자(input, textarea)에 글자를 입력합니다.\n",
    "* `.click()`: 버튼, 링크, 체크박스 등을 마우스로 클릭합니다.\n",
    "* `.clear()`: 입력창에 기존에 입력되어 있던 글자를 모두 지웁니다.\n",
    "\n",
    "\n",
    "* **특수 키 제어 (`Keys` 클래스)**\n",
    "* 엔터키(`Enter`), 탭키(`Tab`), 방향키 등 키보드의 특수 키를 입력해야 할 때 사용합니다.\n",
    "* `selenium.webdriver.common.keys` 패키지에서 `Keys`를 가져와 활용합니다.\n",
    "\n",
    "\n",
    "* **브라우저 창 및 내비게이션 제어**\n",
    "* 웹 페이지 안의 요소뿐만 아니라 브라우저 창 자체를 제어하는 명령어도 자주 쓰입니다.\n",
    "* `driver.maximize_window()`: 브라우저 창 크기를 최대화합니다. (요소가 가려져서 클릭 안 되는 현상 방지)\n",
    "* `driver.back()` / `driver.forward()`: 뒤로 가기 / 앞으로 가기\n",
    "* `driver.refresh()`: 페이지 새로고침\n",
    "\n",
    "\n",
    "### 2. 주요 제어 흐름 안내\n",
    "\n",
    "교육생들에게 사람이 웹서핑을 하는 흐름을 코드로 그대로 구현한다는 점을 인지시킵니다.\n",
    "\n",
    "1. 브라우저를 열고 화면을 크게 키운다.\n",
    "2. 검색창을 찾아서 원하는 키워드를 입력한다.\n",
    "3. 엔터키를 누르거나 검색 버튼을 클릭한다.\n",
    "4. 검색 결과 화면으로 이동하면 잠시 둘러본다.\n",
    "\n",
    "### 3. 파이썬 예시 코드\n",
    "\n",
    "파이썬 공식 홈페이지의 검색창(ID 속성값: `\"id-search-field\"`)을 활용하여 키보드 입력과 브라우저 제어를 실습하는 코드입니다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "cb1b2958-1614-4c93-bd65-830c96ec2fce",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "검색어 'pep' 입력 완료\n",
      "검색 실행 (엔터키 입력)\n",
      "이전 페이지(파이썬 메인)로 돌아왔습니다.\n",
      "브라우저를 종료했습니다.\n"
     ]
    }
   ],
   "source": [
    "from selenium import webdriver\n",
    "from selenium.webdriver.common.by import By\n",
    "from selenium.webdriver.common.keys import Keys # 특수 키 입력을 위한 클래스\n",
    "import time\n",
    "\n",
    "driver = webdriver.Chrome()\n",
    "\n",
    "try:\n",
    "    # 1. 브라우저 창 최대화\n",
    "    driver.maximize_window()\n",
    "    \n",
    "    # 2. 파이썬 공식 웹사이트 접속\n",
    "    driver.get(\"https://www.python.org\")\n",
    "    time.sleep(2)\n",
    "    \n",
    "    # 3. 검색창 요소 찾기 (id 속성값이 'id-search-field'입니다)\n",
    "    search_box = driver.find_element(By.ID, \"id-search-field\")\n",
    "    \n",
    "    # 4. 검색창에 검색어 입력하기\n",
    "    search_box.send_keys(\"pep\")\n",
    "    print(\"검색어 'pep' 입력 완료\")\n",
    "    time.sleep(1)\n",
    "    \n",
    "    # 5. 엔터키(Keys.ENTER)를 입력하여 검색 실행\n",
    "    search_box.send_keys(Keys.ENTER)\n",
    "    print(\"검색 실행 (엔터키 입력)\")\n",
    "    time.sleep(3) # 검색 결과 로딩 및 확인을 위해 대기\n",
    "    \n",
    "    # 6. 내비게이션 기능 테스트 (이전 페이지로 돌아가기)\n",
    "    driver.back()\n",
    "    print(\"이전 페이지(파이썬 메인)로 돌아왔습니다.\")\n",
    "    time.sleep(2)\n",
    "\n",
    "except Exception as e:\n",
    "    print(f\"상호작용 중 오류가 발생했습니다: {e}\")\n",
    "\n",
    "finally:\n",
    "    driver.quit()\n",
    "    print(\"브라우저를 종료했습니다.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "76f33cdf-4443-4f8e-8f71-124735fa962b",
   "metadata": {},
   "source": [
    "## 4단계: 동적 페이지 대응과 대기(Wait) 처리 (오류 방지)\n",
    "\n",
    "이 단계에서는 크롤링과 자동화 과정에서 가장 자주 발생하는 오류인 '요소를 찾을 수 없음(`NoSuchElementException`)'을 해결하는 방법을 배웁니다. 인터넷 속도나 웹 페이지 로딩 시간에 맞춰 안전하게 코드가 실행되도록 제어하는 **자동화 프로그램의 핵심 안정화 단계**입니다.\n",
    "\n",
    "\n",
    "### 1. 내용 정리\n",
    "\n",
    "* **대기(Wait) 처리가 필요한 이유**\n",
    "* 파이썬 코드의 실행 속도는 웹 브라우저가 화면을 불러오는 속도보다 훨씬 빠릅니다.\n",
    "* 웹 페이지가 아직 다 로딩되지 않았거나, 자바스크립트(JavaScript)에 의해 뒤늦게 데이터가 나타나는 경우, 코드가 요소를 찾지 못하고 프로그램이 멈춰버립니다.\n",
    "\n",
    "\n",
    "* **시간 기반 무조건 대기 (`time.sleep`)**\n",
    "* 파이썬 자체 내장 함수로, 지정한 초(second)만큼 무조건 코드를 멈춥니다.\n",
    "* 사용하기는 편리하지만, 인터넷이 빨라서 이미 로딩이 끝났어도 무조건 지정된 시간을 기다려야 하므로 프로그램의 속도가 불필요하게 느려집니다. 실무에서는 디버깅용으로만 제한적으로 권장합니다.\n",
    "\n",
    "\n",
    "* **암묵적 대기 (Implicit Wait)**\n",
    "* 브라우저 전체에 '요소를 찾을 때 최대 N초까지는 기다려라'라고 전역 설정을 하는 방식입니다.\n",
    "* 설정이 간단하며, 요소가 2초 만에 나타나면 2초만 기다리고 바로 다음 코드로 진행하므로 효율적입니다.\n",
    "\n",
    "\n",
    "* **명시적 대기 (Explicit Wait)**\n",
    "* **가장 권장되는 방식**입니다. '특정 요소가 화면에 보일 때까지', '특정 버튼이 클릭 가능해질 때까지'와 같이 구체적인 조건을 지정하여 대기합니다.\n",
    "* 조건이 충족되면 지정한 최대 시간이 지나지 않았더라도 즉시 다음 코드가 실행됩니다.\n",
    "\n",
    "\n",
    "### 2. 주요 개념 비교 안내\n",
    "\n",
    "* `time.sleep(10)`: \"인터넷이 1초 만에 켜져도 넌 무조건 10초 동안 가만히 서 있어.\" (비효율적)\n",
    "* `Implicit Wait(10)`: \"앞으로 모든 물건을 찾을 때, 보이지 않으면 최대 10초까지는 찾아봐.\" (전체 적용)\n",
    "* `Explicit Wait(10)`: \"다른 건 몰라도, '로그인 버튼'이 화면에 나타날 때까지만 최대 10초 동안 집중해서 기다려.\" (타겟 맞춤형)\n",
    "\n",
    "### 3. 파이썬 예시 코드\n",
    "\n",
    "암묵적 대기와 명시적 대기를 실제로 설정하고 사용하는 방법을 보여주는 예시 코드입니다.\n",
    "\n",
    "```python\n",
    "\n",
    "```\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "5ab953b1-9242-4bf9-b8ee-425b7a499058",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "명시적 대기 후 요소를 안전하게 찾아 텍스트를 입력했습니다.\n",
      "브라우저를 종료했습니다.\n"
     ]
    }
   ],
   "source": [
    "from selenium import webdriver\n",
    "from selenium.webdriver.common.by import By\n",
    "# 명시적 대기를 위해 새로 추가되는 라이브러리들\n",
    "from selenium.webdriver.support.ui import WebDriverWait\n",
    "from selenium.webdriver.support import expected_conditions as EC\n",
    "import time\n",
    "\n",
    "driver = webdriver.Chrome()\n",
    "\n",
    "# --- [방법 1] 암묵적 대기 설정 ---\n",
    "# 브라우저가 요소를 찾을 때, 요소가 없다면 최대 10초 동안 대기하도록 설정합니다.\n",
    "driver.implicitly_wait(10)\n",
    "\n",
    "try:\n",
    "    # 파이썬 공식 웹사이트 접속\n",
    "    driver.get(\"https://www.python.org\")\n",
    "    \n",
    "    # --- [방법 2] 명시적 대기 사용 ---\n",
    "    # 파이썬 검색창(id=\"id-search-field\")이 화면에 실제로 배치되어 '위치할 때까지' 최대 5초 동안 대기합니다.\n",
    "    # 조건이 만족되면 5초가 지나지 않았더라도 즉시 search_box 변수에 요소를 할당합니다.\n",
    "    search_box = WebDriverWait(driver, 5).until(\n",
    "        EC.presence_of_element_located((By.ID, \"id-search-field\"))\n",
    "    )\n",
    "    \n",
    "    # 대기가 끝난 후 정상적으로 상호작용 진행\n",
    "    search_box.send_keys(\"Selenium Wait\")\n",
    "    print(\"명시적 대기 후 요소를 안전하게 찾아 텍스트를 입력했습니다.\")\n",
    "    time.sleep(2)\n",
    "\n",
    "except Exception as e:\n",
    "    print(f\"지정한 시간 내에 요소를 찾지 못했습니다: {e}\")\n",
    "\n",
    "finally:\n",
    "    driver.quit()\n",
    "    print(\"브라우저를 종료했습니다.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8af39c74-627b-44b3-ae65-0685e06fd2f5",
   "metadata": {},
   "source": [
    "## 5단계: 고급 자동화 기능 및 실무 팁\n",
    "\n",
    "### 1. 내용 정리\n",
    "\n",
    "* **웹 프레임(`iframe`) 전환**\n",
    "* 웹페이지 내부에 또 다른 웹페이지를 삽입하는 `iframe` 태그가 사용된 경우, 셀레니움은 기본적으로 내부에 있는 요소를 인식하지 못합니다.\n",
    "* 조작 영역을 `iframe` 내부로 스위칭(`.switch_to.frame()`)해 준 뒤 작업을 하고, 원래 페이지로 다시 돌아오는(`.switch_to.default_content()`) 개념을 이해해야 합니다.\n",
    "\n",
    "\n",
    "* **자바스크립트(`JavaScript`) 명령 연동 및 스크롤**\n",
    "* 마우스 스크롤을 아래로 내려야 새로운 상품이나 댓글이 로딩되는 동적 페이지를 다룰 때 사용합니다.\n",
    "* 셀레니움의 `execute_script()` 메소드를 이용하면 브라우저에 직접 자바스크립트 명령어를 내려 화면을 스크롤할 수 있습니다.\n",
    "\n",
    "\n",
    "* **헤드리스(Headless) 모드**\n",
    "* 실제 서비스 운영 환경(예: 매일 새벽 자동으로 데이터를 수집하는 서버)에서는 굳이 브라우저 창이 눈에 보일 필요가 없습니다. 창을 띄우지 않고 백그라운드에서 실행하여 컴퓨터 자원을 절약하는 방법입니다.\n",
    "\n",
    "\n",
    "### 2. 파이썬 예시 코드\n",
    "\n",
    "자바스크립트를 활용한 화면 스크롤 제어와, 화면을 띄우지 않는 헤드리스 옵션을 적용하는 종합 실습 코드입니다.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "1ab4e593-df24-46b6-95d0-f4538ed9aee4",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "검색 결과 페이지로 이동했습니다.\n",
      "화면을 아래로 1000px 스크롤했습니다.\n",
      "페이지 최하단까지 스크롤했습니다.\n",
      "브라우저를 안전하게 종료했습니다.\n"
     ]
    }
   ],
   "source": [
    "from selenium import webdriver\n",
    "from selenium.webdriver.common.by import By\n",
    "import time\n",
    "\n",
    "# --- [고급 설정] 헤드리스 모드 적용 방법 ---\n",
    "options = webdriver.ChromeOptions()\n",
    "# 아래 주석을 해제하면 브라우저 창이 뜨지 않는 백그라운드 모드로 동작합니다.\n",
    "# options.add_argument('--headless') \n",
    "\n",
    "driver = webdriver.Chrome(options=options)\n",
    "driver.implicitly_wait(10)\n",
    "\n",
    "try:\n",
    "    # 1. 파이썬 공식 웹사이트 접속 및 창 최대화\n",
    "    driver.get(\"https://www.python.org\")\n",
    "    driver.maximize_window()\n",
    "    time.sleep(2)\n",
    "    \n",
    "    # 2. 검색을 진행하여 내용이 긴 결과 페이지로 이동\n",
    "    search_box = driver.find_element(By.ID, \"id-search-field\")\n",
    "    search_box.send_keys(\"python\")\n",
    "    search_box.submit() # 엔터키 대신 사용할 수 있는 양식 제출 메소드\n",
    "    print(\"검색 결과 페이지로 이동했습니다.\")\n",
    "    time.sleep(2)\n",
    "    \n",
    "    # 3. [스크롤 제어] 자바스크립트를 실행하여 화면을 아래로 1000픽셀 이동\n",
    "    driver.execute_script(\"window.scrollTo(0, 1000);\")\n",
    "    print(\"화면을 아래로 1000px 스크롤했습니다.\")\n",
    "    time.sleep(2)\n",
    "    \n",
    "    # 4. [스크롤 제어] 페이지 가장 바닥까지 스크롤 내리기\n",
    "    driver.execute_script(\"window.scrollTo(0, document.body.scrollHeight);\")\n",
    "    print(\"페이지 최하단까지 스크롤했습니다.\")\n",
    "    time.sleep(2)\n",
    "\n",
    "except Exception as e:\n",
    "    print(f\"작업 중 오류 발생: {e}\")\n",
    "\n",
    "finally:\n",
    "    driver.quit()\n",
    "    print(\"브라우저를 안전하게 종료했습니다.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ca748364-cc6b-4178-bf5c-7a2ea4ded3cc",
   "metadata": {},
   "source": [
    "## 추가: 우회 자동화를 위한 언디텍티드 셀레니움 (Undetected Selenium)\n",
    "\n",
    "실무나 프로젝트를 진행하다 보면 `python.org` 같은 연습용 사이트 외에 일반적인 웹사이트에서 봇 탐지 솔루션(예: Cloudflare 등)에 막혀 자동화가 진행되지 않는 경우가 많습니다. 이때 유용하게 사용할 수 있는 확장 라이브러리인 언디텍티드 셀레니움(Undetected Selenium)을 배웁니다.\n",
    "\n",
    "### 1. 내용 정리\n",
    "\n",
    "* **언디텍티드 셀레니움(Undetected Selenium)이란?**\n",
    "* 일반적인 셀레니움은 브라우저를 실행할 때 \"이 브라우저는 자동화 소프트웨어에 의해 제어되고 있습니다\"라는 고유한 표식(`cdc_` 문자열 등)을 남깁니다. 보안이 적용된 웹사이트는 이 표식을 보고 접속을 차단합니다.\n",
    "* 언디텍티드 셀레니움은 이러한 자동화 흔적을 지우고 수정하여, 웹서버가 일반 사용자가 크롬 브라우저를 켜서 접속한 것처럼 인식하도록 만드는 오프소스 패키지입니다.\n",
    "\n",
    "\n",
    "* **주요 특징**\n",
    "* 기존 셀레니움 문법(`driver.get`, `find_element` 등)을 그대로 사용할 수 있어 코드를 크게 바꿀 필요가 없습니다.\n",
    "* 크롬 드라이버 패치 및 설정을 자동으로 수행해 줍니다.\n",
    "\n",
    "\n",
    "* **주의 사항**\n",
    "* 100% 모든 차단을 뚫을 수 있는 것은 아니며, 웹사이트의 보안 정책 강화에 따라 작동 여부가 달라질 수 있습니다. \n",
    "\n",
    "### 2. 패키지 설치 명령어\n",
    "\n",
    "```bash\n",
    "pip install undetected-chromedriver\n",
    "\n",
    "```\n",
    "\n",
    "### 3. 파이썬 예시 코드\n",
    "\n",
    "기존 셀레니움 코드와 매우 유사하지만, 브라우저를 불러오는 선언부만 `undetected_webdriver`로 변경된 형태입니다. 차단이 자주 발생하는 사이트나 일반 웹페이지를 테스트할 때 활용합니다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "c366b0c6-e1b7-4667-bf53-df7bf5f6d2f0",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "우회 브라우저에서 검색어 입력 성공\n",
      "검색 결과로 이동했습니다.\n",
      "브라우저를 종료했습니다.\n"
     ]
    }
   ],
   "source": [
    "import undetected_chromedriver as uc\n",
    "from selenium.webdriver.common.by import By\n",
    "import time\n",
    "\n",
    "# driver = uc.Chrome()\n",
    "# 또는 내 크롬 브라우저 버전(149)에 맞춰 작동하도록 주 버전을 수동 지정합니다.\n",
    "driver = uc.Chrome(version_main=149)\n",
    "\n",
    "try:\n",
    "    # 테스트할 웹페이지 접속\n",
    "    driver.get(\"https://www.google.com\")\n",
    "    driver.maximize_window()\n",
    "    time.sleep(3)\n",
    "    \n",
    "    # 요소 찾기 및 검색어 입력\n",
    "    search_box = driver.find_element(By.CSS_SELECTOR, \"[name='q']\")\n",
    "    search_box.send_keys(\"Undetected Chromedriver\")\n",
    "    print(\"우회 브라우저에서 검색어 입력 성공\")\n",
    "    time.sleep(1)\n",
    "    \n",
    "    search_box.submit()\n",
    "    print(\"검색 결과로 이동했습니다.\")\n",
    "    time.sleep(3)\n",
    "\n",
    "except Exception as e:\n",
    "    print(f\"우회 실행 중 오류가 발생했습니다: {e}\")\n",
    "\n",
    "finally:\n",
    "    # 브라우저 종료\n",
    "    driver.quit()\n",
    "    print(\"브라우저를 종료했습니다.\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f5f4a513-d5dd-4b3d-a573-32458b307af6",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.15"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
