728x90

 

1. 배경

티스토리 스토리 섹션에서 댓글을 다는 도중 자동화하면 좋을 것 같아서 도전해보았다. 티스토리 댓글을 자동으로 달아주는 봇을 만들면 편할 것 같아서 selenium으로 Python 봇을 만들기로 했다.

2. 코드

python 크롤링할 때 많이 쓰이는 selenium을 사용했고, 크롬 웹 드라이버를 사용했다. 아이디 비밀번호 입력할 때는 pyperclip으로 클립보드에 복사하여 입력하는 방식을 사용했다. (코드 설명은 아래 주석 참고하자)

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

# 크롬 드라이버 자동 업데이트
from webdriver_manager.chrome import ChromeDriverManager

import pyperclip
import time
import datetime
import openpyxl

# 클립보드에 input을 복사한 뒤
# 해당 내용을 actionChain을 이용해 로그인 폼에 붙여넣기
def copy_input(xpath, input):
    # 해당 element가 있을 때까지 대기
    isActivated(xpath)
    while True:
        try:
            pyperclip.copy(input)
            driver.find_element(By.XPATH, xpath).click()
            ActionChains(driver).key_down(Keys.COMMAND).send_keys('v').key_up(Keys.COMMAND).perform()
            return
        except:
            time.sleep(0.2)
            continue

# 존재하면 클릭
def existClick(xpath):
    isActivated(xpath)
    driver.find_element(By.XPATH, xpath).click()

# 존재하면 가져옴
def existGetElement(xpath):
    isActivated(xpath)
    return driver.find_element(By.XPATH, xpath)
    
# 존재하면 종료
def isActivated(xpath):
    while True:
        try:
            target = driver.find_element(By.XPATH, xpath)
            if not target:
                time.sleep(0.2)
            else:
                break
        except Exception as e:
            print(e)
            time.sleep(0.2)
            continue

# 티스토리 로그인
def tistoryLogin(userid, pw):
    print('tistoryLogin', userid, pw)
    # 카카오 로그인 창 띄우기
    driver.get('https://www.tistory.com/auth/login')
    # 카카오 계정 로그인
    existClick('//*[@id="cMain"]/div/div/div/a[1]')
    # 아이디 입력
    copy_input('//*[@id="input-loginKey"]', userid)
    # 비밀번호 입력
    copy_input('//*[@id="input-password"]', pw)
    # 로그인 버튼 클릭
    existClick('//*[@id="mainContent"]/div/div/form/div[4]/button[1]')
    # 스토리 접속
    existClick('//*[@id="kakaoGnb"]/ul/li[2]/a')

# 댓글 달기
def reply():
    # 라이프
    existClick('//*[@id="mArticle"]/div[1]/ul/li[1]/a/span')
    time.sleep(3)
    driver.execute_script("window.scrollTo(0, 2000)")
    time.sleep(3)
    driver.execute_script("window.scrollTo(0, 2000)")
    time.sleep(3)
    # 첫 번째 글
    existClick('//*[@id="mArticle"]/div[3]/ul/li[2]')
    # 댓글 달기
    time.sleep(3)
    driver.execute_script("window.scrollTo(0, 2000)")
    time.sleep(3)
    driver.execute_script("window.scrollTo(0, 2000)")
    time.sleep(3)

    copy_input('//*[@id="comment"]', '글 솜씨가 좋으시네요! 잘 보고 갑니다~')
    existClick('//*[@id="entry60Comment"]/div/form/div/div[2]/button')
    
# 브라우저 꺼짐 방지
chrome_options = Options()
chrome_options.add_experimental_option("detach", True)

# 불필요한 에러 메시지 없애기
chrome_options.add_experimental_option("excludeSwitches", ["enable-logging"])

service = Service(executable_path=ChromeDriverManager().install())
driver = webdriver.Chrome(service=service, options=chrome_options)

driver.set_window_position(0, 0)
driver.set_window_size(1600, 1000)

# 티스토리 로그인 아이디, 비밀번호
id = 'id'
pw = 'pw'

tistoryLogin(id, pw)

reply()

XPath로 html element를 찾고나서 클릭하거나 텍스트를 입력해야 한다. element가 로딩되고 있는 시점에는 element를 찾지 못하는 오류가 발생하기 때문이다. 봇이 클릭이나 텍스트 입력 시, isActivated() 함수를 통해 해당 element가 존재하는지 0.2초 마다 검사하도록 했다. 해당 element를 발견하면 다음 동작을 실행시키도록 구현했다.

티스토리 로그인 및 게시글 이동까지는 성공했으나 댓글 달기 시, 여러 방해물이 존재했다.

 

3. 한계

티스토리에서 크롬 웹 드라이버를 이용한 댓글 자동으로 다는 Python 봇을 막으려고 여러가지 장치를 해놓아서 다른 방법을 찾아봐야 할 것 같다. 이유는 아래와 같다.

첫째로, 크롬 웹 드라이버로 게시글에 들어가서 댓글을 달고자 할 때 랜덤으로 생성된 매크로 방지문자 6자리를 입력해야 해서 자동화가 불가능하다.

둘째로, html element를 찾을 때 element의 id 정보를 포함한 XPath를 사용하는데, id가 entry60Comment, entry210Comment 이런식으로 게시글, 블로그마다 불규칙적이여서 기준을 잡기가 불가능하다.

 

4. 소감

티스토리 댓글 봇은 현재 어렵지만, 다른 방법이 있는지 고민해봐야겠다.

element의 id나 XPath가 규칙적이거나 자동입력방지문자가 없는 사이트에 대해서 데이터 크롤링, 자동 매크로를 사용할 때 요긴하게 쓰일 것 같다.

+ Recent posts