개발/자동화

[자동화] 네이버 자동검색 티스토리 이동

kimchangmin02 2025. 8. 10. 20:35
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from webdriver_manager.chrome import ChromeDriverManager
import time
import random
import pyautogui
from bs4 import BeautifulSoup # HTML 분석을 위한 라이브러리

# --- 설정 (이전과 동일) ---
SEARCH_QUERY = "site:kimchangmin02.tistory.com"
BLOG_URL_IDENTIFIER = "kimchangmin02.tistory.com" # 링크가 이 주소로 시작하는지 검사
TOTAL_LOOPS = 1

# --- 헬퍼 함수 (이전과 동일) ---
def get_absolute_coordinates(driver, element):
    """요소의 모니터 기준 절대 좌표를 계산하는 함수"""
    script = """
    const rect = arguments[0].getBoundingClientRect();
    return [
        Math.round(rect.left + window.screenX), 
        Math.round(rect.top + window.screenY)
    ];
    """
    return driver.execute_script(script, element)

def human_like_move_and_click(driver, element):
    """사람처럼 마우스를 움직여 클릭하는 함수"""
    driver.execute_script("arguments[0].scrollIntoView({block: 'center'});", element)
    time.sleep(0.5)

    abs_x, abs_y = get_absolute_coordinates(driver, element)
    if abs_x == 0 and abs_y == 0 and not element.is_displayed():
        print("!!! 경고: 요소의 좌표를 얻지 못했거나 화면에 보이지 않습니다. JavaScript 클릭으로 대체합니다.")
        driver.execute_script("arguments[0].click();", element)
        return

    size = element.size
    target_x = abs_x + (size['width'] / 2) + random.uniform(-size['width']/4, size['width']/4)
    target_y = abs_y + (size['height'] / 2) + random.uniform(-size['height']/4, size['height']/4)
    
    print(f"-> 사람처럼 마우스를 (모니터 기준 {int(target_x)}, {int(target_y)}) 좌표로 이동합니다.")
    pyautogui.moveTo(target_x, target_y, duration=random.uniform(0.5, 1.2))
    
    print("-> Selenium으로 안전하게 클릭하여 포커스를 확보합니다.")
    element.click()


# --- 메인 로직 ---
for i in range(TOTAL_LOOPS):
    print(f"\n--- [ {i + 1} / {TOTAL_LOOPS} 번째 네이버 자동화 작업을 시작합니다 ] ---")
    driver = None
    try:
        # --- 드라이버 설정 (이전과 동일) ---
        options = webdriver.ChromeOptions()
        options.add_argument("user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36")
        options.add_experimental_option("excludeSwitches", ["enable-automation"])
        options.add_experimental_option('useAutomationExtension', False)
        
        driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=options)
        driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", { "source": "Object.defineProperty(navigator, 'webdriver', {get: () => undefined})" })
        driver.maximize_window()
        driver.implicitly_wait(10)

        # --- 네이버 검색 (이전과 동일) ---
        driver.get('https://www.naver.com')
        print("네이버에 접속했습니다.")
        time.sleep(random.uniform(1.5, 2.5))

        search_box = driver.find_element(By.NAME, 'query')
        search_box.click()
        time.sleep(random.uniform(0.5, 1.0))

        pyautogui.typewrite(SEARCH_QUERY, interval=random.uniform(0.1, 0.25))
        pyautogui.press('enter')
        print("검색을 실행했습니다.")
        time.sleep(random.uniform(3, 5))
        
        # 1. HTML 소스 가져와서 분석
        print("현재 페이지의 전체 HTML 소스를 가져옵니다.")
        html_source = driver.page_source
        
        print("BeautifulSoup을 사용하여 HTML을 분석합니다.")
        soup = BeautifulSoup(html_source, 'html.parser')
        
        # ⬇️⬇️⬇️ 여기가 수정된 핵심 부분입니다! ⬇️⬇️⬇️
        # href 속성이 'https://내블로그주소' 또는 'http://내블로그주소'로 '시작'하는 <a> 태그만 찾습니다.
        # 이 방법으로 search.naver.com 링크나 nid.naver.com 링크를 완벽하게 걸러낼 수 있습니다.
        print("링크 주소가 실제 블로그 주소로 '시작'하는 링크만 정교하게 필터링합니다...")
        blog_links = soup.find_all('a', href=lambda href: href and (href.startswith('https://' + BLOG_URL_IDENTIFIER) or href.startswith('http://' + BLOG_URL_IDENTIFIER)))

        if blog_links:
            # 2. 찾은 '진짜' 링크의 URL을 가져옵니다.
            target_link_url = blog_links[0]['href']
            print(f"성공! 실제 블로그 링크 주소를 찾았습니다: {target_link_url}")
            
            try:
                # 3. 해당 URL을 가진 요소를 페이지에서 다시 찾아 클릭합니다.
                print("찾아낸 링크 주소를 이용해 페이지에서 클릭할 요소를 정확히 다시 찾습니다.")
                element_to_click = driver.find_element(By.XPATH, f"//a[@href='{target_link_url}']")

                print("사람처럼 움직여서 요소를 클릭합니다.")
                human_like_move_and_click(driver, element_to_click)
                
                time.sleep(3)
                print(f"블로그 방문 성공! 현재 URL: {driver.current_url}")

            except Exception as e:
                print(f"!!! HTML에서 링크는 찾았지만, 페이지에서 해당 요소를 클릭하는 데 실패했습니다: {e}")
                
        else:
            print(f"페이지 HTML 소스에서 '{BLOG_URL_IDENTIFIER}'로 시작하는 실제 블로그 링크를 찾지 못했습니다.")
            
    except Exception as e:
        print(f"!!! 작업 중 오류가 발생했습니다: {e}")

    finally:
        if driver:
            print("5초 후에 브라우저를 닫습니다.")
            time.sleep(5)
            driver.quit()

print("\n--- [ 모든 자동화 작업이 종료되었습니다 ] ---")

 

