파이썬을 이용한 Twitter 크롤링

왕형준
16 min readFeb 15, 2018

--

앞으로 조금 더 친절하게 설명하기로 다짐했다. 이제까지의 글은 필자 스스로 배운 내용을 정리하기 위한 목적으로 썼다. 그래서 독자들을 크게 고려하지 않고 글을 써왔다.

하지만 다루는 내용이 조금씩 어려워지면서 필자도 원하는 정보를 국내 사이트와 블로그에서 거의 얻지 못하고 있다. 한국어로 된 정보의 필요성을 실감하면서 영어에 약한 한국인들에게 미약한 도움이라도 주고자 앞으로 글을 더 쉽고 친절하게 작성할 계획이다. 단 한 명이라도 필자의 글에서 도움을 받았다면 그것으로 족하다.

목적: 특정 단어가 언급된 트윗의 개수를 날짜별로 파악하기

트위터를 크롤링할 때 일반적으로 tweepy 모듈을 많이 사용한다. Twitter에게 API를 요청하고 정보를 얻는 방식이다. 최근 자료를 얻는 데 있어서 직접 크롤링을 하는 것보다 Tweepy를 쓰는 것이 훨씬 간단하고 빠르다.

하지만 API를 사용하여 원하는 정보를 얻는 방식에는 치명적인 단점이 있다. 바로 7일 내의 자료만 얻을 수 있다는 점이다. 7일 이전의 자료를 API를 요청하여 얻고 싶다면 돈을 지불해야 한다. 한 달에 20만원 정도로 알고 있다. 여유가 있는 사람들과 기업은 돈을 지불하면서 자료를 얻어도 나쁘진 않을 것이다.

하지만 돈을 지불하지 않고 지금으로부터 3년 전인 2015년 2월 15일의 Tweet을 직접 얻고 싶다면 API가 아닌 다른 방법을 사용해야 한다. 따라서 앞으로의 설명에서 Tweepy 모듈을 사용할 일은 없을 것이다. Tweepy를 이용한 크롤링은 기회가 된다면 포스팅하겠다.

우리의 목적은 특정 단어가 언급된 트윗의 개수를 날짜별로 파악하는 일이다. 예를 들어 ‘Blockchain’이라는 단어가 2015년 2월 15일에 몇 번 언급됐는지 파악하는 것이다. 날짜별로 몇 번 언급됐는지 알 수 있다면 트렌드 또한 알 수 있다. 이제 본격적으로 설명해보겠다.

필요한 모듈: selenium, datetime, time, BeautifulSoup

명령 프롬프트를 통하여 필요한 모듈부터 설치하자. 명령 프롬프트는 cmd를 치고 실행하면 된다. 명령 프롬프트 창이 뜨면

pip install selenium

위와 같은 방식으로 필요한 모듈을 하나하나 설치한다. 참고로 BeautifulSoup 모듈을 깔기 위해서

pip install bs4

를 입력해야 한다. pip install BeautifulSoup이 아님에 유의하자.

In [1]:

from bs4 import BeautifulSoupimport requestsfrom selenium import webdriverfrom selenium.webdriver.firefox.firefox_binary import  FirefoxBinaryfrom selenium.webdriver.common.desired_capabilities import  DesiredCapabilitiesimport timefrom selenium.webdriver.common.keys import Keysimport datetime as dt

In [2]:

binary=FirefoxBinary('C:/Program Files/Mozilla Firefox/firefox.exe')
browser=webdriver.Firefox(executable_path='yourpath/geckodriver.exe',firefox_binary=binary)

우리는 파이어폭스를 통해서 크롤링을 할 것이다. BeautifulSoup만 이용해서 크롤링을 하면 속도가 느려서 원하는 결과를 얻을 수가 없다. 속도와 정확성 모두를 얻기 위해서는 selenium 및 firefox를 이용해야 한다. Firefox가 안 깔려 있는 독자들은 Firefox를 설치해야 한다.

‘https://twitter.com/search?q=Blockchain%20since%3A2016-02-15%20until%3A2016-02-16&lang=eg’

위의 사이트는 2015년 2월 15일에 Blockchain을 언급한 모든 tweet을 보여주는 사이트다. since와 until을 주목하자. since는 2015년 2월 15일이고 until은 2015년 2월 16일이다. until 날짜의 Tweet은 포함되지 않는다. 즉, 결과에서 2015년 2월 16일의 트윗은 배제된다.

