Skip to content

Commit d6eaf65

Browse files
author
Claudio
committed
initial commit
0 parents  commit d6eaf65

File tree

3 files changed

+226
-0
lines changed

3 files changed

+226
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.svn

__init__.py

Whitespace-only changes.

bitly.py

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
#!/usr/bin/python2.4
2+
#
3+
# Copyright 2009 Empeeric LTD. All Rights Reserved.
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
import simplejson
18+
import urllib,urllib2
19+
import urlparse
20+
import string
21+
22+
BITLY_BASE_URL = "http://api.bit.ly/"
23+
BITLY_API_VERSION = "2.0.1"
24+
25+
VERBS_PARAM = {
26+
'shorten':'longUrl',
27+
'expand':'shortUrl',
28+
'info':'shortUrl',
29+
'stats':'shortUrl',
30+
'errors':'',
31+
}
32+
33+
class BitlyError(Exception):
34+
'''Base class for bitly errors'''
35+
36+
@property
37+
def message(self):
38+
'''Returns the first argument used to construct this error.'''
39+
return self.args[0]
40+
41+
class Api(object):
42+
""" API class for bit.ly """
43+
def __init__(self, login, apikey):
44+
self.login = login
45+
self.apikey = apikey
46+
self._urllib = urllib2
47+
48+
def shorten(self,longURLs,params={}):
49+
"""
50+
Takes either:
51+
A long URL string and returns shortened URL string
52+
Or a list of long URL strings and returns a list of shortened URL strings.
53+
"""
54+
want_result_list = True
55+
if not isinstance(longURLs, list):
56+
longURLs = [longURLs]
57+
want_result_list = False
58+
59+
for index,url in enumerate(longURLs):
60+
if not '://' in url:
61+
longURLs[index] = "http://" + url
62+
63+
request = self._getURL("shorten",longURLs,params)
64+
result = self._fetchUrl(request)
65+
json = simplejson.loads(result)
66+
self._CheckForError(json)
67+
68+
results = json['results']
69+
res = [self._extract_short_url(results[url]) for url in longURLs]
70+
71+
if want_result_list:
72+
return res
73+
else:
74+
return res[0]
75+
76+
def _extract_short_url(self,item):
77+
if item['shortKeywordUrl'] == "":
78+
return item['shortUrl']
79+
else:
80+
return item['shortKeywordUrl']
81+
82+
def expand(self,shortURL,params={}):
83+
""" Given a bit.ly url or hash, return long source url """
84+
request = self._getURL("expand",shortURL,params)
85+
result = self._fetchUrl(request)
86+
json = simplejson.loads(result)
87+
self._CheckForError(json)
88+
return json['results'][string.split(shortURL, '/')[-1]]['longUrl']
89+
90+
def info(self,shortURL,params={}):
91+
"""
92+
Given a bit.ly url or hash,
93+
return information about that page,
94+
such as the long source url
95+
"""
96+
request = self._getURL("info",shortURL,params)
97+
result = self._fetchUrl(request)
98+
json = simplejson.loads(result)
99+
self._CheckForError(json)
100+
return json['results'][string.split(shortURL, '/')[-1]]
101+
102+
def stats(self,shortURL,params={}):
103+
""" Given a bit.ly url or hash, return traffic and referrer data. """
104+
request = self._getURL("stats",shortURL,params)
105+
result = self._fetchUrl(request)
106+
json = simplejson.loads(result)
107+
self._CheckForError(json)
108+
return Stats.NewFromJsonDict(json['results'])
109+
110+
def errors(self,params={}):
111+
""" Get a list of bit.ly API error codes. """
112+
request = self._getURL("errors","",params)
113+
result = self._fetchUrl(request)
114+
json = simplejson.loads(result)
115+
self._CheckForError(json)
116+
return json['results']
117+
118+
def setUrllib(self, urllib):
119+
'''Override the default urllib implementation.
120+
121+
Args:
122+
urllib: an instance that supports the same API as the urllib2 module
123+
'''
124+
self._urllib = urllib
125+
126+
def _getURL(self,verb,paramVal,more_params={}):
127+
if not isinstance(paramVal, list):
128+
paramVal = [paramVal]
129+
130+
params = {
131+
'version':BITLY_API_VERSION,
132+
'format':'json',
133+
'login':self.login,
134+
'apiKey':self.apikey,
135+
}
136+
137+
params.update(more_params)
138+
params = params.items()
139+
140+
verbParam = VERBS_PARAM[verb]
141+
if verbParam:
142+
for val in paramVal:
143+
params.append(( verbParam,val ))
144+
145+
encoded_params = urllib.urlencode(params)
146+
return "%s%s?%s" % (BITLY_BASE_URL,verb,encoded_params)
147+
148+
def _fetchUrl(self,url):
149+
'''Fetch a URL
150+
151+
Args:
152+
url: The URL to retrieve
153+
154+
Returns:
155+
A string containing the body of the response.
156+
'''
157+
158+
# Open and return the URL
159+
url_data = self._urllib.urlopen(url).read()
160+
return url_data
161+
162+
def _CheckForError(self, data):
163+
"""Raises a BitlyError if bitly returns an error message.
164+
165+
Args:
166+
data: A python dict created from the bitly json response
167+
Raises:
168+
BitlyError wrapping the bitly error message if one exists.
169+
"""
170+
# bitly errors are relatively unlikely, so it is faster
171+
# to check first, rather than try and catch the exception
172+
if 'ERROR' in data or data['statusCode'] == 'ERROR':
173+
raise BitlyError, data['errorMessage']
174+
for key in data['results']:
175+
if type(data['results']) is dict and type(data['results'][key]) is dict:
176+
if 'statusCode' in data['results'][key] and data['results'][key]['statusCode'] == 'ERROR':
177+
raise BitlyError, data['results'][key]['errorMessage']
178+
179+
class Stats(object):
180+
'''A class representing the Statistics returned by the bitly api.
181+
182+
The Stats structure exposes the following properties:
183+
status.user_clicks # read only
184+
status.clicks # read only
185+
'''
186+
187+
def __init__(self,user_clicks=None,total_clicks=None):
188+
self.user_clicks = user_clicks
189+
self.total_clicks = total_clicks
190+
191+
@staticmethod
192+
def NewFromJsonDict(data):
193+
'''Create a new instance based on a JSON dict.
194+
195+
Args:
196+
data: A JSON dict, as converted from the JSON in the bitly API
197+
Returns:
198+
A bitly.Stats instance
199+
'''
200+
return Stats(user_clicks=data.get('userClicks', None),
201+
total_clicks=data.get('clicks', None))
202+
203+
204+
if __name__ == '__main__':
205+
testURL1="www.yahoo.com"
206+
testURL2="www.cnn.com"
207+
a=Api(login="pythonbitly",apikey="R_06871db6b7fd31a4242709acaf1b6648")
208+
short=a.shorten(testURL1)
209+
print "Short URL = %s" % short
210+
short=a.shorten(testURL1,{'history':1})
211+
print "Short URL with history = %s" % short
212+
urlList=[testURL1,testURL2]
213+
shortList=a.shorten(urlList)
214+
print "Short URL list = %s" % shortList
215+
long=a.expand(short)
216+
print "Expanded URL = %s" % long
217+
info=a.info(short)
218+
print "Info: %s" % info
219+
stats=a.stats(short)
220+
print "User clicks %s, total clicks: %s" % (stats.user_clicks,stats.total_clicks)
221+
errors=a.errors()
222+
print "Errors: %s" % errors
223+
testURL3=["www.google.com"]
224+
short=a.shorten(testURL3)
225+
print "Short url in list = %s" % short

0 commit comments

Comments
 (0)