GIL's LAB

tweepy를 이용한 트윗 데이터 수집 방법 본문

파이썬/데이터 분석을 위한 파이썬

tweepy를 이용한 트윗 데이터 수집 방법

GIL~ 2022. 8. 28. 15:21

이번 시간에는 tweepy라는 트위터 API에 접근할 수 있는 파이썬 패키지를 이용해서 트위터 데이터를 수집하는 방법에 대해 알아보겠습니다. 구체적으로 수집하고자 하는 데이터는 다음과 같습니다.

  • 유저의 팔로워 목록
  • 유저의 트윗 목록
  • 유저의 팔로워 수 등

tweepy 공식 문서는 아래에서 확인할 수 있습니다.

https://docs.tweepy.org/en/stable/

 

Tweepy Documentation — tweepy 4.10.1 documentation

© Copyright 2009-2022, Joshua Roesslein. Revision ad5e31be.

docs.tweepy.org

 

사실 많은 블로그에서 tweepy를 사용해서 트윗 데이터를 수집하는 방법을 소개했지만, 옛날 글이 많아 최신 버전을 다루는게 없어서 이렇게 새로 정리하는 것입니다. 

특히 다수의 데이터를 수집하는 과정에서 발생하는 이슈 등을 해결하는 방법도 같이 정리하고자 합니다.

 

tweepy 설치

tweepy는 pip을 이용해서 아래와 같이 간단히 설치할 수 있습니다.

pip install tweepy

정상적으로 설치됐는지 확인하기 위해 아래와 같이 tweepy 패키지를 임포트합니다.

import tweepy

 

bearer_token 얻기

트위터 API를 사용하려면 bearer_token이라는 토큰 값을 얻어야 합니다.

이 토큰은 tweepy.Client라는 클래스의 인자로 사용됩니다.

 

먼저 트위터 Developer Platform에 접속합니다.

https://developer.twitter.com/en

 

Use Cases, Tutorials, & Documentation

Publish & analyze Tweets, optimize ads, & create unique customer experiences with the Twitter API, Twitter Ads API, & Twitter Embeds.

developer.twitter.com

접속하면 아래와 같은 화면이 보이는데, 우측 상단에 있는 Sign In을 눌러서 트위터에 로그인합니다.

만약 트위터 ID가 없다면 Sign Up을 눌러 가입합니다.

저는 이미 ID를 만들어놨기 때문에 다음과 같이 로그인하겠습니다.

로그인하고나서 우측 상단에 있는 Developer Portal을 클릭해줍니다.

처음 클릭한 것이라면 아래와 같은 화면이 뜹니다. 여기서 Email과 국적, 사용 목적 등을 입력해주고 Let's do this를 클릭합니다.

다음으로 Developer agreement & policy 화면이 나오는데, Accept Terms & Conditions을 누르고 Submit을 누릅니다.

이제 메일을 verify하라는 메시지가 뜹니다.

입력한 메일로 가면 다음과 같은 메일이 와있을 것입니다.

 

참고로 전 네이버메일을 사용했는데 그러면 SNS 폴더에 가 있으니 잘 확인하셔야 합니다.

스팸으로 가는 경우도 있을 것 같습니다.

 

이제 Confirm your email을 눌러줍니다. 

이 메일을 누르면 다음과 같은 화면이 뜹니다.

App name을 입력하라 되어 있는데, 자신의 목적에 맞게 적절하게 앱 이름을 정해줍니다.

저는 Gils_LAB로 입력했고 Get keys를 누릅니다. 참고로 다른 App 이름과 겹치면 안되므로 유니크하게 설정해줘야 합니다.

 

이제 키를 다음과 같이 줍니다. 

여기에 써있는 것처럼 이 키들은 일종의 개인정보기 때문에 파란색으로 가려놨습니다.

실제 사용을 위해서 각 키를 어디다 저장해두는 것이 좋습니다.

 

Access levels 올리기

위의 내용으로 얻은 키로는 매월 50만 개의 데이터만 수집할 수 있습니다. 

크게 문제가 될 만한 사이즈는 아니지만, 학술적 목적 등으로 대용량 데이터를 얻기에는 좀 부족합니다.