일단 티스토리로 이동까지는 됫음

이제 게시물 클릭을 해야함

 

또한 다른 사람의 티스토리에서도 적용가능하게, 

input을 이용해서, 주소 입력받는 코드도 추가해야지

 


#알림창 대비

import time
import random
import string
import traceback
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.action_chains import ActionChains
from selenium.common.exceptions import NoSuchElementException, TimeoutException, UnexpectedAlertPresentException

# --- 헬퍼 함수 정의 (이전과 동일) ---
def human_like_mouse_move(driver, element):
    """사람처럼 보이게 마우스 커서를 해당 요소로 부드럽게 이동시킵니다."""
    actions = ActionChains(driver)
    actions.move_to_element(element).perform()
    print("    ㄴ 사람처럼 마우스 커서를 목표 지점으로 이동했습니다.")
    time.sleep(random.uniform(0.5, 1.0))

def extract_post_text(driver):
    """게시물 본문에서 텍스트를 추출하여 단어 리스트로 반환합니다."""
    try:
        wait = WebDriverWait(driver, 10)
        post_body = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, "div.contents_style")))
        text = post_body.text
        # 구두점을 공백으로 치환하여 단어 분리 개선
        translator = str.maketrans(string.punctuation, ' ' * len(string.punctuation))
        words = text.translate(translator).split()
        return [word for word in words if len(word) > 1]  # 1글자 단어 제외
    except Exception:
        return []

def generate_random_comment(word_list):
    """추출된 단어 리스트를 기반으로 무작위 댓글을 생성합니다."""
    if not word_list:
        return "포스팅 잘 보고 갑니다. 좋은 하루 되세요."
    num_words = random.randint(4, 9)
    if len(word_list) < num_words:
        num_words = len(word_list)
    return " ".join(random.sample(word_list, num_words))

def generate_random_credentials():
    """무작위 이름과 비밀번호를 생성합니다."""
    name = ''.join(random.choices(string.ascii_lowercase, k=random.randint(5, 8)))
    password = ''.join(random.choices(string.ascii_letters + string.digits, k=random.randint(8, 12)))
    return name, password

# --- 메인 코드 ---
BASE_URL = "https://kimchangmin02.tistory.com"
TARGET_URLS = [f"{BASE_URL}/{i}" for i in range(1, 65)]
WAIT_TIMEOUT = 15
loop_count = 0  # 몇 번째 시도인지 카운트하기 위한 변수

print("="*60)
print(" TISTORY 자동화 봇 (무한 반복 버전) ")
print(f" 총 {len(TARGET_URLS)}개의 게시물을 대상으로 작업을 무한 반복합니다.")
print(" 프로그램을 종료하려면 이 창에서 Ctrl + C 를 누르세요.")
print("="*60)

