Python/Project

파이썬 Selenium 모듈을 이용한 트위터 팔로워 크롤러 개발

728x90
반응형

실행 결과

해당 Python File이 위치한 경로에 export라는 디렉토리가 생성되고 export 디렉토리의 하위 txt파일로 targetName.txt라는 텍스트 파일이 생성되며, 해당 텍스트파일에는 targetName유저의 팔로워들의 ID가 아래와같이 저장됩니다.

기상청 공식 트위터의 팔로워들의 리스트가 txt파일에 저장된다.

개요

구글링을 꽤나 오랫동안 했지만, 파이썬의 Selenium 모듈을 이용하여 특정 유저의 팔로워 리스트를 추출하는 소스코드를 찾을수가 없어 직접 개발하였습니다. Twitter 공식 API센터에서 이와 유사한 내용을 얻을 수 있는 API가 존재하지만 1시간에 15명의 팔로워 리스트만을 반환해주는 API이기때문에 대량의 팔로워 리스트가 필요한 현재 상황에서 사용할 수 없다 판단하여 직접 스크래핑하게되었습니다.

전체 코드는 아래와 같습니다.

# TwitterFollowerCrawler.py
import os
import time
import requests
from selenium import webdriver
from bs4 import BeautifulSoup

def crawler(username):
    path = os.path.realpath(os.path.dirname(__file__))
    dataFile = open(os.path.join(path, "export", "{}".format(username)), mode="at", encoding="utf-8")

    # Your Twitter account here
    twitterAccount = {"id":"", "pw":""}

    SCROLL_PAUSE_TIME = 2

    options = webdriver.ChromeOptions()
    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")
    options.add_argument("headless")

    path = os.path.realpath(os.path.dirname(__file__))
    driver = webdriver.Chrome(os.path.join(path, "chromedriver.exe"), chrome_options=options)

    followersURL = "https://twitter.com/{}/followers".format(username)

    # 트위터 로그인
    driver.get("https://mobile.twitter.com/login?lang=ko")
    username_field = driver.find_element_by_name("session[username_or_email]")
    password_field = driver.find_element_by_name("session[password]")
    username_field.send_keys(twitterAccount["id"])
    driver.implicitly_wait(1)
    password_field.send_keys(twitterAccount["pw"])
    driver.implicitly_wait(1)
    driver.find_element_by_xpath('//*[@id="react-root"]/div/div/div[2]/main/div/div/div[1]/form/div/div[3]/div').click()

    driver.get(followersURL)

    last_height = 0

    userList = []

    while True:
        # 화면 최하단으로 스크롤 다운
        driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
    
        # 페이지 로드를 기다림
        time.sleep(SCROLL_PAUSE_TIME)
        driver.execute_script("window.scrollTo(0, document.body.scrollHeight-50);")
        time.sleep(SCROLL_PAUSE_TIME)
    
        # Calculate new scroll height and compare with last scroll height
        new_height = driver.execute_script("return document.body.scrollHeight")
    
        # 새로운 높이가 이전 높이와 변하지 않았을 경우 스크롤 종료
        if new_height == last_height:
            break
        
        # 스크롤 다운이 된다면 스크롤 다운이 된 후의 창 높이를 새로운 높이로 갱신
        last_height = new_height

        crawlData = driver.find_elements_by_xpath('//*[@id="react-root"]/div/div/div[2]/main/div/div/div/div/div/div[2]/section/div/div/div')
        for i in crawlData:
            for x in i.text.split():
                if x[0] == '@':
                    print(x)
                    dataFile.write("{}\n".format(x))
                    userList.append(x)
    return userList

userList = crawler(input("검색하실 유저명을 입력해주세요 : "))

해당 코드를 위치한 곳에 export라는 디렉토리와 자신의 chrome 버전에 맞는 chromedriver가 설치되어있어야 정상적으로 동작하며, 13번째 라인의 twitterAccount 사전변수에 자신의 twitter id/pw를 기입해주어야 동작합니다.