프리미엄 결제를 해도 되지만 학술적 목적(academic research access)으로 사용하면 조금 더 많은 데이터 접근이 가능합니다. 

아래 링크에 접속하여 Ready to move forward? 우측에 있는 Apply ->를 눌러줍니다.

https://developer.twitter.com/en/products/twitter-api/academic-research

 

Twitter API for Academic Research | Products

The Twitter API product track for academic research offers precise, complete, and unbiased public data. Apply today and support your research with data on nearly any topic.

developer.twitter.com

 

Apply 버튼을 누르면 다음과 같은 화면이 뜹니다. 하단에 써있는 것처럼 상업적 이용은 불가능하다는 점 주의하시고 Start Academic Research application을 눌러줍니다.

그러면 다음과 같이 국적과 코딩 레벨을 물어봅니다.

코딩 레벨을 왜 물어보는진 모르겠지만, Highly experienced를 선택한 뒤 Next를 누릅니다.

이제 Academic profile을 입력하라고 나옵니다. 자신의 소속 학교 등을 입력해주고 Next를 누릅니다.

다음으로 Project details를 입력하라고 나옵니다.

여기에는 적당히 써주시면 됩니다.

작성이 완료되면 Next를 누르고 다음으로 나오는 Review 부분 역시 Next를 누릅니다.

그러면 마지막으로 Developer agreement & policy이 나오는데, 박스를 클릭 후에 Submit을 누릅니다.

그러면 다음과 같은 메일이 오고 확정이 될 때까지 기다려주면 됩니다.

 

 

클라이언트 객체 생성

앞서 간단히 이야기한대로 트위터 API를 사용하려면 클라이언트 객체를 만들어야 합니다.

클라이언트 객체는 tweepy.Client를 이용하여 다음과 같이 만들 수 있습니다.

bearer_token = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
client = tweepy.Client(bearer_token)

위 코드에서 bearer_token에는 자신의 토큰을 입력해주면 됩니다.

 

사용자 ID 얻기

Tweepy의 대부분 함수에서는 사용자 ID를 입력으로 받습니다.

그런데 이 사용자 ID는 화면에서 보이는 ID(이를 screen ID라 합니다)가 아니라, 트위터 내부에서 사용하는 ID를 의미합니다. 

예를 들어, 일론 머스크의 트윗에서 @elonmusk라 표시된 부분이 screen ID입니다.  

 

사용자ID를 얻으려면 get_user 메서드를 사용해야 합니다.

get_user 메서드를 사용해서 elonmusk의 사용자 ID를 얻어보겠습니다.

client.get_user(username="elonmusk")

[실행 결과]

Response(data=<User id=44196397 name=Elon Musk username=elonmusk>, includes={}, errors=[], meta={})

실행 결과를 보면 Response 객체인 것을 알 수 있습니다. 

이 객체는 data, includes, errors, meta 속성으로 구성되어 있는데, 우리가 관심있는 것은 data 속성입니다. 

data 속성은 User id, name, username으로 구분되어 있는데 여기서 user id가 사용자 ID입니다. 

따라서 사용자 ID는 다음과 같이 얻을 수 있습니다.

t_id = client.get_user(username="elonmusk").data.id
t_id

이 과정을 함수화하면 다음과 같습니다. 

import time
def get_id(screen_name):
    # 스크린에 나오는 id를 바탕으로 tweepy에서 사용하는 id를 획득
    while True:
        try:
            t_id = client.get_user(username=screen_name).data.id
            break
        except:
            print("id를 획득하는 도중 연결이 끊겨 15분간 잡니다.")
            time.sleep(15 * 60)
        
    return t_id

함수 자체는 어렵지 않지만 왜 이런 구조의 함수가 필요한지가 중요합니다. 참고로 이 코드 구조는 대부분 크롤링 코드에서 활용됩니다.

tweepy는 메서드마다 특정 횟수 이상 요청을 하면 연결이 끊어지도록 설정되어 있으며, 15분이 지나면 다시 재연결됩니다. 

예를 들어, get_user 메서드의 최대 사용 횟수가 100번이라면 100번을 사용하고 나면 연결이 끊깁니다. 그래서 get_id 메서드를 문제없이 쓰려면 일단 get user 메서드를 사용해보고 에러가 발생하면 15분을 재우는 것입니다. 