# --- 무한 루프 ---
while True:
    driver = None
    loop_count += 1
    try:
        print(f"\n--- [ {loop_count} 번째 시도 시작 ] ---")
        target_page = random.choice(TARGET_URLS)
        print(f"대상 URL: '{target_page}'")
        
        chrome_options = webdriver.ChromeOptions()
        chrome_options.add_argument("--start-maximized")
        chrome_options.add_experimental_option("excludeSwitches", ["enable-automation"])
        driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=chrome_options)
        wait = WebDriverWait(driver, WAIT_TIMEOUT)
        
        driver.get(target_page)
        print(f"페이지로 성공적으로 이동했습니다.")
        
        print("\n--- 1. 페이지 탐색 (스크롤) ---")
        time.sleep(random.uniform(1, 2))
        scroll_count = random.randint(2, 4)
        for j in range(scroll_count):
            scroll_depth = (j + 1) / (scroll_count + 1)
            driver.execute_script(f"window.scrollTo(0, document.body.scrollHeight * {scroll_depth});")
            time.sleep(random.uniform(1.0, 2.5))
        print("페이지 스크롤을 완료했습니다.")

        print("\n--- 2. '좋아요' 클릭 시도 ---")
        try:
            like_button = WebDriverWait(driver, 5).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "div[id^='reaction-'] button.btn_post")))
            driver.execute_script("arguments[0].scrollIntoView({block: 'center', behavior: 'smooth'});", like_button)
            time.sleep(random.uniform(1, 2))
            human_like_mouse_move(driver, like_button)
            like_button.click()
            print("    ㄴ 성공! '좋아요' 버튼을 클릭했습니다.")
        except TimeoutException:
            print("    ㄴ '좋아요' 버튼을 찾을 수 없거나 이미 비활성화 상태입니다.")
        
        # --- 3. 댓글 작성 시도 (수정된 부분) ---
        print("\n--- 3. 댓글 작성 시도 ---")
        try:
            print("댓글창 로딩을 유도하기 위해 페이지 최하단으로 스크롤합니다.")
            driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
            time.sleep(random.uniform(1, 2))

            comment_container_selector = "div.tt-comment-cont"
            print(f"댓글 컨테이너({comment_container_selector})를 기다립니다...")
            comment_container = WebDriverWait(driver, 15).until(
                EC.presence_of_element_located((By.CSS_SELECTOR, comment_container_selector))
            )
            print("    ㄴ 댓글 컨테이너를 찾았습니다.")

            words = extract_post_text(driver)
            if not words:
                print("    ㄴ 본문 단어 추출에 실패하여 댓글 작성을 건너뜁니다.")
            else:
                comment_text = generate_random_comment(words)
                user_name, user_password = generate_random_credentials()
                
                name_input = comment_container.find_element(By.CSS_SELECTOR, "input[placeholder='이름']")
                password_input = comment_container.find_element(By.CSS_SELECTOR, "input[placeholder='비밀번호']")
                comment_textarea = comment_container.find_element(By.CSS_SELECTOR, "div.tt-cmt")

                for char in user_name: name_input.send_keys(char); time.sleep(random.uniform(0.05, 0.12))
                time.sleep(random.uniform(0.5, 1.0))
                for char in user_password: password_input.send_keys(char); time.sleep(random.uniform(0.05, 0.12))
                time.sleep(random.uniform(0.5, 1.0))
                for char in comment_text: comment_textarea.send_keys(char); time.sleep(random.uniform(0.06, 0.15))
                print("    ㄴ 이름, 비밀번호, 댓글 내용 입력을 완료했습니다.")

                submit_button = WebDriverWait(driver, 5).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "button.tt-btn_register")))
                driver.execute_script("arguments[0].click();", submit_button)
                print("    ㄴ 댓글 등록 버튼을 클릭했습니다.")
                
                # --- [핵심 수정] 댓글 등록 후 나타날 수 있는 alert를 처리하는 로직 ---
                try:
                    # alert가 나타날 때까지 최대 3초간 기다립니다.
                    WebDriverWait(driver, 3).until(EC.alert_is_present())
                    
                    # alert 객체로 제어권을 전환합니다.
                    alert = driver.switch_to.alert
                    
                    # alert의 텍스트를 가져와서 출력합니다. (디버깅에 유용)
                    alert_text = alert.text
                    print(f"    ㄴ 알림 발견! 내용: {alert_text}")
                    
                    # alert를 닫습니다. (확인 버튼 클릭)
                    alert.accept()
                    print("    ㄴ 알림을 성공적으로 닫았습니다.")
                    
                except TimeoutException:
                    # 3초 내에 alert가 나타나지 않으면, 별도 알림 없이 등록된 것으로 간주하고 계속 진행합니다.
                    print("    ㄴ 별도의 알림창 없이 댓글이 등록된 것으로 보입니다.")
                
                print("    ㄴ 성공! 댓글 등록 플로우를 완료했습니다.")

        except TimeoutException:
            print("    ㄴ 댓글 영역을 시간 내에 찾지 못했습니다. (지연 로딩 실패 또는 다른 문제)")
        except Exception as comment_e:
            # Alert 처리 로직이 있음에도 다른 Alert 관련 에러가 날 경우를 대비
            if isinstance(comment_e, UnexpectedAlertPresentException):
                 print(f"    ㄴ 예상치 못한 시점에 알림창이 나타났습니다: {comment_e.alert_text}")
                 # 이 경우에도 드라이버가 불안정해질 수 있으므로 루프를 종료하고 새로 시작하는 것이 안전합니다.
            else:
                 print(f"    ㄴ 댓글 작성 중 예상치 못한 오류 발생: {comment_e}")

        final_wait_time = random.uniform(3, 6)
        print(f"\n작업 완료! {final_wait_time:.2f}초 더 머문 뒤 브라우저를 닫습니다.")
        time.sleep(final_wait_time)

    except Exception as e:
        print(f"[전체 오류 발생] 작업 중 예기치 않은 오류가 발생했습니다: {e}")
        traceback.print_exc() # 오류의 상세 내용을 보기 위해 추가
        error_sleep_time = random.uniform(5, 10)
        print(f"오류로 인해 {error_sleep_time:.2f}초 대기합니다.")
        time.sleep(error_sleep_time)

    finally:
        if driver:
            print("브라우저를 종료합니다.")
            driver.quit()

    # 무한 루프의 각 시도 사이에 휴식
    break_time = random.uniform(10, 25)
    print(f"\n>>> 다음 작업을 위해 {break_time:.2f}초간 휴식합니다...")
    time.sleep(break_time)

 

 

1회반복

import time
import random
import string
import traceback
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.action_chains import ActionChains
from selenium.common.exceptions import NoSuchElementException, TimeoutException, UnexpectedAlertPresentException

# --- 헬퍼 함수 정의 ---
def human_like_mouse_move(driver, element):
    """사람처럼 보이게 마우스 커서를 해당 요소로 부드럽게 이동시킵니다."""
    actions = ActionChains(driver)
    actions.move_to_element(element).perform()
    print("    ㄴ 사람처럼 마우스 커서를 목표 지점으로 이동했습니다.")
    time.sleep(random.uniform(0.5, 1.0))

def extract_post_text(driver):
    """게시물 본문에서 텍스트를 추출하여 단어 리스트로 반환합니다."""
    try:
        wait = WebDriverWait(driver, 10)
        post_body = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, "div.contents_style")))
        text = post_body.text
        translator = str.maketrans(string.punctuation, ' ' * len(string.punctuation))
        words = text.translate(translator).split()
        return [word for word in words if len(word) > 1]
    except Exception:
        return []

def generate_random_comment(word_list):
    """추출된 단어 리스트를 기반으로 무작위 댓글을 생성합니다."""
    if not word_list:
        return "포스팅 잘 보고 갑니다. 좋은 하루 되세요."
    num_words = random.randint(4, 9)
    if len(word_list) < num_words:
        num_words = len(word_list)
    return " ".join(random.sample(word_list, num_words))

def generate_random_credentials():
    """무작위 이름과 비밀번호를 생성합니다."""
    name = ''.join(random.choices(string.ascii_lowercase, k=random.randint(5, 8)))
    password = ''.join(random.choices(string.ascii_letters + string.digits, k=random.randint(8, 12)))
    return name, password

# --- 메인 코드 ---
BASE_URL = "https://kimchangmin02.tistory.com"
TARGET_URLS = [f"{BASE_URL}/{i}" for i in range(1, 65)]
WAIT_TIMEOUT = 15
driver = None  # driver 변수를 try 블록 외부에서 초기화

print("="*60)
print(" TISTORY 자동화 봇 (1회 실행 버전) ")
print(f" 총 {len(TARGET_URLS)}개의 게시물 중 하나를 대상으로 작업을 시작합니다.")
print("="*60)