여기서 주의할점은 본인인증이 완료된 실사용 계정이어야 프로그램이 동작합니다. 미인증 계정의 경우 로그인 후 특정 유저의 팔로워 리스트를 볼 수 없습니다.

코드 설명

path = os.path.realpath(os.path.dirname(__file__))
dataFile = open(os.path.join(path, "export", "{}".format(username)), mode="at", encoding="utf-8")

# Your Twitter account here
twitterAccount = {"id":"", "pw":""}

SCROLL_PAUSE_TIME = 2

options = webdriver.ChromeOptions()
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")
options.add_argument("headless")

path = os.path.realpath(os.path.dirname(__file__))
driver = webdriver.Chrome(os.path.join(path, "chromedriver.exe"), chrome_options=options)

해당 코드는 추후 팔로워 목록을 저장할 dataFile에 대한 파일경로 설정 이후 Chrome driver의 세부적인 설정을 수정합니다.

options.add_argument를 이용해 user-agent 설정, chrome창을 background에서 동작하도록 설정, 그리고 selenium이 사용할 chromedriver.exe의 위치를 잡아줍니다.

followersURL = "https://twitter.com/{}/followers".format(username)

# 트위터 로그인
driver.get("https://mobile.twitter.com/login?lang=ko")
username_field = driver.find_element_by_name("session[username_or_email]")
password_field = driver.find_element_by_name("session[password]")
username_field.send_keys(twitterAccount["id"])
driver.implicitly_wait(1)
password_field.send_keys(twitterAccount["pw"])
driver.implicitly_wait(1)
driver.find_element_by_xpath('//*[@id="react-root"]/div/div/div[2]/main/div/div/div[1]/form/div/div[3]/div').click()

driver.get(followersURL)

해당 코드는 트위터 로그인을 위한 코드입니다. 앞서 말한바대로 특정 유저의 팔로워 리스트를 보기 위해서는 트위터에 로그인해야합니다.

성공적으로 로그인을 수행했다면, followersURL 즉 대상 유저의 팔로워 리스트가 표시된 페이지로 이동합니다.

last_height = 0

userList = []

while True:
    # 화면 최하단으로 스크롤 다운
    driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")

    # 페이지 로드를 기다림
    time.sleep(SCROLL_PAUSE_TIME)
    driver.execute_script("window.scrollTo(0, document.body.scrollHeight-50);")
    time.sleep(SCROLL_PAUSE_TIME)

    # Calculate new scroll height and compare with last scroll height
    new_height = driver.execute_script("return document.body.scrollHeight")

    # 새로운 높이가 이전 높이와 변하지 않았을 경우 스크롤 종료
    if new_height == last_height:
        break
    
    # 스크롤 다운이 된다면 스크롤 다운이 된 후의 창 높이를 새로운 높이로 갱신
    last_height = new_height

    crawlData = driver.find_elements_by_xpath('//*[@id="react-root"]/div/div/div[2]/main/div/div/div/div/div/div[2]/section/div/div/div')
    for i in crawlData:
        for x in i.text.split():
            if x[0] == '@':
                print(x)
                dataFile.write("{}\n".format(x))
                userList.append(x)
return userList

해당 코드는 팔로워 리스트 페이지에서 전체 유저목록을 갱신 + 저장하기 위한 핵심 코드입니다.

트위터는 팔로워 리스트 페이지 접근시 한번의 통신만으로 전체 유저목록을 보여주는것이 아닌, Ajax 통신을 이용해 유저가 스크롤을 내릴 때마다 유저 리스트를 추가/갱신하여 보여줍니다.

따라서 모든 팔로워의 정보를 얻기 위해 더이상 스크롤이 내려가지 않는 지점까지 스크롤링하며 유저 리스트를 파싱합니다.

728x90
반응형