코드를 보면 while True를 사용하여 무한 루프에 빠치고, try 구문에서 t_id를 정상적으로 얻으면 break를 통해 빠져나오되 문제가 생기면 except 구문에서 15분을 재우게 됩니다.

15분 후에는 while문을 빠져나오지 못했으므로 다시 t_id를 얻게 됩니다. 

 

 

트윗 데이터 수집

트윗은 get_users_tweets 메서드를 이용해서 수집할 수 있습니다.

이 메서드는 사용자 ID를 입력받으며, 1회 요청에 10개의 트윗을 반환합니다.

max_results라는 인자를 통해 1회에 받을 수 있는 트윗 수를 설정할 수 있으며, 최대 100건의 트윗을 수집할 수 있습니다.

tweets = client.get_users_tweets(t_id, max_results = 100)
tweets

[실행 결과]

Response(data=[<Tweet id=1563744697578704896 text='@culturaltutor Maybe it’s times for new roman'>, <Tweet id=1563743845296132096 text='@slashdot Getting to orbit &amp; back is harder than it may seem'>, <Tweet id=1563338817154666497 text='@RenataKonkoly The caffeine one is most troubling'>, <Tweet id=1563310519934943233 text='@SKMorefield Some are indeed sadly anti-human'>, <Tweet id=1563292201043431424 text='Countries should be increasing nuclear power generation! It is insane from a national security standpoint &amp; bad for the environment to shut them down.'>, <Tweet id=1563291582429687809 text='@teslaownersSV @ICannot_Enough LotR/HG2G'>, <Tweet id=1563291162114670600 text='@ICannot_Enough It is safe to say that our output will not exceed the mass of the known universe'>, <Tweet id=1563255562133209088 text='@ID_AA_Carmack Similarly high cycle life with nickel cathode is possible if optimizing for that.\n\nAdding silicon to (primarily carbon) anode, which improves energy density, reduces cycle life, due to large volumetric changes during charge/discharge.'>, <Tweet id=1563221980220116994 text='@BillyM2k True'>, <Tweet id=1563162322239373315 text='@MatchasmMatt @jasondebolt @heydave7 That, plus pessimism about the future / false overpopulation fears'>, <Tweet id=1563161958106660865 text='@heydave7 Yes'>, <Tweet id=1563021346510372865 text='(And I do think global warming is a major risk)'>, <Tweet id=1563021120046919680 text='@teslaownersSV Yes'>, <Tweet id=1563020753876193281 text='Mark these words'>, <Tweet id=1563020169160851456 text='Population collapse due to low birth rates is a much bigger risk to civilization than global warming'>, <Tweet id=1563016064555814914 text='@SciGuySpace Good summary'>, <Tweet id=1562981667480150018 text='@teslaownersSV Yes'>, <Tweet id=1562978042443857921 text='@heydave7 Yes'>, <Tweet id=1562963693490475010 text='Note, connectivity will be 2 to 4 Mbits per cell zone, so will work great for texting &amp; voice calls, but not high bandwidth'>, <Tweet id=1562956451538014209 text='Starlink V2, launching next year, will transmit direct to mobile phones, eliminating dead zones worldwide'>, <Tweet id=1562954616278380544 text='RT @SpaceX: Watch live as Elon and @TMobile CEO and President Mike Sievert announce plans to increase connectivity → https://t.co/pxgvgTWzr…'>, <Tweet id=1562935219644235776 text='Livestream of big news in about an hour https://t.co/LSMIRYBxHH'>, <Tweet id=1562887318825906176 text='@teslaownersSV 10.69.1 releases Monday night. 10.69.2 about a week or so later – this will be the wide beta version.'>, <Tweet id=1562885864820076544 text='@SawyerMerritt @SpaceX Ron Barron is awesome'>, <Tweet id=1562839680747388934 text='@WholeMarsBlog @RealDanODowd 🦇 💩 crazy'>, <Tweet id=1562535862943961088 text='@BBC_Future I would gladly obey their commands, but I can’t tell what they’re saying!'>, <Tweet id=1562521553358581760 text='@RationalEtienne @arctechinc I do not doubt that. Criticism, public or private, is welcome after we go to wide beta. \n\nEarly beta has many known issues. The reason we release it to a limited number of cars is to discover unknown issues.'>, <Tweet id=1562507192342761473 text='@RationalEtienne @arctechinc No. Let me make something clear: James contacted me directly to be included in *early* beta, which is only ~1000 cars, mostly employees. \n\nEarly beta explicitly has issues or it would be rolled out widely, so publicly criticizing something he had asked for is wrong.'>, <Tweet id=1562477813613047811 text='This is something special https://t.co/LSMIRYT8zf'>, <Tweet id=1562340989775511552 text='The course of civilization is not always upward https://t.co/8mr7pFWcnI'>, <Tweet id=1562316090151038977 text='@heydave7 True'>, <Tweet id=1562314849912119296 text='Mechazilla loads Starship on launchpad https://t.co/LfkfjpAcZj'>, <Tweet id=1562314348206297088 text='@DavidSacks 🤣'>, <Tweet id=1562177549500706821 text='@teslaherbert 🤣'>, <Tweet id=1562157209513066501 text='@arctechinc 10.69 is in limited release for a reason. Please do not ask to be included in early beta releases and then complain.'>, <Tweet id=1562142970320756738 text='@spideycyp_155 !!'>, <Tweet id=1562135640598528000 text='So spam prevalence *was* shared with the board, but the board chose not disclose that to the public … https://t.co/lXk48TFZL1'>, <Tweet id=1562133579060092931 text='@RenataKonkoly @AlliyahsimsTV 🤣'>, <Tweet id=1562133293562314752 text='@andst7 Important thread'>, <Tweet id=1562112249698869248 text='@EvaFoxU In case anyone feels like buying a fine whistle …'>, <Tweet id=1562106034042306563 text='@AlliyahsimsTV Yet another “verified”🤖'>, <Tweet id=1562105544692973569 text='@EpochTimes 🧐'>, <Tweet id=1562105413977493504 text='https://t.co/KsWiazActx'>, <Tweet id=1562104998783324160 text='@ID_AA_Carmack You’re waking up in the morning!?'>, <Tweet id=1561840143060828162 text='@chazman @aelluswamy 👍'>, <Tweet id=1561608534629965826 text='Neuralink progress update show &amp; tell on October 31 st (Halloween)'>, <Tweet id=1561607860101996544 text='2 main goals this year: \n\n- Starship to orbit\n- FSD wide release\n\nMany other things, of course, but those are the 2 giant kahunas. Will require insane work by many super talented people, but, if anyone can do it, they can. \n\nIt is an honor to work with such awesome human beings.'>, <Tweet id=1561605094344466432 text='It is the fastest way to get between one downtown and another with known physics (and the Standard Model is proving quiet resilient)!'>, <Tweet id=1561604589962625025 text='Would be cool to do a (much simplified) Hyperloop demo tunnel between maybe Austin &amp; San Antonio?'>, <Tweet id=1561604107726716928 text='Now @boringcompany tunnels are in active use in Vegas. Try it if you’re in town. Will be expanding to connect all major destinations in Vegas plus airport.'>, <Tweet id=1561603367155163136 text='🤣🤣'>, <Tweet id=1561602929957691392 text='Funny how there are often simultaneous reactions saying “it’s impossible” and “it was already done 3000 years ago”'>, <Tweet id=1561475238705401856 text='👿 https://t.co/hdzRzphcii'>, <Tweet id=1561429523899482112 text='@chazman @aelluswamy Glad it’s working out! Thanks for giving us this tough case to solve. That’s what field testing is all about.\n\nThis early version of 10.69 is being extra cautious, so waits for a moderately big gap in traffic to cross. Upcoming releases will do better in heavy traffic.'>, <Tweet id=1561428443484536835 text='@chazman @aelluswamy Just wanted to emphasize again that our awesome Tesla software/AI team is who really deserves the credit. The talent level at Tesla is incredible.'>, <Tweet id=1561362967945531395 text='Note, you can upgrade your existing car to FSD in 2 mins via the Tesla app'>, <Tweet id=1561362640261226499 text='After wide release of FSD Beta 10.69.2, price of FSD will rise to $15k in North America on September 5th. \n\nCurrent price will be honored for orders made before Sept 5th, but delivered later.'>, <Tweet id=1561361064163516417 text='FSD Beta 10.69 started rolling out to Tesla owners last night. This build is a big step forward!\n\n10.69.1 probably end of week with wider release.\n\n1069.2 in a few weeks should be good enough to provide to all FSD Beta participants.'>, <Tweet id=1561332565960806400 text='@BLKMDL3 @Tesla Tesla Autopilot/AI team did great work with 10.69! Some point releases needed for polish, so 10.69.2 should really shine.'>, <Tweet id=1561157556369768448 text='@Teslarati @Tesla @WholeMarsBlog @JohnnaCrider1 🤔'>, <Tweet id=1561157326291222536 text='@aelluswamy Worth hearing about Tesla Autopilot software/AI progress'>, <Tweet id=1561046217840332808 text='@WholeMarsBlog As mentioned previously, this is a major release, so we need to be cautious. Goes out to ~1000 Beta participants later today.'>, <Tweet id=1561032727230648322 text='@WholeMarsBlog 🤣💯👍'>, <Tweet id=1560853365206171648 text="RT @SpaceX: Shots from the @Space_Station of Dragon's departure https://t.co/nK2ZteNy2c">, <Tweet id=1560806937008250880 text='@Andst7 🤣'>, <Tweet id=1560732675899817984 text='Might need a new PO Box after tweeting this 🤣'>, <Tweet id=1560720224588120064 text='Nice letter from Bill Nix, who would’ve been my prof at Stanford if I hadn’t put grad studies on (permanent) deferment https://t.co/ioq0mGwN7K'>, <Tweet id=1560711872344690691 text='@SpaceX Each fairing half is a fully capable reentry vehicle with its own thrusters, thermal protection, avionics &amp; sensor suite'>, <Tweet id=1560709005558910981 text='RT @SpaceX: Watch Falcon 9 launch 53 Starlink satellites to orbit → https://t.co/R2EbNzbkrU  https://t.co/ugqA7fPTVT'>, <Tweet id=1560651065854656513 text='RT @SpaceX: Separation confirmed! Dragon will perform three departure burns to move away from the @space_station https://t.co/kcgap2pxZF'>, <Tweet id=1560634159386992640 text='@Dilbert_Daily Free Willy was never free 😢'>, <Tweet id=1560630340137664513 text='RT @SpaceX: SKY Perfect JSAT has selected Starship for launch of its Superbird-9 communications satellite! https://t.co/sFZssT3omn https://…'>, <Tweet id=1560508081729044480 text='Still so grateful \U0001f979 https://t.co/EhdyXw4fqN'>, <Tweet id=1560503326105649153 text='@teslaownersSV @CChomp13 There are many major code changes, so this will be an extra cautious rollout.\n\nReleasing on 8/20 to ~1000 Tesla owners, then 10.69.1 next week to accommodate feedback &amp; release to ~10k customers, then 10.69.2 week after &amp; release to rest of FSD Beta.'>, <Tweet id=1560499044438495233 text='@CChomp13 Entropy'>, <Tweet id=1560466783567478784 text='https://t.co/KBjnfkU4ia'>, <Tweet id=1560360640044228609 text='@Andst7 Those are the questions that Twitter is doing everything possible to avoid answering …'>, <Tweet id=1560360034583838721 text='@SawyerMerritt @ICannot_Enough Modifying your car after delivery is legal in America, so yes 😀'>, <Tweet id=1560308317255340034 text='@ICannot_Enough That said, we will add autoconfigure to side mirrors'>, <Tweet id=1560294614690660355 text='@ICannot_Enough Side mirrors cause ~5% range reduction at highway speed'>, <Tweet id=1560293928045162497 text='@ICannot_Enough Yes, but side mirrors won’t be needed in a self-driving future'>, <Tweet id=1560279650168983552 text='@DogecoinNorway @micsolana 🤣'>, <Tweet id=1560275630196871171 text='@micsolana 🤣'>, <Tweet id=1560076884154744833 text='@ashleevance 🔥'>, <Tweet id=1560012992338591744 text='@lexfridman 🔥🔥'>, <Tweet id=1559917784280322048 text='@RationalEtienne @MuskUniversity For some reason, I just agree with that guy so much!'>, <Tweet id=1559916646470393856 text='@mayemusk @kimbal @ToscaMusk 💕'>, <Tweet id=1559832948538482690 text='🎶 This Magic Moment 🎶'>, <Tweet id=1559824523704389634 text='@Kristennetten @wonderofscience *shall'>, <Tweet id=1559824351318474753 text='@Kristennetten @wonderofscience I will seek out one of these “Mars Bars” tomorrow!'>, <Tweet id=1559823434028400640 text='Being a Mom is just as important as any career'>, <Tweet id=1559814335320432642 text='Highly Recommend https://t.co/nrDZ4gJqEE'>, <Tweet id=1559810142027354114 text='@waitbutwhy @trevorjacob93 😮'>, <Tweet id=1559809610134986752 text='@wonderofscience Mercury, the forbidden candy'>, <Tweet id=1559805894703161345 text='@MuskUniversity Yes'>, <Tweet id=1559805654918914049 text='@MuskUniversity ♟'>, <Tweet id=1559804716976046081 text='@Elon_Quotes_ Almost all of Earth’s energy comes from the sun already – we would be a dark iceball at near absolute zero if not for the sun. And essentially the entire ecosystem is solar-powered.\n\nCivilization uses a tiny amount of energy by comparison. Not hard to generate from wind/solar.'>, <Tweet id=1559769337912856576 text='@teslaownersSV Although, if it were any team, it would be Man U. They were my fav team as a kid.'>, <Tweet id=1559767523079372802 text='@BillyM2k no u'>, <Tweet id=1559766938498236416 text='Standup is my side-hustle'>], includes={}, errors=[], meta={'next_token': '7140dibdnow9c7btw422nuqe7hzxcb2eudpc0lu0kss3a', 'result_count': 100, 'newest_id': '1563744697578704896', 'oldest_id': '1559766938498236416'})