try:
    target_page = random.choice(TARGET_URLS)
    print(f"\n--- [ 작업 시작 ] ---")
    print(f"대상 URL: '{target_page}'")
    
    chrome_options = webdriver.ChromeOptions()
    chrome_options.add_argument("--start-maximized")
    chrome_options.add_experimental_option("excludeSwitches", ["enable-automation"])
    driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=chrome_options)
    wait = WebDriverWait(driver, WAIT_TIMEOUT)
    
    driver.get(target_page)
    print(f"페이지로 성공적으로 이동했습니다.")
    
    print("\n--- 1. 페이지 탐색 (스크롤) ---")
    time.sleep(random.uniform(1, 2))
    scroll_count = random.randint(2, 4)
    for j in range(scroll_count):
        scroll_depth = (j + 1) / (scroll_count + 1)
        driver.execute_script(f"window.scrollTo(0, document.body.scrollHeight * {scroll_depth});")
        time.sleep(random.uniform(1.0, 2.5))
    print("페이지 스크롤을 완료했습니다.")

    print("\n--- 2. '좋아요' 클릭 시도 ---")
    try:
        like_button = WebDriverWait(driver, 5).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "div[id^='reaction-'] button.btn_post")))
        driver.execute_script("arguments[0].scrollIntoView({block: 'center', behavior: 'smooth'});", like_button)
        time.sleep(random.uniform(1, 2))
        human_like_mouse_move(driver, like_button)
        like_button.click()
        print("    ㄴ 성공! '좋아요' 버튼을 클릭했습니다.")
    except TimeoutException:
        print("    ㄴ '좋아요' 버튼을 찾을 수 없거나 이미 비활성화 상태입니다.")
    
    # --- 3. 댓글 작성 시도 (알림창 처리 강화) ---
    print("\n--- 3. 댓글 작성 시도 ---")
    try:
        print("댓글창 로딩을 유도하기 위해 페이지 최하단으로 스크롤합니다.")
        driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
        time.sleep(random.uniform(1, 2))

        comment_container = WebDriverWait(driver, 15).until(
            EC.presence_of_element_located((By.CSS_SELECTOR, "div.tt-comment-cont"))
        )
        print("    ㄴ 댓글 컨테이너를 찾았습니다.")

        words = extract_post_text(driver)
        if not words:
            print("    ㄴ 본문 단어 추출에 실패하여 댓글 작성을 건너뜁니다.")
        else:
            comment_text = generate_random_comment(words)
            user_name, user_password = generate_random_credentials()
            
            name_input = comment_container.find_element(By.CSS_SELECTOR, "input[placeholder='이름']")
            password_input = comment_container.find_element(By.CSS_SELECTOR, "input[placeholder='비밀번호']")
            comment_textarea = comment_container.find_element(By.CSS_SELECTOR, "div.tt-cmt")

            for char in user_name: name_input.send_keys(char); time.sleep(random.uniform(0.05, 0.12))
            time.sleep(random.uniform(0.5, 1.0))
            for char in user_password: password_input.send_keys(char); time.sleep(random.uniform(0.05, 0.12))
            time.sleep(random.uniform(0.5, 1.0))
            for char in comment_text: comment_textarea.send_keys(char); time.sleep(random.uniform(0.06, 0.15))
            print("    ㄴ 이름, 비밀번호, 댓글 내용 입력을 완료했습니다.")

            submit_button = WebDriverWait(driver, 5).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "button.tt-btn_register")))
            driver.execute_script("arguments[0].click();", submit_button)
            print("    ㄴ 댓글 등록 버튼을 클릭했습니다.")
            
            # --- [핵심] 클릭 직후, 알림창을 기다리고 처리합니다. ---
            try:
                # 1. 알림창이 나타날 때까지 최대 3초간 기다립니다.
                WebDriverWait(driver, 3).until(EC.alert_is_present())
                
                # 2. 알림창으로 제어권을 전환하고 '확인' 버튼을 누릅니다.
                alert = driver.switch_to.alert
                alert_text = alert.text
                print(f"    ㄴ 알림 발견! 내용: {alert_text}")
                alert.accept() # <-- 이 부분이 '확인' 버튼을 누르는 동작입니다.
                print("    ㄴ 알림을 성공적으로 닫았습니다.")
                
            except TimeoutException:
                # 3. 정해진 시간 내에 알림창이 안 뜨면, 정상 등록으로 간주합니다.
                print("    ㄴ 별도의 알림창 없이 댓글이 등록된 것으로 보입니다.")
            
            print("    ㄴ 성공! 댓글 등록 플로우를 완료했습니다.")

    except UnexpectedAlertPresentException as e:
        # ★★★ 예상치 못한 타이밍에 알림창이 떴을 때를 위한 최종 방어 코드 ★★★
        print(f"    ㄴ [경고] 예상치 못한 시점에 알림창이 나타나 강제로 처리합니다!")
        print(f"    ㄴ 알림 내용: {e.alert_text}")
        try:
            alert = driver.switch_to.alert
            alert.accept() # '확인' 버튼 클릭
            print("    ㄴ 비상 로직으로 알림을 성공적으로 닫았습니다.")
        except Exception as alert_err:
            print(f"    ㄴ 비상 알림 처리 중 또 다른 오류 발생: {alert_err}")

    except TimeoutException:
        print("    ㄴ 댓글 영역을 시간 내에 찾지 못했습니다.")
    except Exception as comment_e:
         print(f"    ㄴ 댓글 작성 중 예상치 못한 오류 발생: {comment_e}")

    final_wait_time = random.uniform(3, 6)
    print(f"\n작업 완료! {final_wait_time:.2f}초 더 머문 뒤 브라우저를 닫습니다.")
    time.sleep(final_wait_time)

except Exception as e:
    print(f"\n[전체 스크립트 오류 발생] 작업 중 예기치 않은 오류가 발생했습니다: {e}")
    traceback.print_exc()

finally:
    if driver:
        print("브라우저를 종료합니다.")
        driver.quit()
    print("\n--- 모든 작업이 종료되었습니다. ---")

 

 

 

 

다른 게시물로도 이동하는 코드

 

import time
import random
import string
import traceback
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.action_chains import ActionChains
from selenium.common.exceptions import NoSuchElementException, TimeoutException

