Skip to content

Commit 7c57116

Browse files
committed
Move Python script to ipynb
1 parent ea289ca commit 7c57116

File tree

2 files changed

+153
-120
lines changed

2 files changed

+153
-120
lines changed
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "code",
5+
"execution_count": null,
6+
"metadata": {},
7+
"outputs": [],
8+
"source": [
9+
"\"\"\"\n",
10+
"Copyright (c) 2018 Randal S. Olson\n",
11+
"\n",
12+
"Permission is hereby granted, free of charge, to any person obtaining a copy of this software\n",
13+
"and associated documentation files (the \"Software\"), to deal in the Software without restriction,\n",
14+
"including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,\n",
15+
"and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,\n",
16+
"subject to the following conditions:\n",
17+
"\n",
18+
"The above copyright notice and this permission notice shall be included in all copies or substantial\n",
19+
"portions of the Software.\n",
20+
"\n",
21+
"THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT\n",
22+
"LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n",
23+
"IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\n",
24+
"WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n",
25+
"SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n",
26+
"\"\"\"\n",
27+
"\n",
28+
"from __future__ import print_function\n",
29+
"import time\n",
30+
"from datetime import datetime\n",
31+
"import os\n",
32+
"\n",
33+
"from twitter import Twitter, OAuth, TwitterHTTPError\n",
34+
"from tqdm import tqdm\n",
35+
"import pandas as pd\n",
36+
"import matplotlib.pyplot as plt\n",
37+
"\n",
38+
"USER_TO_ANALYZE = ''\n",
39+
"OAUTH_TOKEN = ''\n",
40+
"OAUTH_SECRET = ''\n",
41+
"CONSUMER_KEY = ''\n",
42+
"CONSUMER_SECRET = ''\n",
43+
"\n",
44+
"twitter_connection = Twitter(auth=OAuth(OAUTH_TOKEN, OAUTH_SECRET, CONSUMER_KEY, CONSUMER_SECRET))\n",
45+
"\n",
46+
"pbar = tqdm()\n",
47+
"pbar.write('Collecting list of Twitter followers for @{}'.format(USER_TO_ANALYZE))\n",
48+
"\n",
49+
"rl_status = twitter_connection.application.rate_limit_status()\n",
50+
"if rl_status['resources']['followers']['/followers/ids']['remaining'] <= 0:\n",
51+
" sleep_until = rl_status['resources']['followers']['/followers/ids']['reset']\n",
52+
" sleep_for = int(sleep_until - time.time()) + 10 # wait a little extra time just in case\n",
53+
" if sleep_for > 0:\n",
54+
" pbar.write('Sleeping for {} seconds...'.format(sleep_for))\n",
55+
" time.sleep(sleep_for)\n",
56+
" pbar.write('Awake!')\n",
57+
"\n",
58+
"followers_status = twitter_connection.followers.ids(screen_name=USER_TO_ANALYZE)\n",
59+
"followers = followers_status['ids']\n",
60+
"next_cursor = followers_status['next_cursor']\n",
61+
"pbar.update(len(followers))\n",
62+
"\n",
63+
"while next_cursor != 0:\n",
64+
" rl_status = twitter_connection.application.rate_limit_status()\n",
65+
" if rl_status['resources']['followers']['/followers/ids']['remaining'] <= 0:\n",
66+
" sleep_until = rl_status['resources']['followers']['/followers/ids']['reset']\n",
67+
" sleep_for = int(sleep_until - time.time()) + 10 # wait a little extra time just in case\n",
68+
" if sleep_for > 0:\n",
69+
" pbar.write('Sleeping for {} seconds...'.format(sleep_for))\n",
70+
" time.sleep(sleep_for)\n",
71+
" pbar.write('Awake!')\n",
72+
"\n",
73+
" followers_status = twitter_connection.followers.ids(screen_name=USER_TO_ANALYZE, cursor=next_cursor)\n",
74+
" # Prevent duplicate Twitter user IDs\n",
75+
" more_followers = [follower for follower in followers_status['ids'] if follower not in followers]\n",
76+
" followers += more_followers\n",
77+
" next_cursor = followers_status['next_cursor']\n",
78+
"\n",
79+
" pbar.update(len(more_followers))\n",
80+
"\n",
81+
"pbar.close()\n",
82+
"\n",
83+
"pbar = tqdm(total=len(followers))\n",
84+
"pbar.write('Collecting join dates of Twitter followers for @{}'.format(USER_TO_ANALYZE))\n",
85+
"followers_created = list()\n",
86+
"\n",
87+
"rl_status = twitter_connection.application.rate_limit_status()\n",
88+
"remaining_calls = rl_status['resources']['users']['/users/lookup']['remaining']\n",
89+
"\n",
90+
"for base_index in range(0, len(followers), 100):\n",
91+
" if remaining_calls == 50:\n",
92+
" # Update the remaining calls count just in case\n",
93+
" rl_status = twitter_connection.application.rate_limit_status()\n",
94+
" remaining_calls = rl_status['resources']['users']['/users/lookup']['remaining']\n",
95+
"\n",
96+
" if remaining_calls <= 0:\n",
97+
" sleep_until = rl_status['resources']['users']['/users/lookup']['reset']\n",
98+
" sleep_for = int(sleep_until - time.time()) + 10 # wait a little extra time just in case\n",
99+
" if sleep_for > 0:\n",
100+
" pbar.write('Sleeping for {} seconds...'.format(sleep_for))\n",
101+
" time.sleep(sleep_for)\n",
102+
" pbar.write('Awake!')\n",
103+
" rl_status = twitter_connection.application.rate_limit_status()\n",
104+
" remaining_calls = rl_status['resources']['users']['/users/lookup']['remaining']\n",
105+
"\n",
106+
" remaining_calls -= 1\n",
107+
"\n",
108+
" # 100 users per request\n",
109+
" user_info = twitter_connection.users.lookup(user_id=list(followers[base_index:base_index + 100]))\n",
110+
" followers_created += [x['created_at'] for x in user_info]\n",
111+
"\n",
112+
" pbar.update(len(followers[base_index:base_index + 100]))\n",
113+
"\n",
114+
"pbar.close()\n",
115+
"print('Creating Follower Factory visualization for @{}'.format(USER_TO_ANALYZE))\n",
116+
"\n",
117+
"days_since_2006 = [(x.year - 2006) * 365 + x.dayofyear for x in pd.to_datetime(followers_created)]\n",
118+
"\n",
119+
"mpl_style_url = 'https://gist.githubusercontent.com/rhiever/d0a7332fe0beebfdc3d5/raw/1b807615235ff6f4c919b5b70b01a609619e1e9c/tableau10.mplstyle'\n",
120+
"with plt.style.context(mpl_style_url):\n",
121+
" plt.figure(figsize=(9, 12))\n",
122+
" plt.scatter(x=range(len(days_since_2006)), y=days_since_2006[::-1], s=2, alpha=0.1 * (80000. / len(days_since_2006)))\n",
123+
" plt.yticks(range(0, 365 * (datetime.today().year + 1 - 2006), 365), range(2006, datetime.today().year + 1))\n",
124+
" plt.xlabel('Follower count for @{}'.format(USER_TO_ANALYZE))\n",
125+
" plt.ylabel('Date follower joined Twitter')\n",
126+
" plt.savefig('{}-follower-factory.png'.format(USER_TO_ANALYZE))\n",
127+
"\n",
128+
"print('Follower Factory visualization saved to {}'.format(os.getcwd()))"
129+
]
130+
}
131+
],
132+
"metadata": {
133+
"kernelspec": {
134+
"display_name": "Python 3",
135+
"language": "python",
136+
"name": "python3"
137+
},
138+
"language_info": {
139+
"codemirror_mode": {
140+
"name": "ipython",
141+
"version": 3
142+
},
143+
"file_extension": ".py",
144+
"mimetype": "text/x-python",
145+
"name": "python",
146+
"nbconvert_exporter": "python",
147+
"pygments_lexer": "ipython3",
148+
"version": "3.6.5"
149+
}
150+
},
151+
"nbformat": 4,
152+
"nbformat_minor": 2
153+
}

follower-factory/follower_factory.py

Lines changed: 0 additions & 120 deletions
This file was deleted.

0 commit comments

Comments
 (0)