인스타그램을 하다 보니, 어떤 사람이 내가 올리는 모든 스토리에 좋아요를 누르는 것을 발견했다. 예전 같았으면 그냥 넘어갔겠지만 파이썬을 다루다 보니 이 사람이 혹시 좋아요를 봇을 이용하여 누르는 것이 아닌가 의심을 하게 되었다. 그래서 나도 만들어보기로 했다.
2023.06.30. 업데이트
- 좋아요 버튼의 속성이 button --> div로 변경됨.
driver.find_element(By.XPATH, '//*[@aria-label="좋아요"] //ancestor :: div[2]').click()
1. 필요한 스토리 찾기
먼저 스토리의 xpath를 찾아보았다. li 태그 내부에 button이 리스트 형태로 들어가 있었다. xpath는 '//ul//li//button' 으로 충분했다.
문제는 li 태그 내에서 내 스토리는 좋아요를 누를 필요가 없고, Live가 있으면 좋아요를 누를 수가 없다는 것이다. 그래서 li 태그 내에서 id를 나타내는 요소를 찾아 텍스트를 추출해 보았다. selenium에서 요소를 찾고 텍스트를 추출하는 방법은 간단하다. 뒤에 .text를 붙여주면 된다.
storys = driver.find_elements(By.XPATH, "//ul//li//button")
isstory = 0
for story in storys:
if "LIVE" in story.text or "내 스토리" in story.text:
isstory +=1
storys[isstory].click()
time.sleep(2)
driver.implicitly_wait(10)
storys는 스토리의 모든 요소들을 추출한다. for 문은 찾은 스토리 요소들을 꺼내 내부의 아이디 요소들을 추출한다. 그 중에서 'LIVE'나 '내 스토리'를 포함 하고 있다면 isstory에 값을 추가한다. 그리고 storys 내부 요소들 중, isstory+1 번째의 요소를 클릭하도록 한다. 이렇게 하면 라이브 스토리나 내 스토리를 피해 클릭이 가능하다.
2. 스토리 내부 요소들
먼저 스토리가 켜져 있는 상태에서 프로그램이 항상 실행되도록 만들어야 한다. 그래서 어떤 요소를 검사해서 확인할지를 고민하다가 '닫기'버튼을 검사하기로 했다. 닫기 버튼은 내부 요소 중 aria-label 이 '닫기'인 요소를 찾으면 간단했다.
storylike는 총 누른 스토리 좋아요의 개수이며, storystop은 누르고자 하는 스토리의 최대 개수이다. while문에 조건을 주어 누르는 좋아요의 개수가 최대 개수를 넘지 않도록 만들었다. 그리고 닫기 버튼을 찾고, 만약 닫기 버튼이 없다면 함수는 정지한다.
storylike = 0
while storystop > storylike :
try :
driver.find_element(By.XPATH, '//section//*[@aria-label = "닫기"]')
except:
print('스토리 창이 열려있지 않습니다.')
break
좋아요 버튼과 다음 버튼은 그냥 인스타 피드와 같다. 처음에는 그냥 좋아요 버튼을 쭉 누르면 끝날 것 같았다.
3. 한 사람이 다량의 스토리를 업로드 할 경우
가끔 인친들 중에 하루에 10개 이상의 다량의 스토리를 올리는 사람이 있다. 좋아요를 누르다가, 이런 사람을 만난다면 한명의 사람에게 너무 많은 좋아요를 할당하게 된다. 참고로 몇번의 실험을 해보니 스토리도 과도하게 좋아요를 많이 누르는 경우에 쉐도우 밴을 당할 수도 있으니 조심해야 한다.
따라서 좋아요를 2~3개쯤 누르고, 그 뒤에도 만약 같은 사람의 스토리라면 계속적으로 다음 버튼을 누르도록 구현해보려고 했다. 복잡해서 순서도로 한번 그려보았다.
그냥 말로 하자면 하나의 아이디에서 스토리를 2~3개 정도 지정한 개수만큼 누르고, 새로운 아이디가 나올 때 까지 다음 버튼을 누르는 것이다. 아직 스크립트에 대한 이해도가 낮아서 아이디를 저장하고 이를 확인하는 방식으로 하다 보니 코드가 좀 복잡해졌다.
while storystop > storylike :
try :
driver.find_element(By.XPATH, '//section//*[@aria-label = "닫기"]')
except:
print('스토리 창이 열려있지 않습니다.')
break
storyid = driver.find_elements(By.XPATH, "//section//header//div//a")[1].text
numofstory = 0
while True:
if numofstory > max:
break
try :
get_storyid = driver.find_elements(By.XPATH, "//section//header//div//a")[1].text
if get_storyid != storyid:
break
driver.find_element(By.XPATH, '//*[@aria-label="좋아요"] //ancestor :: div[2]').click()
numofstory += 1
storylike += 1
print(f'{get_storyid}님 {numofstory}개 클릭: 총 {storylike}개 클릭')
driver.find_element(By.XPATH, '//button[@aria-label="다음"]/div').click()
time.sleep(round(random.randrange(1, 3)))
except:
driver.find_element(By.XPATH, '//button[@aria-label="다음"]/div').click()
time.sleep(round(random.randrange(1, 3)))
if get_storyid != storyid:
continue
while storyid == driver.find_elements(By.XPATH, "//section//header//div//a")[1].text:
driver.find_element(By.XPATH, '//button[@aria-label="다음"]/div').click()
time.sleep(round(random.randrange(1, 3)))
이 방식으로 하면 좋아요를 누르는 속도가 조금 빠르다. 쉐도우 밴이 걱정된다면 '다음'버튼을 누르지 않고 피드가 넘어갈 때 까지 존버하는 방법도 있다.
4. 함수 구현
click_story 함수는 storystop, max 총 2개의 변수를 받는다. storystop은 누르고자 하는 좋아요의 총 개수, max는 1명의 아이디에서 최대로 누르고자 하는 스토리의 개수를 지정하면 된다.
만약 click(20,3)을 넣는다면, 최대 20개의 스토리에 좋아요를 누르며 한 아이디에서 최대 2개의 스토리를 누르게 된다. 쉐도우 밴이 걱정되어 '다음' 버튼은 누르지 않고, 최대한 대기하도록 만들었다.
def click_story(storystop, max):
try :
driver.find_element(By.XPATH, '//*[text() = "나중에 하기"]').click()
driver.implicitly_wait(10)
except :
pass
storys = driver.find_elements(By.XPATH, "//ul//li//button")
isstory = 0
for story in storys:
if "LIVE" in story.text or "내 스토리" in story.text:
isstory +=1
storys[isstory].click()
time.sleep(2)
driver.implicitly_wait(10)
storylike = 0
while storystop > storylike :
try :
driver.find_element(By.XPATH, '//section//*[@aria-label = "닫기"]')
except:
print('스토리 창이 열려있지 않습니다.')
break
storyid = driver.find_elements(By.XPATH, "//section//header//div//a")[1].text
numofstory = 0
while True:
if numofstory > max:
break
try :
get_storyid = driver.find_elements(By.XPATH, "//section//header//div//a")[1].text
if get_storyid != storyid:
break
driver.find_element(By.XPATH, '//*[@aria-label="좋아요"] //ancestor :: div[2]').click()
numofstory += 1
storylike += 1
print(f'{get_storyid}님 {numofstory}개 클릭: 총 {storylike}개 클릭')
driver.find_element(By.XPATH, '//button[@aria-label="다음"]/div').click()
time.sleep(round(random.randrange(1, 3)))
except:
# driver.find_element(By.XPATH, '//button[@aria-label="다음"]/div').click()
time.sleep(round(random.randrange(1, 3)))
if get_storyid != storyid:
continue
while storyid == driver.find_elements(By.XPATH, "//section//header//div//a")[1].text:
driver.find_element(By.XPATH, '//button[@aria-label="다음"]/div').click()
5. 함수 적용하기
함수를 적용한 예시를 아래처럼 만들어 보았다. 참고로 스토리를 누르는 속도는 일반 피드를 누르는 것보다 상당히 빠르기 때문에 너무 자주 쓰는 것은 권하지 않는다.
다만, 스토리 자체가 넘어가는 속도가 빠르기 때문에 일반 피드를 누르는 것과는 다른 제한 사항이 적용되는 것으로 보인다. 실제로 함수를 실험하면서 10분 이내에 스토리 좋아요를 약 200개 넘게 누르고도 한 동안은 쉐도우 밴을 당하지 않았다. 그리도 괜한 모험은 하지 않기를 바란다.
#위쪽 부분의 함수들 중 제일 아래에 넣으면 됨
def click_story(storystop, max):
driver.get('https://instagram.com')
time.sleep(5)
try :
driver.find_element(By.XPATH, '//*[text() = "나중에 하기"]').click()
time.sleep(2)
except :
time.sleep(2)
pass
storys = driver.find_elements(By.XPATH, "//ul//li//button")
isstory = 0
for story in storys:
if "LIVE" in story.text or "내 스토리" in story.text:
isstory +=1
storys[isstory].click()
time.sleep(2)
driver.implicitly_wait(10)
storylike = 0
while storystop > storylike :
try :
driver.find_element(By.XPATH, '//section//*[@aria-label = "닫기"]')
except:
print('스토리 창이 열려있지 않습니다.')
break
storyid = driver.find_elements(By.XPATH, "//section//header//div//a")[1].text
numofstory = 0
while True:
if numofstory > max:
break
try :
get_storyid = driver.find_elements(By.XPATH, "//section//header//div//a")[1].text
if get_storyid != storyid:
break
driver.find_element(By.XPATH, '//*[@aria-label="좋아요"] //ancestor :: div[2]').click()
numofstory += 1
storylike += 1
print(f'{get_storyid}님 {numofstory}개 클릭: 총 {storylike}개 클릭')
driver.find_element(By.XPATH, '//button[@aria-label="다음"]/div').click()
time.sleep(round(random.randrange(1, 3)))
except:
# driver.find_element(By.XPATH, '//button[@aria-label="다음"]/div').click()
time.sleep(round(random.randrange(1, 3)))
if get_storyid != storyid:
continue
while storyid == driver.find_elements(By.XPATH, "//section//header//div//a")[1].text:
# driver.find_element(By.XPATH, '//button[@aria-label="다음"]/div').click()
time.sleep(round(random.randrange(1, 3)))
#본문에 함수를 삽입
driver.get('https://instagram.com')
login('자신의 아이디','자신의 비밀번호')
random.shuffle(tags)
# 아래처럼 본문에 함수를 삽입합니다.
click_story(40, 2)
for tag in tags:
try :
bot(tag, random.randrange(15,30))
except :
print('새로운 피드가 없거나, 다음 피드가 없습니다. 다음 태그로 넘어갑니다.')
time.sleep(random.randrange(5,10))
driver.refresh()
driver.quit()