# --- 헬퍼 함수 정의 (기존과 거의 동일) ---
def human_like_mouse_move(driver, element):
    """사람처럼 마우스 커서를 부드럽게 이동시키는 함수"""
    actions = ActionChains(driver)
    actions.move_to_element(element).perform()
    print("      ㄴ 사람처럼 마우스 커서를 목표 지점으로 이동...")
    time.sleep(random.uniform(0.5, 1.0))

def extract_post_text(driver):
    """게시물 본문에서 단어를 추출하는 함수"""
    try:
        wait = WebDriverWait(driver, 10)
        post_body = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, "div.contents_style")))
        text = post_body.text
        translator = str.maketrans(string.punctuation, ' ' * len(string.punctuation))
        words = text.translate(translator).split()
        return [word for word in words if len(word) > 1]
    except Exception:
        return []

def generate_random_comment(word_list):
    """추출된 단어로 무작위 댓글을 생성하는 함수"""
    if not word_list: return "포스팅 잘 보고 갑니다. 좋은 하루 되세요."
    num_words = random.randint(4, 9)
    if len(word_list) < num_words: num_words = len(word_list)
    return " ".join(random.sample(word_list, num_words))

def generate_random_credentials():
    """무작위 이름과 비밀번호를 생성하는 함수"""
    name = ''.join(random.choices(string.ascii_lowercase, k=random.randint(5, 8)))
    password = ''.join(random.choices(string.ascii_letters + string.digits, k=random.randint(8, 12)))
    return name, password

# --- [신규] 행동 수행 함수 ---
def perform_like_action(driver, wait):
    """'좋아요'를 누르는 행동을 수행하는 함수"""
    print("    -> '좋아요' 작업 시도...")
    # 80% 확률로 '좋아요'를 누름
    if random.random() < 0.8:
        print("      ㄴ 80% 확률 통과! '좋아요'를 누릅니다.")
        try:
            like_button = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, "div[id^='reaction-'] button.btn_post")))
            driver.execute_script("arguments[0].scrollIntoView({block: 'center', behavior: 'smooth'});", like_button)
            time.sleep(random.uniform(1, 2))
            human_like_mouse_move(driver, like_button)
            like_button.click()
            print("      ㄴ 성공! '좋아요' 버튼을 클릭했습니다.")
        except TimeoutException:
            print("      ㄴ '좋아요' 버튼을 찾을 수 없거나 이미 비활성화 상태입니다.")
    else:
        print("      ㄴ 80% 확률 미달... 이번에는 '좋아요'를 누르지 않고 건너뜁니다.")

def perform_comment_action(driver, wait):
    """'댓글'을 작성하는 행동을 수행하는 함수"""
    print("    -> '댓글' 작업 시도...")
    # 70% 확률로 댓글을 작성함
    if random.random() < 0.7:
        print("      ㄴ 70% 확률 통과! '댓글'을 작성합니다.")
        try:
            print("      ㄴ 댓글창 로딩을 위해 페이지 최하단으로 스크롤합니다.")
            driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
            time.sleep(random.uniform(1, 2))

            comment_container = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, "div.tt-comment-cont")))

            words = extract_post_text(driver)
            if not words:
                print("      ㄴ 본문 단어 추출에 실패하여 댓글 작성을 건너뜁니다.")
                return

            comment_text = generate_random_comment(words)
            user_name, user_password = generate_random_credentials()
            
            name_input = comment_container.find_element(By.CSS_SELECTOR, "input[placeholder='이름']")
            password_input = comment_container.find_element(By.CSS_SELECTOR, "input[placeholder='비밀번호']")
            comment_textarea = comment_container.find_element(By.CSS_SELECTOR, "div.tt-cmt")

            # 인간적인 타이핑
            for char in user_name: name_input.send_keys(char); time.sleep(random.uniform(0.05, 0.12))
            time.sleep(random.uniform(0.5, 1.0))
            for char in user_password: password_input.send_keys(char); time.sleep(random.uniform(0.05, 0.12))
            time.sleep(random.uniform(0.5, 1.0))
            for char in comment_text: comment_textarea.send_keys(char); time.sleep(random.uniform(0.06, 0.15))
            print("      ㄴ 이름, 비밀번호, 댓글 내용 입력을 완료했습니다.")

            submit_button = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, "button.tt-btn_register")))
            driver.execute_script("arguments[0].click();", submit_button)
            print("      ㄴ 성공! 댓글을 성공적으로 등록했습니다.")
        except TimeoutException:
            print("      ㄴ 댓글 영역을 시간 내에 찾지 못했습니다.")
        except Exception as comment_e:
            print(f"      ㄴ 댓글 작성 중 예상치 못한 오류 발생: {comment_e}")
    else:
        print("      ㄴ 70% 확률 미달... 이번에는 '댓글'을 작성하지 않고 건너뜁니다.")


# --- 메인 코드 ---
BASE_URL = "https://kimchangmin02.tistory.com"
TARGET_URLS = [f"{BASE_URL}/{i}" for i in range(1, 60)]
WAIT_TIMEOUT = 15
session_count = 0

print("="*60)
print(" TISTORY 자동화 봇 (지능형 반복 버전) ")
print(f" 총 {len(TARGET_URLS)}개의 게시물을 대상으로 작업을 무한 반복합니다.")
print(" 프로그램을 종료하려면 이 창에서 Ctrl + C 를 누르세요.")
print("="*60)

