Categories
코딩

인스타그램 F4F(맞팔로우) 여부 자동 확인이 가능할까

안녕하세요.

저도 크롤링을 처음 공부하면서 여러가지를 해보는 중인데요.

이번에는 최근 인기있는 SNS 중 하나인 인스타그램에 적용해 볼까 합니다.

흥미를 주기 위해 사람의 심리를 조금 이용하여 다음과 같은 작업을 해보려고 합니다.

어때요? 해보고 싶지 않나요. 🙂

간혹 팔로잉을 끊는 사람들이 있더라구요. TT

자 그럼 차근차근 해볼까요.

필요한 모듈 임포트

import time
import sys
from selenium import webdriver
from bs4 import BeautifulSoup

selenium 패키지의 webdriver 모듈은 웹 브라우저를 실행시키고 스크립트 명령에 따라 액션을 수행할 수 있게 해주고 bs4 패키지 내의 BeautifulSoup 모듈은 HTML DOM 형태의 데이터에서 원하는 내용을 쉽게 추출할 수 있게 해주는 기능을 가지고 있습니다.

만약 selenium과 bs4 패키지가 없다면 커맨드 창에서 아래와 같이 각각 입력하여 설치해 줍니다.

pip install bs4
pip install selenium

그리고 크롬 webdriver는 아래 링크에서 다운받으실 수 있습니다.

https://sites.google.com/a/chromium.org/chromedriver/downloads

그 외 나머지는 기본 모듈이므로 바로 임포트 해서 사용하실 수 있습니다.

인스타그램 로그인 하기

이제 크롬 브라우저를 띄우고 인스타그램 주소로 접속하여 로그인을 시도할 건데요.

커맨드 창에서 직접 입력받아보겠습니다.

예를 들어 커맨드 창에 아래와 같이 입력을 한다고 가정해 보겠습니다.

python crawling_instagram.py sangminem 123456

여기서 sys.argv[0]은 crawling_instagram.py 이고 sys.argv[1]은 sangminem 그리고 sys.argv[2]는 123456 이 됩니다.

이를 활용하여 아래와 같이 인스타에 로그인하는 코드를 작성해 보겠습니다.

browser = webdriver.Chrome('./chromedriver')
browser.get('https://www.instagram.com/'+sys.argv[1])
browser.execute_script("document.querySelectorAll('.-nal3')[1].click();")
time.sleep(2)
browser.find_element_by_name('username').send_keys(sys.argv[1])
browser.find_element_by_name('password').send_keys(sys.argv[2])
browser.find_element_by_xpath('//*[@id="loginForm"]/div[1]/div[3]/button').submit()
time.sleep(5)
browser.find_element_by_xpath('//*[@id="react-root"]/section/main/div/div/div/div/button').click()

chromedriver를 이용하여 크롬 웹 브라우저를 띄우고 인스타그램 주소와 사용자명을 결합하여 접속을 했구요.

browser.execute_script를 이용하여 팔로워 버튼을 클릭하여 로그인 창을 띄웠습니다.

(로그인이 안 되어 있을 때 클릭하면 로그인 창이 뜨도록 되어있습니다.)

그 후에 로딩이 길어질 것을 대비하여 2초 대기를 뒀습니다.

다음으로 input 타입이 username과 password 부분을 찾아내어 send_keys 메서드로 사용자명과 비밀번호를 입력하도록 하였습니다.

그리고 xpath로 form 내 로그인 버튼을 찾아 submit 메서드를 호출하였습니다.

xpath는 크롬에서 F12 키를 누르면 나오는 개발자의 Elements 탭에서 찾아가고자 하는 엘리먼트에서 마우스 오른쪽 버튼을 누르고 Copy > Copy xpath를 선택하면 얻을 수 있습니다.

다음 로그인될 때까지 5초를 기다립니다.

다시 xpath를 이용하여 나중에 하기 버튼을 클릭하도록 작성한 코드입니다.

이 부분은 단순히 건너뛰기 위한 부분이므로 자세한 설명은 생략하겠습니다.

