코딩 강의/유용한 스킬 없을까요

파이썬 크롤링을 이용하여 인스타그램 맞팔 여부 자동 확인하기

아미넴 2020. 8. 31.
반응형

안녕하세요.

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

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

흥미를 주기 위해 사람의 심리를 조금 이용하여

다음과 같은 작업을 해보려고 합니다.

 

목차

     

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

    간혹 팔로우를 끊는 사람들이 있더라구요. ㅂㄷㅂㄷ

     

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

     

    필요한 모듈 import

    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

     

    Downloads - ChromeDriver - WebDriver for Chrome

    WebDriver for Chrome

    sites.google.com

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

     

     

    인스타그램 로그인 하기

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

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

     

    예를 들어 커맨드 창에 아래와 같이 입력을 한다면

    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('.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("팔로워 수: " + 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('.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')
    followings = soup.findAll('a',['FPmhX','notranslate','_0imsa'])
    followings_text = []
    for following in followings:
        followings_text.append(following.get_text())
    
    print("팔로잉 수: " + 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('맞팔하지 않은 사람 목록: '+str(result))

    팔로잉 사용자명을 기준으로 한명씩 모든 팔로워 사용자명 명단과 대조를 하여

    있으면 카운팅을 하고 빠져 나오는 로직을 반복적으로 수행했습니다.

    만약 카운팅이 0이라면 나는 팔로우 하지만 팔로워 명단에는 없는 것이므로

    맞팔하지 않은 대상이라는 뜻이고 결과 배열에 추가를 합니다.

     

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

    바로 이렇게요.

     

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

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

     

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

     

    전체 소스 공유

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

    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('계정: ' + 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("팔로워 수: " + 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('.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')
    followings = soup.findAll('a',['FPmhX','notranslate','_0imsa'])
    followings_text = []
    for following in followings:
        followings_text.append(following.get_text())
    
    print("팔로잉 수: " + 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('맞팔하지 않은 사람 목록: '+str(result))

     

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

    반응형
    그리드형(광고전용)

    댓글4

    • 궁금해요 2021.07.18 21:46

      안녕하세요!! 블로그 아주 잘보고 배우고 있습니다!!

      제가 이 코드 따라하다가 계속 오류가 나는데 어떠한 부분에서의 탭 실수로 오류가 나는거 같아요 ㅠㅠ 어디서 탭을 해야하는지 정확히 몰라서 ㅠㅠ

      혹시 다른 글처럼 전체 소스로 한번 공유 가능할까요??? ㅠㅠ

      가능하시다면 부탁드립니다!! 감사합니다 ㅠㅠ

      답글

    • 이형종 2022.06.04 12:51

      안녕하세요 블로그 보고 배우고 있는 학생입니다.

      newHeight = browser.execute_script("return document.querySelectorAll('.jSC57')[0].scrollHeight")

      잘 동작되던 중 이 부분에서 오류가 계속 나는데 어떻게 수정해야 할까요 ㅠㅠ?

      .jSC57 이부분을 바꿔야 한다면 어떻게 바꿔야 할지도 같이 문의 드립니다.
      답글

      • 아미넴 2022.06.04 14:35 신고

        안녕하세요. 확인해 보니 그 부분이 최근 ._aano로 변경된 것 같네요. 바꿔서 해보세요~ 클래스명은 수시로 변경될 수 있으니 위치를 기억해 두셨다가 에러나면 체크해 보시기 바랄게요.

    💲 추천 글