In [3]:

startdate=dt.date(year=2015,month=2,day=6)
untildate=dt.date(year=2015,month=2,day=7)
enddate=dt.date(year=2015,month=5,day=7)

2015년 2월 6일부터 2015년 5월 7일까지 기간을 잡도록 하겠다. 시작 날과 마지막 날을 각각 startdate와 enddate로 변수를 설정하겠다. untildate는 하루하루의 결과를 출력하기 위한 중간변수이다.

더 진행하기에 앞서 위의 사이트를 한 번 들어가볼 것은 권장한다. 들어가서 특정 트윗에 마우스 우클릭을 하고 검사를 누르면 트윗이 전체 페이지 소스 중에서 p element의 class “TweetTextSize”에 입력된 것을 확인할 수 있다.

이제부터는 내용이 조금 어려워질 수 있으므로 더 차근차근 설명하겠다. 우선 전체 코드부터 공개하겠다.

In [4]:

totalfreq=[]
while not enddate==startdate:
url='https://twitter.com/search?q=Blockchain%20since%3A'+str(startdate)+'%20until%3A'+str(untildate)+'&lang=eg'
browser.get(url)
html = browser.page_source
soup=BeautifulSoup(html,'html.parser')

lastHeight = browser.execute_script("return document.body.scrollHeight")
while True:
dailyfreq={'Date':startdate}
# i=0 i는 페이지수
wordfreq=0
tweets=soup.find_all("p", {"class": "TweetTextSize"})
wordfreq+=len(tweets)

browser.execute_script("window.scrollTo(0, document.body.scrollHeight);")
time.sleep(1)

newHeight = browser.execute_script("return document.body.scrollHeight")
print(newHeight)
if newHeight != lastHeight:
html = browser.page_source
soup=BeautifulSoup(html,'html.parser')
tweets=soup.find_all("p", {"class": "TweetTextSize"})
wordfreq=len(tweets)
else:
dailyfreq['Frequency']=wordfreq
wordfreq=0
totalfreq.append(dailyfreq)
startdate=untildate
untildate+=dt.timedelta(days=1)
dailyfreq={}
break
# i+=1
lastHeight = newHeight

코드부터 공개하는 것은 한 번에 입력하지 않으면 실행이 안 되기 때문이다. 이제 쪼개서 설명해보겠다.

while not enddate==startdate:
url='https://twitter.com/search?q=Blockchain%20since%3A'+str(startdate)+'%20until%3A'+str(untildate)+'&lang=eg'
browser.get(url)
html = browser.page_source
soup=BeautifulSoup(html,'html.parser')

우선 enddate와 startdate가 같아질 때까지 코드를 반복하도록 설정했다. url 부분을 주목해보자. startdate와 untildate를 string으로 변환시켜 url 변수를 설정했다.

순서를 바꿔 코드의 끝부분을 보겠다. while문이 한 번 반복되면 startdate와 untildate가 어떻게 바뀌는지 주목해보자.

else:
dailyfreq['Frequency']=wordfreq
wordfreq=0
totalfreq.append(dailyfreq)
startdate=untildate
untildate+=dt.timedelta(days=1)
dailyfreq={}
break

5번째 줄과 6번째 줄을 보자. startdate는 untildate인 2월 6일로 untildate는 하루 더 늘어서 2월 7일이 됐다. 이런식으로 startdate가 enddate인 5월 7일까지 반복된다면 하루 하루 데이터를 뽑아낼 수 있다.

lastHeight = browser.execute_script("return document.body.scrollHeight")

browser.execute_script("window.scrollTo(0, document.body.scrollHeight);")
time.sleep(1)
newHeight = browser.execute_script("return document.body.scrollHeight")
print(newHeight)
if newHeight != lastHeight:lastHeight = newHeight

위의 코드는 중간중간에 섞여 있는 코드들을 한 데 모아놓은 것이다. 무엇인지 궁금한 사람이 있을 것이다. 무한 scrolling을 위한 코드다. 위 코드를 실행하면 스크롤이 계속 내려가는 것을 확인할 수 있다. scrolling을 하지 않는다면 한 페이지에 있는 트윗 개수밖에 얻지 못한다. 결과의 끝을 보고 싶다면 한 페이지에 있는 트윗들을 얻고 나서 다음 페이지로 자동으로 넘어갈 수 있도록 scrolling 코드를 입력해야 한다.