팔로워 목록 가져오기

이제 로그인이 완료되었으니 팔로워 수를 구하는 로직을 구현해 보겠습니다.

time.sleep(2)
browser.execute_script("document.querySelectorAll('.-nal3')[1].click();")
time.sleep(1)
oldHeight = -1
newHeight = -2
while oldHeight != newHeight:
    oldHeight = newHeight
    newHeight = browser.execute_script("return document.querySelectorAll('._aano')[0].scrollHeight")
    browser.execute_script("document.querySelectorAll('.isgrP')[0].scrollTo(0,document.querySelectorAll('._aano')[0].scrollHeight)")
    time.sleep(0.5)
soup = BeautifulSoup(browser.page_source, 'html.parser')
followers = soup.findAll('a',['FPmhX','notranslate','_0imsa'])
followers_text = []
for follower in followers:
    followers_text.append(follower.get_text())
print("Number of followers: " + str(len(followers_text)))

오동작을 막기 위해 다시 2초를 대기한 후에 다시 팔로워 바튼을 클릭했습니다.

그리고 1초를 대기하고 본격적으로 팔로워 사용자명을 구하는 부분입니다.

먼저 모든 팔로워를 불러와야 하기 때문에 모든 사람을 로딩을 시키기 위해 스크롤을 반복적으로 내리는 로직을 while 문을 통해 구현했습니다.

이전 스크롤 높이와 새로운 스크롤 높이가 다르면 추가적으로 로딩할 것이 남아있다는 의미이므로 이전 스크롤 높이와 새로운 스크롤 높이가 같아질 때까지 계속 반복하는 구문입니다.

querySelectorAll 메서드 인자 값인 class 명은 개발자 모드 Elements 탭에서 직접 보고 가져온 값들입니다.

로딩을 마친 후 html 데이터를 BeautifulSoup 모듈을 통해 가져와서 사용자명이 있는 태그 및 클래스를 확인하여 모두 추출하여 배열에 담습니다.

print 메서드로 배열 길이를 구하여 팔로워 수를 화면에 출력하였습니다.

팔로잉 목록 가져오기

다음으로 팔로잉 수를 구해 보겠습니다.

browser.find_element_by_xpath('/html/body/div[4]/div/div/div[1]/div/div[2]/button').click()
time.sleep(0.5)
browser.execute_script("document.querySelectorAll('.-nal3')[2].click();")
time.sleep(1)
oldHeight = -1
newHeight = -2
while oldHeight != newHeight:
    oldHeight = newHeight
    newHeight = browser.execute_script("return document.querySelectorAll('._aano')[0].scrollHeight")
    browser.execute_script("document.querySelectorAll('.isgrP')[0].scrollTo(0,document.querySelectorAll('._aano')[0].scrollHeight)")
    time.sleep(0.5)
soup = BeautifulSoup(browser.page_source, 'html.parser')
followings = soup.findAll('a',['FPmhX','notranslate','_0imsa'])
followings_text = []
for following in followings:
    followings_text.append(following.get_text())
print("Number of followings: " + str(len(followings_text)))

팔로워 창을 닫기 위해 xpath를 이용하여 닫기 버튼을 클릭하였습니다.

그 다음 0.5초를 대기하고 팔로잉 버튼을 클릭했습니다.

그리고 다시 1초를 대기하고 팔로잉 하고 있는 사용자명을 구했습니다.

팔로워 사용자명을 구하는 패턴과 거의 유사하므로 이 부분은 다시 설명하지는 않겠습니다.

나만 팔로우 한 대상 뽑아오기

마지막으로 팔로워 사용자명 목록과 팔로잉 사용자명 목록을 비교하여 맞팔을 하지 않은 대상을 구해 보겠습니다.

result = []
for following in followings_text:
    cnt = 0
    for follower in followers_text:
        if following == follower:
            cnt += 1
            break
    if cnt == 0:
        result.append(following)
print('List of people who did not F4F: '+str(result))

