최근 티스토리가 터진 후 네이버 블로그에 관심이 옮겨가면서, 네이버 블로그의 좋아요도 한번 자동화 시키면 어떨까 하는 생각이 들었다. 검색을 해보니 네이버는 체류시간이 너무 짧을 경우 봇으로 인식해서 무의미한 클릭으로 받아들이며 블로그 순위가 낮아진다고 한다.
그러니 이 글은 블로그를 운영하는 보조적인 용도로만 쓰길 바란다. 스크롤의 양에 따라 시간을 조금 조절하도록 코드를 한번 구성해보았다. 스크롤의 속도가 느리다면 상단의 변수를 통해 스스로 조절하면 된다. 이 글을 읽기 전에 파이썬과 셀레니움 환경이 구성되지 않았다면 아래에 있는 글을 참고하길 바란다.
2023.08.08. 업데이트
1. 어차피 좋아요만 누를 예정이니, 애초에 좋아요가 눌러지지 않은 게시물을 선택하도록 xpath변경.
2. 스크롤 함수 수정. 전체 페이지 높이의 90%까지 스크롤 후 멈추도록 설정.
3. 변수명 일부 변경.
2023.10.29. 업데이트
total_wrap --> title_area 로 xpath 변경. xpath를 수정해주신 짱고아빠님 감사합니다^^
2023.11.13. 업데이트
selenium 업데이트에 따른 driverPath 삭제
1. 네이버 로그인 하기
셀레니움이 업데이트 되면서 드라이버를 따로 다운받을 필요가 없어졌다.
먼저 기본적으로 필요한 모듈들을 호출해보자. driverFolder는 자신의 환경에 맞도록 경로를 지정해주자. 나는 크롬 드라이버를 c:\coding\selenium 폴더 안에 넣어놨기에 아래처럼 경로를 지정해 보았다.
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
import pyperclip as pp
import time
import random
import os
options = webdriver.ChromeOptions()
options.add_experimental_option('excludeSwitches', ['enable-logging'])
options.add_argument('--no-sandbox')
options.add_argument("disable-gpu")
# options.add_argument('headless')
options.add_argument('window-size=1920x1080')
options.add_argument("user-agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36")
driver = webdriver.Chrome(options=options)
네이버에 접속하기 위해서 데스크톱 사이트를 활용할 지, 모바일 사이트를 활용할 지 고민해보았다. 아래 그림을 보면 모바일 사이트가 클릭할 것들이 훨씬 적어보였다. 그래서 모바일 사이트를 사용하기로 했다.
크롬 드라이버로 모바일용 네이버 로그인 사이트를 호출한다. 그리고 플레이스 홀드(아무것도 안넣었을 때 뜨는 회색 글자)를 이용하여 내가 원하는 내용을 복사 붙여넣기로 입력하는 함수를 만들어 보았다. 이런식으로 입력하지 않으면 프로그램을 인식한다고... 네이버 캡챠는 상당히 어려워서 풀기가 불가능했다. 그래서 이런 방법으로 우회해 보았다.
driver.get("https://nid.naver.com/nidlogin.login?svctype=262144&url=http://m.naver.com/aside/")
yourid = "내 아이디 입력"
yourpassword = "내 패스워드 입력"
def inputkeys(myId, placeholder):
pp.copy(myId)
idInput = driver.find_element(By.XPATH, f"//input[@placeholder='{placeholder}']")
idInput.click()
pp.copy(myId)
time.sleep(random.uniform(3.0, 10.0))
idInput.send_keys(Keys.CONTROL, 'v')
inputkeys(yourid, "아이디")
inputkeys(yourpassword, "비밀번호")
driver.find_element(By.XPATH, f"//input[@placeholder='비밀번호']").send_keys(Keys.ENTER)
time.sleep(2)
그리고 마지막으로 send_keys와 Keys로 엔터를 입력해주면 로그인이 끝난다. 이제 검색을 통해 블로그를 찾아보자.
2. 블로그 검색 및 url 추출
이제 내가 누르고자 하는 블로그를 검색하고 url을 추출, 그리고 접속을 하면 된다. 일단 '카페'라는 검색어를 통해 블로그를 검색하고, 검색한 웹사이트의 출처를 블로그, 정렬을 최신순으로 정렬했다.
이런 옵션을 가졌을 때 웹사이트의 주소가 아래와 같았다. 이를 이용해 원하는 검색어에 옵션을 붙여 빠르게 접근할 수 있다.
searchWord = "내가 원하는 검색어"
adress = "https://m.search.naver.com/search.naver?where=m_blog&query="+searchWord+"&nso=so%3Add%2Cp%3Aall"
driver.get(adress)
모바일버젼에서 블로그를 검색하니 화면 끝까지 스크롤을 할 때마다 새로운 블로그들을 로딩해주었다.
찾아보니 블로그 글이 total_wrap(현재 title_area로 수정)이라는 클래스를 가진 div로 하나씩 감싸져 있었다. 이 요소들을 articles라는 리스트로 담고 len을 활용해 크기를 쟀다. 이것이 articleLimit이라는 변수보다 더 클 때까지 스크롤을 하도록 코드를 짜보았다.
articleLimit = 20
articles = driver.find_elements(By.XPATH, "//div[@class='title_area']/a")
numOfArticles = len(articles)
SCROLL_PAUSE_TIME = 0.5
while numOfArticles < articleLimit :
articles = driver.find_elements(By.XPATH, "//div[@class='title_area']/a")
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
time.sleep(SCROLL_PAUSE_TIME)
numOfArticles = len(driver.find_elements(By.XPATH, "//div[@class='title_area']/a"))
스크롤이 모두 끝난 뒤에는 get_attribute를 활용하여 articles의 첫번째 글의 url을 str 형태로 추출한다. url은 <a>태그 안에 href라는 속성 속에 들어있었다. 그리고 이를 새창에서 열고 새창으로 전환해 준다.
url = str(articles[0].get_attribute("href"))
driver.execute_script(f"window.open('{url}');")
driver.switch_to.window(driver.window_handles[1])
3. 좋아요 클릭
블로그로 들어가면 창 아래쪽에 좋아요, 댓글로 바로 이동할 수 있는 모듈이 있다. 이것을 이용해 좋아요를 누를 수 있는 글인지 아닌지를 먼저 판단한다.
Ctrl + Shift + C 를 이용해 좋아요 버튼의 속성을 확인해 보았다. 좋아요가 클릭되어 있으면 <a> 태그의 class가 on으로, 클릭되지 않았으면 off로 나왔다. 그래서 get_attribute로 가져온 이름을 in 함수를 이용해 on, off유무를 확인하도록 했다.
가끔 크롤링을 하다 보면 좋아요를 누를 수 없는 글도 있었다. 이런 것들은 except으로 예외처리 했다. 좋아요를 누를 수 있는 경우는 아래의 함수를 모두 통과하여 다음 함수가 실행될 것이다.
try:
confirmlike = driver.find_element(By.XPATH, "//*[@id='body']/div[10]/div/div[1]/div/div/a").get_attribute("class").split(" ")
if confirmlike in "on" :
print("이미 좋아요를 누른 게시물입니다.")
driver.close()
driver.switch_to.window(driver.window_handles[0])
except Exception as e:
print(e)
print("좋아요가 제한된 게시물입니다.")
driver.close()
driver.switch_to.window(driver.window_handles[0])
참고로 네이버는 블로그에 머무는 시간이 중요하다고 한다. 그래서 블로그 게시물 끝까지 천천히 스크롤하는 함수를 짜보았다. likePauseTime 변수 두개는 스크롤을 1회 실행하는 시간을 결정한다.
현재 스크롤의 위치 + 창의 크기를 now_scroll_height라는 변수에 담고, 이것이 만약 문서 전체의 높이보다 더 클 경우 스크롤을 멈추도록 했다.
# 좋아요를 누르기 전 스크롤을 할 때 스크롤 최소, 최대 시간
likeminPauseTime : float = 0.5
likemaxPauseTime : float = 6.5
document_height = driver.execute_script("return document.body.scrollHeight")
while True:
driver.find_element(By.XPATH, "//body").send_keys(Keys.PAGE_DOWN)
time.sleep(random.uniform(likeminPauseTime,likemaxPauseTime))
now_scroll_height = driver.execute_script("return window.scrollY + window.innerHeight")
if now_scroll_height >= document_height:
break
document_height = driver.execute_script("return document.body.scrollHeight")
그리고 위에서와 마찬가지로 좋아요 버튼의 xpath를 확인하고, 버튼이 화면 중앙으로 오도록 만든 후 클릭을 했다.
like_btn = driver.find_element(By.XPATH, "//div[@class='btn_like']/div")
driver.execute_script("arguments[0].scrollIntoView({block : 'center'});", like_btn)
like_btn.click()
print("좋아요 누름")
driver.close()
driver.switch_to.window(driver.window_handles[0])
마지막으로 블로그 창을 닫고 다시 첫번째 창으로 돌아온다. 이제 이 동작을 블로그 url 개수만큼 하루종일 반복하면 된다.
4. 코드 정리 하기
그냥 생각나는 대로 코드를 짜다보니 상당히 난잡해져서 이전에 노마드코더가 제안한 방법으로 코드를 한번 정리해 보았다. 이를 순서도로 나타내면 아래와 같다.
- searchBlog(searchWord : str, articleLimit : int) : 검색어, 검색하고자 하는 블로그의 수를 받고 urls를 리턴한다.
- openBlog(url) : url을 받아 블로그를 새 창에서 연다.
- closeBlog() : 현재 창을 닫고 첫번째 창으로 전환한다.
- availableLike() : 좋아요를 누를 수 있는지, 혹은 이미 좋아요를 눌렀는지 판별한다. Boolean 값을 반환한다.
- clickLike() : 블로그의 좋아요를 누른다.
5. 전체 코드
전체 코드를 보기 좋게 아래처럼 써 보았다. 변수들은 대부분 모듈 아래쪽에 모아두었다. 그리고 각 변수에 태그를 달아 무엇에 대한 변수인지도 적어놓았다.
네이버 블로그는 머무는 시간이 상당히 중요하기에 좋아요를 누르는 개수가 제한이 많다. 그러니 이 코드는 밤에 잘 때 실행하거나, 남는 시간에 블로그 운영을 보조해주는 역할로 쓰길 바란다.
아래 코드를 활용하면 자동으로 댓글을 달 수 있는 코드도 쉽게 짤 수 있을 것이다. 요구하는 댓글이 많으면 한번 만들어보려고 한다. 이웃 글의 공감을 누르고 싶다면 다음글을 참고 바란다.
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
import pyperclip as pp
import time
from random import uniform, randrange, shuffle
#자신의 아이디, 비밀번호
yourid : str = "자신의 아이디"
yourpassword : str = "자신의 비밀번호"
# 좋아요를 누르기 전 스크롤을 할 때 스크롤 최소, 최대 시간
scrollMinPauseTime : float = 0.5
scrollMaxPauseTime : float = 5.0
# 원하는 태그 목록
searchWords : list = ["원하는","태그","입력"]
# 태그 순서 섞기
shuffle(searchWords)
# 각 태그당 좋아요를 누를 최소, 최대 개수
tagMinNum : int = 40
tagMaxNum : int = 60
#좋아요 누른 개수, 다음 태그로 넘어가는 개수
clickedLikeNum : int = 0
stopTagNum : int = 0
options = webdriver.ChromeOptions()
options.add_experimental_option('excludeSwitches', ['enable-logging'])
options.add_argument('--no-sandbox')
options.add_argument("disable-gpu")
# options.add_argument('headless')
options.add_argument('window-size=1920x1080')
options.add_argument("user-agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36")
driver = webdriver.Chrome(options=options)
def inputkeys(someWord : str, placeholder : str):
pp.copy(someWord)
holderInput = driver.find_element(By.XPATH, f"//input[@placeholder='{placeholder}']")
holderInput.click()
pp.copy(someWord)
time.sleep(uniform(3.0, 10.0))
# 윈도우 환경에서 붙여넣기
# holderInput.send_keys(Keys.CONTROL, 'v')
# 맥 환경에서 붙여넣기
holderInput.send_keys(Keys.COMMAND, 'v')
def login(naverid : str, naverpassword : str):
driver.get("https://nid.naver.com/nidlogin.login?svctype=262144&url=http://m.naver.com/aside/")
inputkeys(naverid, "아이디")
inputkeys(naverpassword, "비밀번호")
driver.find_element(By.XPATH, f"//input[@placeholder='비밀번호']").send_keys(Keys.ENTER)
time.sleep(2)
def scrollEndPosition():
document_height = int(driver.execute_script("return document.body.scrollHeight"))
now_scroll_height = int(driver.execute_script("return window.scrollY + window.innerHeight"))
if now_scroll_height >= (document_height*9/10):
return False
else :
return True
def searchBlog(searchWord : str, articleLimit : int):
adress = "https://m.search.naver.com/search.naver?where=m_blog&query="+searchWord+"&nso=so%3Add%2Cp%3Aall"
driver.get(adress)
driver.implicitly_wait(10)
articles = driver.find_elements(By.XPATH, "//div[@class='title_area']/a")
numOfArticles = len(articles)
SCROLL_PAUSE_TIME = 1
while numOfArticles < articleLimit :
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
time.sleep(SCROLL_PAUSE_TIME)
articles = driver.find_elements(By.XPATH, "//div[@class='title_area']/a")
numOfArticles = len(driver.find_elements(By.XPATH, "//div[@class='title_area']/a"))
articles = driver.find_elements(By.XPATH, "//div[@class='title_area']/a")
numOfArticles = len(articles)
urls = []
for i in range(numOfArticles):
url = str(articles[i].get_attribute("href"))
urls.append(url)
return urls
def openBlog(url):
driver.execute_script(f"window.open('{url}');")
driver.switch_to.window(driver.window_handles[1])
def closeBlog():
driver.close()
driver.switch_to.window(driver.window_handles[0])
def availableLike():
global stopTagNum
try :
confirmlike = driver.find_element(By.XPATH, "//*[@id='body']/div[10]/div/div[1]/div/div/a").get_attribute("class").split(" ")
if "on" in confirmlike :
stopTagNum += 1
print(f'이미 좋아요 누른 게시물 {stopTagNum}개')
return False
elif "off" in confirmlike :
return True
except Exception as e:
print(e)
print('좋아요가 제한된 게시물')
return False
def clickLike():
while scrollEndPosition():
driver.find_element(By.XPATH, "//body").send_keys(Keys.PAGE_DOWN)
time.sleep(uniform(scrollMinPauseTime, scrollMaxPauseTime))
like_btn = driver.find_element(By.XPATH, "//div[@class='btn_like']/div")
driver.execute_script("arguments[0].scrollIntoView({block : 'center'});", like_btn)
like_btn.click()
global clickedLikeNum
clickedLikeNum += 1
print(f"블로그 좋아요를 {clickedLikeNum}개 누름")
closeBlog()
login(yourid, yourpassword)
# 설정한 태그 공감 누르기
for searchWord in searchWords :
urls = searchBlog(searchWord, randrange(tagMinNum, tagMaxNum))
for url in urls:
openBlog(url)
# 블로그 페이지 로딩을 위한 시간
time.sleep(uniform(3.0, 7.0))
# 좋아요가 클릭 가능한지 확인 후 클릭, 아니면 창 닫기
if availableLike() :
clickLike()
else :
closeBlog()
driver.quit()