while True:
dailyfreq={'Date':startdate}
# i=0 i는 페이지수
wordfreq=0
tweets=soup.find_all("p", {"class": "TweetTextSize"})
wordfreq+=len(tweets)

위에서 언급했듯이 tweet은 페이지 소스 코드에서 ‘p’ element에 class “TweetTextSize”에 저장돼 있다. soup.find_all은 페이지 소스에서 원하는 부분을 찾도록 도와주는 함수다. Text뿐만 아니라 영상, 사진, 도표 등 모든 크롤링을 하는 데 있어 필수적으로 쓰인다.

전체 코드를 실행하면 원하는 ‘Blockchain’ 이라는 단어가 특정 기간 동안 일별로 몇 번 언급됐는지 결과를 얻을 수 있다. 물론 결과가 바로 얻어지지 않는다. 필자는 전체 코드를 실행하는 데 약 3시간이 걸렸다.

import pandas as pd
df=pd.DataFrame(totalfreq)
df.head()

totalfreq를 DataFrame으로 변환시켜 head 부분의 결과만 출력하면

Date Frequency
0 2015–02–06 243
1 2015–02–07 253
2 2015–02–08 209
3 2015–02–09 251
4 2015–02–10 281

위와 같은 내용을 얻을 수 있다.

얻은 결과를 그래프로 그려보겠다.

import matplotlib.pyplot as pltplt.figure(figsize=(20,10))
plt.xticks(rotation=90)
plt.scatter(df.Date,df.Frequency)

rotation은 x축 내용을 90도 꺾은 것이다. 글자들이 겹치지 않게 하기 위하여 실행했다.

큰 경향성이 없다고 볼 수도 있지만 필자는 언급되고 있는 횟수가 조금씩 증가하고 있다고 분석했다.

이제까지 Twitter API를 사용하지 않고 직접 크롤링을 해봤다. 다시 강조하지만 API를 사용하지 않는 이유는 돈을 지불하지 않는 이상 7일 이전의 자료들은 얻을 수 없기 때문이다.

하지만 직접 크롤링하는 것이 만병통치약은 아니다. 이또한 한계가 있다. Blockchain이라는 단어는 2015년 당시만 해도 대부분의 사람들이 들어보지 못한 기술이었다. 따라서 Twitter에 많이 언급되지 않았고 이때문에 실행 시간이 오래 걸리지 않았다.

만약 특정 단어가 하루에 600번이 아닌 1만 번 언급됐다면 API 없이 직접 크롤링을 통해서 결과를 얻는 것은 불가능하다. 하루종일 실행되다가 노트북이 터져버릴 것이다. 그럴 경우에는 지역을 한정한다던지 아니면 비율로 결과를 얻는 등의 방향 전환이 필요하다.

위의 코드 중에서 이해가 안 되는 것이 있다면 댓글을 통해서 질문해주길 바란다.

20180813 수정 업데이트

안녕하세요. 이 글을 쓴 지도 벌써 6개월이 됐네요. 프로그래밍 한 달 공부하고 썼던 글인데 이렇게나 많은 호응을 받을 것이라고 생각도 못했습니다.

구글에 ‘트위터 크롤링’을 검색하면 가장 상위에 노출되는만큼 여러분들에게 더 유용한 정보를 제공하고자 몇몇 부분 수정하고자 합니다.

1. 버전관리

모든 라이브러리는 업그레이드됩니다. 지금으로부터 6개월 뒤, 1년 뒤에 이 글을 보시는 분들은 selenium, bs4, requests 등 여러 라이브러리가 업그레이드면서 정상적으로 코드가 작동하지 않을 수도 있어요.

코딩을 직업으로 삼는 분이라면 상관 없겠지만 다른 직업에 종사하는 분이라면 업그레이된 버전에 맞게 코드를 작성하는 일이 어렵게 느껴질 수 있어요.

그런 분들을 위해 한 가지 팁을 알려드립니다. 대단한 건 아니구요, 미래의 업그레이드된 버전이 아닌 2018년 8월 현재의 버전으로 라이브러리를 설치하는 겁니다.