while True:
    driver = None
    session_count += 1
    try:
        print(f"\n--- [ {session_count} 번째 세션 시작 ] ---")
        
        # --- 1. 브라우저 환경 설정 고도화 ---
        chrome_options = webdriver.ChromeOptions()
        # User-Agent 설정 (실제 브라우저 값으로 위장)
        user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36"
        chrome_options.add_argument(f"user-agent={user_agent}")
        # 자동화 탐지 플래그 비활성화 (필수)
        chrome_options.add_experimental_option("excludeSwitches", ["enable-automation"])
        chrome_options.add_experimental_option('useAutomationExtension', False)
        chrome_options.add_argument("--disable-blink-features=AutomationControlled")
        
        driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=chrome_options)
        wait = WebDriverWait(driver, WAIT_TIMEOUT)
        
        # --- 2. 한 세션 내에서 여러 게시물 방문 ---
        num_posts_to_visit = random.randint(1, 5)
        print(f"  이번 세션에서는 총 {num_posts_to_visit}개의 게시물을 방문합니다.")
        
        # 중복 없이 방문할 게시물 목록 선택
        if len(TARGET_URLS) < num_posts_to_visit:
            posts_to_visit = TARGET_URLS
        else:
            posts_to_visit = random.sample(TARGET_URLS, num_posts_to_visit)

        for i, target_page in enumerate(posts_to_visit):
            print(f"\n  ㄴ [게시물 {i+1}/{num_posts_to_visit}] 작업 시작: {target_page}")
            driver.get(target_page)
            print(f"    페이지로 성공적으로 이동했습니다.")
            
            # 페이지 탐색 (스크롤)
            time.sleep(random.uniform(1, 3))
            for _ in range(random.randint(2, 4)):
                scroll_depth = random.uniform(0.2, 1.0)
                driver.execute_script(f"window.scrollTo(0, document.body.scrollHeight * {scroll_depth});")
                time.sleep(random.uniform(1.0, 2.5))
            print("    페이지 스크롤을 완료했습니다.")

            # --- 3. 행동 패턴 무작위화 ---
            action_plan = random.choice(['like_only', 'comment_only', 'like_then_comment', 'comment_then_like'])
            print(f"    이번 게시물의 행동 패턴: '{action_plan}'")
            
            action_plan ='comment_only'
            #좋아요 기능이 차단된 관계로 댓글 기능만 활성화
            
            if action_plan == 'like_only':
                perform_like_action(driver, wait)
            elif action_plan == 'comment_only':
                perform_comment_action(driver, wait)
            elif action_plan == 'like_then_comment':
                perform_like_action(driver, wait)
                time.sleep(random.uniform(1, 3)) # 행동 사이의 텀
                perform_comment_action(driver, wait)
            elif action_plan == 'comment_then_like':
                perform_comment_action(driver, wait)
                time.sleep(random.uniform(1, 3)) # 행동 사이의 텀
                perform_like_action(driver, wait)
            
            print(f"  ㄴ [게시물 {i+1}/{num_posts_to_visit}] 작업 완료")
            # 다음 게시물 방문 전 짧은 휴식
            if i < len(posts_to_visit) - 1:
                time.sleep(random.uniform(3, 7))

    except Exception as e:
        print(f"[전체 세션 오류 발생] 작업 중 예기치 않은 오류가 발생했습니다: {e}")
        traceback.print_exc()
        time.sleep(random.uniform(5, 10))

    finally:
        if driver:
            print("\n세션 종료! 브라우저를 닫습니다.")
            driver.quit()

    # 다음 세션을 시작하기 전 긴 휴식>일단 짧은 휴식으로 하게 할까
    break_time = random.uniform(10,25)
    print(f"\n>>> 다음 세션을 위해 {break_time:.2f}초간 휴식합니다...")
    time.sleep(break_time)

 

 

네이버 이동+게시물은 url로

근데 댓글이 아직 안됨(해결안됨)

import time
import random
import string
import traceback
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
from selenium.common.exceptions import TimeoutException, UnexpectedAlertPresentException
import pyautogui
from bs4 import BeautifulSoup

# --- 설정 ---
SEARCH_QUERY = "site:kimchangmin02.tistory.com"
BLOG_URL_IDENTIFIER = "kimchangmin02.tistory.com"
BASE_URL = f"https://{BLOG_URL_IDENTIFIER}"
TARGET_URLS = [f"{BASE_URL}/{i}" for i in range(1, 75)]
WAIT_TIMEOUT = 15
loop_count = 0

# --- 헬퍼 함수 ---
def extract_post_text(driver):
    try:
        wait = WebDriverWait(driver, 10)
        post_body = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, "div.contents_style")))
        text = post_body.text
        translator = str.maketrans(string.punctuation, ' ' * len(string.punctuation))
        words = text.translate(translator).split()
        return [word for word in words if len(word) > 1]
    except Exception:
        print("    ㄴ [경고] 본문 내용을 추출하지 못했습니다.")
        return []

def generate_random_comment(word_list):
    if not word_list:
        return "포스팅 잘 보고 갑니다. 좋은 하루 되세요."
    num_words = random.randint(5, 10)
    if len(word_list) < num_words:
        num_words = len(word_list)
    return " ".join(random.sample(word_list, num_words))

def generate_random_credentials():
    name = ''.join(random.choices(string.ascii_lowercase, k=random.randint(5, 8)))
    password = ''.join(random.choices(string.ascii_letters + string.digits, k=random.randint(8, 12)))
    return name, password

# --- 메인 코드 ---
print("="*60)
print(" TISTORY 자동화 봇 (네이버 유입 + 무한 반복 최종 버전) ")
print(f" 총 {len(TARGET_URLS)}개의 게시물을 대상으로 작업을 무한 반복합니다.")
print(" 프로그램을 종료하려면 이 창에서 Ctrl + C 를 누르세요.")
print("="*60)