데이터가 보기 좋은 형태가 아니므로, 트윗 텍스트만 추출해보겠습니다.

tweets의 data 속성에는 각 트윗 데이터가 있고 각 트윗 데이터는  Tweet id와 text 속성이 있습니다.

이 점을 활용하면 다음과 같이 트윗 데이터를 정리할 수 있습니다.

tweet_text_list = [tweet.text for tweet in tweets.data]
tweet_text_list[0]

[실행 결과]

'@culturaltutor Maybe it’s times for new roman'

그런데 문제는 100개의 트윗으로 충분하지 않고 전체 트윗을 수집해야 하는 경우가 있다는 것입니다. 

또한, 100개 트윗은 가장 최근 100개 트윗으로 과거의 트윗이 필요할 수도 있습니다.

이때 활용할 수 있는 것이 Paginator입니다.

Paginator는 tweepy에서 전체 데이터를 수집할 수 있도록 도와주는 클래스로, 데이터 요청 결과를 페이지 목록으로 정리해줍니다. 

예시를 통해 살펴보는 것이 이해에 도움이 될 것 같습니다.

paginator = iter(tweepy.Paginator(client.get_users_tweets, t_id, max_results=100))

위 코드는 get_users_tweets(t_id, max_results)의 모든 결과를 Paginator로 감싸고 이터레이터로 바꾼 것입니다.