팔로잉 사용자명을 기준으로 한명씩 모든 팔로워 사용자명 명단과 대조를 하여 있으면 카운팅을 하고 빠져 나오는 로직을 반복적으로 수행했습니다.

만약 카운팅이 0이라면 나는 팔로잉 하지만 팔로워 명단에는 없는 것이므로 F4F하지 않은 대상이라는 뜻이고 결과 배열에 추가를 합니다.

최종적으로 결과 배열을 출력하면 원하는 목적이 달성 됩니다.

바로 이렇게요.

나는 팔로우 했는데 당신은 안 한거야? ㅠ

참고로 저는 지인만 팔로우 해서 맞팔하지 않은 인원이 많지는 않습니다.

간단하게 목적을 달성했습니다.

전체 소스 공유

원하시는 분이 계셔서 공유합니다.

import time
import sys
from selenium import webdriver
from bs4 import BeautifulSoup
username = sys.argv[1]
browser = webdriver.Chrome('./chromedriver')
browser.get('https://www.instagram.com/'+username)
browser.execute_script("document.querySelectorAll('.-nal3')[1].click();")
time.sleep(2)
browser.find_element_by_name('username').send_keys(sys.argv[1])
browser.find_element_by_name('password').send_keys(sys.argv[2])
browser.find_element_by_xpath('//*[@id="loginForm"]/div[1]/div[3]/button').submit()
time.sleep(5)
browser.find_element_by_xpath('//*[@id="react-root"]/section/main/div/div/div/div/button').click()
time.sleep(5)
if len(sys.argv) > 3:
    username = sys.argv[3]
print('Account: ' + username)
browser.get('https://www.instagram.com/'+username)
time.sleep(2)
browser.execute_script("document.querySelectorAll('.-nal3')[1].click();")
time.sleep(1)
oldHeight = -1
newHeight = -2
while oldHeight != newHeight:
    oldHeight = newHeight
    newHeight = browser.execute_script("return document.querySelectorAll('.jSC57')[0].scrollHeight")
    browser.execute_script("document.querySelectorAll('.isgrP')[0].scrollTo(0,document.querySelectorAll('.jSC57')[0].scrollHeight)")
    time.sleep(0.5)
soup = BeautifulSoup(browser.page_source, 'html.parser')
followers = soup.findAll('a',['FPmhX','notranslate','_0imsa'])
followers_text = []
for follower in followers:
    followers_text.append(follower.get_text())
print("Number of followers: " + str(len(followers_text)))
browser.find_element_by_xpath('/html/body/div[4]/div/div/div[1]/div/div[2]/button').click()
time.sleep(0.5)
browser.execute_script("document.querySelectorAll('.-nal3')[2].click();")
time.sleep(1)
oldHeight = -1
newHeight = -2
while oldHeight != newHeight:
    oldHeight = newHeight
    newHeight = browser.execute_script("return document.querySelectorAll('._aano')[0].scrollHeight")
    browser.execute_script("document.querySelectorAll('.isgrP')[0].scrollTo(0,document.querySelectorAll('._aano')[0].scrollHeight)")
    time.sleep(0.5)
soup = BeautifulSoup(browser.page_source, 'html.parser')
followings = soup.findAll('a',['FPmhX','notranslate','_0imsa'])
followings_text = []
for following in followings:
    followings_text.append(following.get_text())
print("Number of followings: " + str(len(followings_text)))
result = []
for following in followings_text:
    cnt = 0
    for follower in followers_text:
        if following == follower:
            cnt += 1
            break
    if cnt == 0:
        result.append(following)
print('List of people who did not F4F: '+str(result))

다음에 또 다른 주제로 찾아 뵐게요. 🙂

(주의) 클래스 이름은 수시로 변경될 수 있으니 프로그램이 동작하지 않는다면 수정을 위해 인스타그램 태그 구조를 직접 파악해야 할 수도 있습니다.

답글 남기기

이메일 주소는 공개되지 않습니다.

ko_KR한국어