현재 selenium 모듈의 버전은 3.8.0, bs4의 버전은 4.6.0, requests의 버전은 2.18.4입니다. selenium의 버전이 나중에 4.0으로 업그레이드돼도 여러분들은 3.8.0을 설치하시면 됩니다. 모듈을 깔 때 아래 명령어를 입력해주세요

pip install selenium==3.8.0

pip install bs4==4.6.0

pip install requests==2.18.4

이제 업그레이드 여부에 상관없이 크롤링 작업을 성공적으로 할 수 있을 겁니다. 저한테 이메일로 연락하신 분들 대부분이 코딩과 관련없는 직무에 종사하고 계시더라구요. 그런 분들의 시간을 아끼기 위해 한 가지 Tip을 알려드린 것이니 업그레이드된 버전을 사용하고 싶은 분들은 그 버전에 맞게 코드를 작성하셔도 됩니다.

2. 코드 오류 부분

제가 크롤링 작업했을 때는 문제없이 했는데 제가 한 가지 큰 실수를 했더군요. ln[4] 중 While True 이하 부분을 While not enddate==startdata 부분과 동일선상에 배치했는데 그러면 안 됩니다. While True 이하 클릭하셔서 Tab을 눌러야 정상 작동될 겁니다. enddata와 startdate가 같아질 때까지 크롤링 작업을 하는 것인데 동일선상에 있다면 크롤링이 끝나지 않을 겁니다. 이해를 돕기 위해 아래 캡쳐 파일을 보여드리겠습니다.

위의 코드를 고치지 않고 이렇게 캡쳐를 해서 수정하는 이유는 Tab을 눌러도 정상적으로 띄어쓰기가 되지 않았기 때문입니다. Medium이 코드를 올리기에는 그렇게 좋은 매체는 아닌 것 같습니다. 여러분들은 위의 코드를 복붙하시고 While True 이하부터 lastHeight=newHeight까지 선택하셔서 Tab을 누르면 됩니다. 간단합니다.

3. 스크롤링이 끝나지 않다는 문의에 대한 답

몇몇 분들이 이메일로 스크롤링이 끝나지 않는다고 문의하셨는데 그건 코드의 문제가 아니라 수집 데이터 양이 워낙 방대하기 때문입니다. 원본 글에서도 이와 관련하여 말씀드렸는데 에러라고 생각하시는 분들이 많더군요. 파이어폭스 화면이 자동적으로 스크롤 다운되고 있다면 정상적으로 작동하고 있는 겁니다.

이럴 때는 트윗 개수를 한정시켜서 데이터를 수집하는 것을 권합니다. 일별로 트윗 개수를 100개면 100개, 50개면 50개 정해서 수집하면 성공적으로 크롤링할 수 있을 겁니다.

20181111 수정 업데이트

잠깐 크롤링할 일이 있어 제가 쓴 코드 그대로 실험해보니 작동하지 않더군요. 원인은 selenium 라이브러리와 geckodriver 버전의 호환 문제 때문이었습니다. 혹시 위의 제 글 그대로 했는데 에러가 발생하신 분들은 아래를 참고해주세요.

selenium 버전 업그레이드

selenium 버전 업그레이드 혹은 재설치하겠습니다. 아래의 명령어를 입력해주세요.

pip install selenium==3.141.0

geckodriver 설치

우선 위의 사이트를 들어갑시다. 미래에 selenium 버전이 다시 업그레이드될 것이고 그때마다 제가 글을 업데이트할 수는 없으니 미리 버전을 확인하는 겁니다. 2018년 11월 11일 현재 기준으로는 geckodriver 0.23.0 버전을 설치하시면 됩니다. 설치는 아래 사이트에서 합시다.

이렇게 selenium과 geckodriver 버전을 확인한 뒤 버전 업그레이드 혹은 재설치하시면 문제없이 코드가 작동할 것입니다.

피드백주신 분들 모두 감사합니다. 여러분들 덕에 잘못된 부분을 고칠 수 있었습니다. 솔직히 한 번 쓴 글은 다른 글 쓰느라 다시 안 보게 되거든요… 혹시 또 피드백할 부분 있으면 편하게 댓글 달아주세요. 감사합니다!

--

--