next 함수를 쓰기 위해 이터레이터로 변경했는데, next 함수는 이터레이터가 순회할 다음 값을 반환합니다.

즉, 다음과 같이 paginator에서 next를 사용하여 response 값을 가져오고 그 값의 data 속성에서 트윗을 추출할 것입니다.

tweets_list = []
while True: # paginator 순회
    try:
        response = next(paginator)
        for tweets in response.data:
            tweets_list.append(tweets.text)

    except StopIteration: # 이터레이션 종료시
        break

    except: # 과다한 요청으로 인한 오류시 
        print("트윗 페이지에서 연결이 끊겨 15분간 잡니다")
        time.sleep(60 * 15)

코드를 살펴보면 while 구문 아래 try, except StopIteration, except가 있습니다. 

try 구문에는 paginator의 다음 값을 가져옵니다. 

이때 모든 요소를 순회해서 가져올 값이 없다면 StopIteration 오류가 뜨는데, 이 오류가 뜬다는 것은 모든 요소를 순회한 것이므로 while 문에서 빠져나갑니다.

그 외의 오류가 발생한 것은 과다 요청으로 오류가 발생한 것이므로 15분 재웁니다.

 

위 코드를 돌린 결과를 확인해보겠습니다.

print(len(tweets_list))
print(tweets_list[:5])

 

