패키지 없이 트위터 데이터 수집하기 with Python

이전에 트위터 데이터를 키워드를 기준으로 크롤링하는 글을 쓴적이 있다. 최근 내가 진행하는 연구에서도 트위터 크롤링이 계속 요구되었고, 정말 다양한 패키지와 방법을 사용해 왔다.

Tweepy나 TwitterScraper 등 좋은 패키지들이 github에 많이 공유되어 있는데, 뭔가 내 맘에 드는 게 없어서 순정으로 돌아가보기로 했다.

이번 포스트에서는 일절 패키지 없이 트위터 API로만 크롤링을 시도해 볼 것이다. 요즘 대단히 이슈가 되는 코로나 바이러스에 대해서 키워드를 설정하고, 관련 트윗을 수집해 보자!

트위터 개발자 등록하기

먼저, 트위터 공식 API를 사용하려면 인증키를 받아야 한다.
본 포스트에서는 관련 프로세스들을 다루지 않겠다. 너무 기본적인 세팅이라.. 각자 알아서 등록하도록!

간단하게 노트하면,

  1. 트위터 개발자 홈페이지에 접속해서 개발자로 신청하기
  2. App 만들고, OAuth Key 발급 받기

2번 프로세스에 대해 다른 블로그들 중 설명이 잘 되어있는 곳을 찾았다. Mark Lee 님의 블로그를 참고해서 키를 발급 받아 옵시다..!!

위와 같이 키를 확인할 수 있다면, 준비는 끝났다.

트위터 API 연결하기

이제 파이참 또는 주피터를 열 시간이다.
본격적으로 트위터를 수집하기 전, 위에서 발급받은 인증키를 연동시켜, 제대로 연결되었는지 status를 먼저 확인해 보자.

트위터 공식 API 문서에서는 다양한 형태의 OAuth를 지원하는데, 우리는 OAuth2 를 사용하여 인증할 것이다.

1
2
3
4
5
6
7
8
9
10
import base64

# 트위터 API 개발자 키를 아래에 입력
client_key = 'YOUR-CLIENT-KEY'
client_secret = 'YOUR-CLIENT-SECRET-KEY'

# b64 encoded 형태로 만드는 과정
key_secret = '{}:{}'.format(client_key, client_secret).encode('ascii')
b64_encoded_key = base64.b64encode(key_secret)
b64_encoded_key = b64_encoded_key.decode('ascii')

위에서는 b64 형태로 인코딩 된 키를 만들었다. 이제 이를 통해서 트위터 API와 연결하는 코드를 작성한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import requests

# request에 필요한 url 만들기
base_url = 'https://api.twitter.com/'
auth_url = '{}oauth2/token'.format(base_url)

# HEADER 구성하기
auth_headers = {
'Authorization': 'Basic {}'.format(b64_encoded_key),
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
}

# Authentication Data section 만들기
auth_data = {
'grant_type': 'client_credentials'
}

# POST request를 보내서 status 확인!
auth_resp = requests.post(auth_url, headers=auth_headers, data=auth_data)
print(auth_resp.status_code)

status_code 가 200이 출력되면, 정상적으로 연결된 것이다.

데이터 수집하기

위의 코드까지는 세팅이라고 할 수 있으며, 지금부터가 실제 트위터 검색에 사용될 파라미터를 정의하는 구간이다.
코로나 바이러스와 연관된 트윗을 수집하는 것이 목표이므로, 키워드를 “우한폐렴”“코로나” 라고 정했다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# Bearer token 정의하기
access_token = auth_resp.json()['access_token']

# Search HEADER 구성하기
search_headers = {
'Authorization': 'Bearer {}'.format(access_token)
}

# SEARCH TWEET
# Maximum number of tweets returned from a single token is 18,000

search_params = {
'q':'우한폐렴 OR 코로나',
'result_type': 'recent', # 'mixed' or 'popular' 로도 지정 가능
'count':10, # 디폴트 값은 15이며, 최대 100까지 지정 가능
'retryonratelimit':True, # rate limit에 도달했을 때 자동으로 다시 trial
}