while True:
    driver = None
    loop_count += 1
    try:
        print(f"\n--- [ {loop_count} 번째 시도 시작 ] ---")

        # --- [단계 1] 네이버 검색으로 블로그 유입 ---
        print("\n--- [단계 1] 네이버 검색으로 블로그 유입 시도 ---")
        options = webdriver.ChromeOptions()
        options.add_argument("user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36")
        options.add_experimental_option("excludeSwitches", ["enable-automation"])
        driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=options)
        driver.maximize_window()
        wait = WebDriverWait(driver, WAIT_TIMEOUT)
        
        driver.get('https://www.naver.com')
        print("    ㄴ 네이버 접속 완료.")
        search_box = wait.until(EC.element_to_be_clickable((By.NAME, 'query')))
        search_box.click()
        pyautogui.typewrite(SEARCH_QUERY, interval=random.uniform(0.1, 0.2))
        pyautogui.press('enter')
        print(f"    ㄴ 검색어 '{SEARCH_QUERY}' 입력 및 검색 실행 완료.")
        time.sleep(random.uniform(2, 4))
        
        html_source = driver.page_source
        soup = BeautifulSoup(html_source, 'html.parser')
        blog_links = soup.find_all('a', href=lambda href: href and href.startswith(f'https://{BLOG_URL_IDENTIFIER}'))
        
        if not blog_links:
            raise Exception("HTML 분석 결과, 네이버 검색 페이지에서 블로그 링크를 찾지 못했습니다.")

        target_link_url = blog_links[0]['href']
        print(f"    ㄴ [성공] 블로그 링크 주소({target_link_url})를 찾았습니다.")
        
        original_window = driver.current_window_handle
        element_to_click = wait.until(EC.element_to_be_clickable((By.XPATH, f"//a[@href='{target_link_url}']")))
        element_to_click.click()
        
        wait.until(EC.number_of_windows_to_be(2))
        for window_handle in driver.window_handles:
            if window_handle != original_window:
                driver.switch_to.window(window_handle)
                break
        
        print(f"    ㄴ 네이버를 통한 블로그 유입에 성공했습니다. (현재 URL: {driver.current_url})")
        
        # --- [단계 2] Tistory 게시물 작업 수행 ---
        target_page = random.choice(TARGET_URLS)
        print(f"\n--- [단계 2] Tistory 특정 게시물 작업 시작 ---")
        print(f"    ㄴ 목표 게시물: {target_page}")
        driver.get(target_page)
        print(f"    ㄴ 목표 게시물로 이동 완료.")

        print("\n--- [단계 2-1] 페이지 탐색 ---")
        time.sleep(random.uniform(1, 2))
        driver.execute_script("window.scrollTo(0, document.body.scrollHeight * 0.5);")
        time.sleep(random.uniform(1, 2))
        print("    ㄴ 페이지 스크롤 완료.")

        print("\n--- [단계 2-2] 댓글 작성 시도 ---")
        try:
            # 댓글 작성을 위한 단어는 iframe 전환 전에 미리 추출해야 함
            words = extract_post_text(driver)
            comment_text = generate_random_comment(words)
            user_name, user_password = generate_random_credentials()

            print("    ㄴ 댓글창 로딩을 위해 페이지 최하단으로 스크롤합니다.")
            driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
            time.sleep(random.uniform(1, 2))

            # [핵심 수정] 댓글 iframe으로 제어권 전환
            print("    ㄴ 댓글 iframe을 찾고 제어권을 전환합니다...")
            comment_iframe = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, "iframe[title='댓글']")))
            driver.switch_to.frame(comment_iframe)
            print("    ㄴ iframe 전환 성공!")

            # 이제 iframe 내부의 요소를 찾음
            comment_textarea = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, "textarea[name='comment']")))
            name_input = driver.find_element(By.CSS_SELECTOR, "input[placeholder='이름']")
            password_input = driver.find_element(By.CSS_SELECTOR, "input[placeholder='비밀번호']")

            name_input.click(); time.sleep(0.2); name_input.send_keys(user_name)
            password_input.click(); time.sleep(0.2); password_input.send_keys(user_password)
            comment_textarea.click(); time.sleep(0.2); comment_textarea.send_keys(comment_text)
            
            print("    ㄴ 이름, 비밀번호, 댓글 내용 입력을 완료했습니다.")
            print(f"    ㄴ 작성된 댓글: {comment_text}")

            submit_button = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, "button.tt-btn_register")))
            submit_button.click()
            print("    ㄴ 댓글 등록 버튼 클릭 완료.")
            
            # [핵심 수정] 제어권을 다시 메인 페이지로 돌려놓고 alert를 처리
            driver.switch_to.default_content()
            
            try:
                WebDriverWait(driver, 3).until(EC.alert_is_present())
                alert = driver.switch_to.alert
                print(f"    ㄴ 알림 발견! 내용: {alert.text}")
                alert.accept()
                print("    ㄴ 알림을 성공적으로 닫았습니다.")
            except TimeoutException:
                print("    ㄴ 별도의 알림창 없이 댓글이 등록된 것으로 보입니다.")
            
            print("    ㄴ [성공] 댓글 등록 플로우를 완료했습니다.")

        except TimeoutException:
            print("    ㄴ [오류] 댓글 관련 요소(iframe, 입력창 등)를 시간 내에 찾지 못했습니다.")
        except Exception as comment_e:
            print(f"    ㄴ [오류] 댓글 작성 중 예상치 못한 오류 발생: {comment_e}")

        final_wait_time = random.uniform(3, 6)
        print(f"\n작업 완료! {final_wait_time:.2f}초 더 머문 뒤 브라우저를 닫습니다.")
        time.sleep(final_wait_time)

    except Exception as e:
        print(f"\n[전체 프로세스 오류] 작업 중 예기치 않은 오류가 발생했습니다.")
        traceback.print_exc()
        error_sleep_time = random.uniform(5, 10)
        print(f"오류로 인해 {error_sleep_time:.2f}초 대기 후 다음 작업을 시도합니다.")
        time.sleep(error_sleep_time)

    finally:
        if driver:
            print("브라우저를 종료합니다.")
            driver.quit()

    break_time = random.uniform(10, 25)
    print(f"\n>>> 모든 작업을 마쳤습니다. 다음 시도를 위해 {break_time:.2f}초간 휴식합니다...")
    time.sleep(break_time)

 

 

 

댓글은 못달지만, 방문은 하는 코드 

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from webdriver_manager.chrome import ChromeDriverManager
import time
import random
import pyautogui
from bs4 import BeautifulSoup # HTML 분석을 위한 라이브러리