[실행 결과]

3250
['Squeezing extra performance out of Falcon 9 – almost at 17 metric tons to an actual useful orbit with booster &amp; fairing reusable! https://t.co/vgHsmaSj7d', 'RT @SpaceX: Starlink is now available in Norway! Additionally, to better reflect parity in purchasing power across our customers, we’ve adj…', '@culturaltutor Maybe it’s times for new roman', '@slashdot Getting to orbit &amp; back is harder than it may seem', '@RenataKonkoly The caffeine one is most troubling']

3250개의 트윗이 정상적으로 수집됐음을 알 수 있습니다.

위 과정을 함수화하면 다음과 같습니다.

def get_tweets(screen_name):    
    # screen_name을 입력하면 그 유저의 모든 트윗을 획득

    # screen_name을 기준으로 tweepy에서 관리하는 id(t_id) 획득
    t_id = get_id(screen_name)
    
    # 트윗 계산
    tweets_list = []
    while True: # 팔로워 페이지 이터레이터를 얻기 위한 while try except 구문
        try:    
            paginator = iter(tweepy.Paginator(client.get_users_tweets, t_id, max_results=100))
            time.sleep(5)
            break
        except:
            print("트윗 페이지 이터레이터를 얻는 도중 연결이 끊겨 15분간 잡니다")
            time.sleep(60 * 15)
    
    c = 0
    while True: # paginator 순회
        try:
            response = next(paginator)
            c += 1
            for tweets in response.data:
                tweets_list.append(tweets.text)
            time.sleep(0.1)
            
        except StopIteration: # 이터레이션 종료시
            break
            
        except: # 과다한 요청으로 인한 오류시 
            print("트윗 페이지에서 {}번째 팔로워 목록을 받는 도중 연결이 끊겨 15분간 잡니다".format(c))
            time.sleep(60 * 15)
    
    return tweets_list

 

 