search_url = '{}1.1/search/tweets.json'.format(base_url)
search_resp = requests.get(
search_url, headers=search_headers,
params=search_params
)

위 코드를 살행하면, search_resp에 우리가 원하는 결과값이 저장된다. 위에서는 간단하게 “최근 10개의 우한폐렴 또는 코로나 라는 단어가 포함된 트윗을 가져와!” 라고 search_params을 지정했지만, 더 다양한 옵션들이 존재한다. 궁금하면 트위터 공식 API 문서를 참고하자.

Rate Limit 확인하기

위에서는 간단하게 10개의 트윗만 수집했지만, 1000개 또는 그 이상의 트윗을 수집하려는 사용자도 분명 있을 것이다. (물론 나도 위 방법으로 100만개 이상을 수집해 왔으니..) 이런 경우에는 트위터에서 명시해 놓은 Rate Limit에 대해서 민감하게 코드를 작성할 필요가 있다.

방금 위 코드를 실행시켰다면, 트위터 서버에 1번 데이터를 요청한 셈이 된다. 트위터에서 얼마나 요청을 받아줄까?
아래의 코드는 rate limit을 확인할 수 있게 해준다.

1
2
3
4
5
6
# rate limit URL
url = 'https://api.twitter.com/1.1/application/rate_limit_status.json'
search_resp = requests.get(url, headers=search_headers)

# 확인하기
json.loads(search_resp.content)['resources']['search']

위 코드를 그대로 실행시키면, 트위터에서 제공하는 limit가 얼마이고 이 중 현재 남은 사용량이 얼마나 되는지 직관적으로 확인할 수 있다.

다만, 현재는 출력되는 limit가 450 으로 나오는데 직접 코드를 돌려보니 허용량을 초과하지 않았는데도 크롤러가 멈추는 현상이 있었다. 정확하게 확인하려면 트위터 공식 API Ref을 참고하자. 여기서는 15분 당 180번 요청 허용 이라고 나와 있었다.

참고로, 나는 search_resp.content 내의 정보를 확인하고, error가 존재하는 경우 크롤러를 강제로 15분동안 쉬도록 코드를 작성해서 사용했다.

데이터 확인하기

데이터 수집은 완료했고.. 이제 수집된 데이터를 확인해보자.
10개의 트윗을 예시로 수집하였고, 이를 pandas로 읽으면 편하다.

1
2
3
4
5
6
7
import json
import pandas as pd
Data = json.loads(search_resp.content)

# Dict -> Dataframe
df = pd.DataFrame(Data['statuses'])
df[["id",'created_at','text','retweet_count']] # 몇 개의 칼럼만 확인해보기

잘 보인다! 총 28개의 칼럼이 있는데, 이 중 관심이 있는 4개의 칼럼만 예시로 출력한 것이다. GOOD!!

결론

이런 식으로 패키지 없이 트위터에서 제공하는 공식 API로만 트위터를 수집할 수 있다.
다만, 공짜로 이용하는 것이기 때문에 기본적으로 standard API와 동일한 제약이 따른다. 무엇인고 하니, rate limit와 같은 속성도 있지만 무엇보다도 full-archive를 보장받지 못한다. 즉, 2월 20일자 트윗 중 “우한폐렴” 및 “코로나” 가 포함된 트윗을 모두 수집하라고 코드를 실행시켜도 모든 트윗을 긁어왔다고 보장해 주지 않는다.

다시 한번 한계점을 정리해 본다면,

  1. 트윗을 성공적으로 크롤링 하여도, 기간 내 모든 트윗을 수집하였다고 보장하지 못한다.
  2. Standard API의 한계 때문에, 오늘을 기준으로 7일 이전의 데이터만 크롤링이 가능하다.

한계점을 극복하려면, 어쩔수 없이 다른 파이썬 패키지 또는 Premium API를 결제해야 한다.
그러나 미리미리 트위터를 주기적으로 크롤링 해 놓는다면, 공짜로 양질의 데이터를 손쉽게 얻을 수 있을 것이다.

아래는 트윗 10개 말고 100개를 긁어왔을 때 간단하게 frequency를 1초 별로 그려본 것이다. 예쁘게 잘 나오는군 후후

끝!