# --- 설정 ---
SEARCH_QUERY = "site:kimchangmin02.tistory.com"
BLOG_URL_IDENTIFIER = "kimchangmin02.tistory.com" # 링크가 이 주소로 시작하는지 검사

# --- 헬퍼 함수 (이전과 동일) ---
def get_absolute_coordinates(driver, element):
    """요소의 모니터 기준 절대 좌표를 계산하는 함수"""
    script = """
    const rect = arguments[0].getBoundingClientRect();
    return [
        Math.round(rect.left + window.screenX), 
        Math.round(rect.top + window.screenY)
    ];
    """
    return driver.execute_script(script, element)

def human_like_move_and_click(driver, element):
    """사람처럼 마우스를 움직여 클릭하는 함수"""
    driver.execute_script("arguments[0].scrollIntoView({block: 'center'});", element)
    time.sleep(0.5)

    abs_x, abs_y = get_absolute_coordinates(driver, element)
    if abs_x == 0 and abs_y == 0 and not element.is_displayed():
        print("!!! 경고: 요소의 좌표를 얻지 못했거나 화면에 보이지 않습니다. JavaScript 클릭으로 대체합니다.")
        driver.execute_script("arguments[0].click();", element)
        return

    size = element.size
    target_x = abs_x + (size['width'] / 2) + random.uniform(-size['width']/4, size['width']/4)
    target_y = abs_y + (size['height'] / 2) + random.uniform(-size['height']/4, size['height']/4)
    
    print(f"-> 사람처럼 마우스를 (모니터 기준 {int(target_x)}, {int(target_y)}) 좌표로 이동합니다.")
    pyautogui.moveTo(target_x, target_y, duration=random.uniform(0.5, 1.2))
    
    print("-> Selenium으로 안전하게 클릭하여 포커스를 확보합니다.")
    element.click()


# --- 메인 로직 ---
loop_count = 0
while True: # ⬅️ 무한 반복을 위해 for문에서 while True로 변경
    loop_count += 1
    print(f"\n--- [ {loop_count} 번째 네이버 자동화 작업을 시작합니다 ] ---")
    driver = None
    try:
        # --- 드라이버 설정 (이전과 동일) ---
        options = webdriver.ChromeOptions()
        options.add_argument("user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36")
        options.add_experimental_option("excludeSwitches", ["enable-automation"])
        options.add_experimental_option('useAutomationExtension', False)
        
        driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=options)
        driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", { "source": "Object.defineProperty(navigator, 'webdriver', {get: () => undefined})" })
        driver.maximize_window()
        driver.implicitly_wait(10)

        # --- 네이버 검색 (이전과 동일) ---
        driver.get('https://www.naver.com')
        print("네이버에 접속했습니다.")
        time.sleep(random.uniform(1.5, 2.5))

        search_box = driver.find_element(By.NAME, 'query')
        search_box.click()
        time.sleep(random.uniform(0.5, 1.0))

        pyautogui.typewrite(SEARCH_QUERY, interval=random.uniform(0.1, 0.25))
        pyautogui.press('enter')
        print("검색을 실행했습니다.")
        time.sleep(random.uniform(3, 5))
        
        # 1. HTML 소스 가져와서 분석
        print("현재 페이지의 전체 HTML 소스를 가져옵니다.")
        html_source = driver.page_source
        
        print("BeautifulSoup을 사용하여 HTML을 분석합니다.")
        soup = BeautifulSoup(html_source, 'html.parser')
        
        # href 속성이 'https://내블로그주소' 또는 'http://내블로그주소'로 '시작'하는 <a> 태그만 찾습니다.
        print("링크 주소가 실제 블로그 주소로 '시작'하는 링크만 정교하게 필터링합니다...")
        blog_links = soup.find_all('a', href=lambda href: href and (href.startswith('https://' + BLOG_URL_IDENTIFIER) or href.startswith('http://' + BLOG_URL_IDENTIFIER)))

        if blog_links:
            # 2. 찾은 '진짜' 링크의 URL을 가져옵니다.
            target_link_url = blog_links[0]['href']
            print(f"성공! 실제 블로그 링크 주소를 찾았습니다: {target_link_url}")
            
            try:
                # 3. 해당 URL을 가진 요소를 페이지에서 다시 찾아 클릭합니다.
                print("찾아낸 링크 주소를 이용해 페이지에서 클릭할 요소를 정확히 다시 찾습니다.")
                element_to_click = driver.find_element(By.XPATH, f"//a[@href='{target_link_url}']")

                print("사람처럼 움직여서 요소를 클릭합니다.")
                human_like_move_and_click(driver, element_to_click)
                
                time.sleep(3)
                print(f"블로그 방문 성공! 현재 URL: {driver.current_url}")

            except Exception as e:
                print(f"!!! HTML에서 링크는 찾았지만, 페이지에서 해당 요소를 클릭하는 데 실패했습니다: {e}")
                
        else:
            print(f"페이지 HTML 소스에서 '{BLOG_URL_IDENTIFIER}'로 시작하는 실제 블로그 링크를 찾지 못했습니다.")
            
    except Exception as e:
        print(f"!!! 작업 중 오류가 발생했습니다: {e}")

    finally:
        if driver:
            print("5초 후에 브라우저를 닫습니다.")
            time.sleep(5)
            driver.quit()
    
    # ⬅️ 각 반복이 끝난 후, 다음 작업을 시작하기 전에 랜덤 시간 동안 대기
    try:
        sleep_duration = random.uniform(1, 10)
        print(f"\n--- [ 다음 작업을 위해 {sleep_duration:.2f}초 동안 대기합니다. ] ---")
        time.sleep(sleep_duration)
    except KeyboardInterrupt:
        print("\n사용자에 의해 프로그램이 중단되었습니다.")
        break

print("\n--- [ 모든 자동화 작업이 종료되었습니다 ] ---")