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--- [ 모든 자동화 작업이 종료되었습니다 ] ---")'개발 > 자동화' 카테고리의 다른 글
| 자동화)왜 조회수는 그대로(10.24) (0) | 2025.10.24 |
|---|---|
| 자동화)폰으로 자동화 돌릴려면 (10.24) (0) | 2025.10.24 |
| [티스토리 좋아요 댓글 자동화] 구글 검색으로 티스토리 들어가게 하기(미완) #12 (13) | 2025.07.30 |
| [티스토리 좋아요 댓글 자동화] 더 사람처럼(코드)#11 (13) | 2025.07.27 |
| [티스토리 좋아요 댓글 자동화] 더 사람처럼 (이론) #10 (15) | 2025.07.27 |