팔로워 목록 데이터 수집

이번에는 팔로워 목록을 얻는 함수를 만들어보겠습니다.

get_users_followers 메서드를 사용하면 팔로워 목록을 얻을 수 있으며, max_results에는 1000까지 입력할 수 있습니다.

위 코드와 유사하게 다음과 같이 만들면 됩니다.

def get_followers(screen_name):    
    # screen_name을 입력하면 그 유저의 팔로워 리스트를 반환하는 함수

    # screen_name을 기준으로 tweepy에서 관리하는 id(t_id) 획득
    t_id = get_id(screen_name)
    
    # 팔로워 계산
    followers_list = []
    while True: # 팔로워 페이지 이터레이터를 얻기 위한 while try except 구문
        try:    
            paginator = iter(tweepy.Paginator(client.get_users_followers, t_id, max_results=1000))
            time.sleep(1)
            break
        except:
            print("팔로워 페이지 이터레이터를 얻는 도중 연결이 끊겨 15분간 잡니다")
            time.sleep(60 * 15)
    
    c = 0
    while True: # paginator 순회
        try:
            response = next(paginator)
            c += 1
            for followers in response.data:
                followers_list.append(followers.username)
            time.sleep(1)
            
        except StopIteration: # 이터레이션 종료시
            break
            
        except: # 과다한 요청으로 인한 오류시 
            print("팔로워 페이지에서 {}번째 팔로워 목록을 받는 도중 연결이 끊겨 15분간 잡니다".format(c))
            time.sleep(60 * 15)
    
    return followers_list

위 함수를 이용하여 일론머스크의 팔로워 목록을 다음과 같이 얻을 수 있습니다.

followers_list = get_followers("elonmusk")

현 시점을 기준으로 104.3M (약 1억명) 팔로워가 있으므로 위 코드를 실행하면 어마무시하게 오랜 시간이 걸릴 것입니다.

코드 수집에 오래 걸리기보단 중간에 끊겨서 오래 걸립니다.

 

Comments