-
-
Save Tatsh/5073663 to your computer and use it in GitHub Desktop.
Modified to read `~/.bitly` file. PEP8 conformance.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python2 | |
# | |
# Copyright 2009 Empeeric LTD. All Rights Reserved. | |
# | |
# Modified 2013 by Andrew Udvare | |
# | |
# Licensed under the Apache License, Version 2.0 (the "License"); | |
# you may not use this file except in compliance with the License. | |
# You may obtain a copy of the License at | |
# | |
# http://www.apache.org/licenses/LICENSE-2.0 | |
# | |
# Unless required by applicable law or agreed to in writing, software | |
# distributed under the License is distributed on an "AS IS" BASIS, | |
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
# See the License for the specific language governing permissions and | |
# limitations under the License. | |
import urllib | |
import urllib2 | |
import urlparse | |
import string | |
import sys | |
import os | |
try: | |
import simplejson | |
except ImportError: | |
import json as simplejson | |
# Register an account at bit.ly and then copy your username and key from here: | |
# https://bitly.com/a/settings/advanced (click Show legacy API key) | |
# Make a file ~/.bitly with first line as user name and second as API key | |
try: | |
with open(os.environ['HOME'] + '/.bitly') as f: | |
lines = f.readlines() | |
API_USERNAME = lines[0].strip() | |
API_KEY = lines[1].strip() | |
except IOError: | |
print 'Be sure ~/.bitly exists and has the proper format' | |
sys.exit(1) | |
BITLY_BASE_URL = "http://api.bit.ly/" | |
BITLY_API_VERSION = "2.0.1" | |
VERBS_PARAM = { | |
'shorten': 'longUrl', | |
'expand': 'shortUrl', | |
'info': 'shortUrl', | |
'stats': 'shortUrl', | |
'errors': '', | |
} | |
class BitlyError(Exception): | |
'''Base class for bitly errors''' | |
@property | |
def message(self): | |
'''Returns the first argument used to construct this error.''' | |
return self.args[0] | |
class Api(object): | |
""" API class for bit.ly """ | |
def __init__(self, login, apikey): | |
self.login = login | |
self.apikey = apikey | |
self._urllib = urllib2 | |
def shorten(self, longURLs, params={}): | |
""" | |
Takes either: | |
A long URL string and returns shortened URL string | |
Or a list of long URL strings and returns a list of shortened | |
URL strings. | |
""" | |
want_result_list = True | |
if not isinstance(longURLs, list): | |
longURLs = [longURLs] | |
want_result_list = False | |
for index, url in enumerate(longURLs): | |
if not '://' in url: | |
longURLs[index] = "http://" + url | |
request = self._getURL("shorten", longURLs, params) | |
result = self._fetchUrl(request) | |
json = simplejson.loads(result) | |
self._CheckForError(json) | |
results = json['results'] | |
res = [self._extract_short_url(results[url]) for url in longURLs] | |
if want_result_list: | |
return res | |
else: | |
return res[0] | |
def _extract_short_url(self, item): | |
if item['shortKeywordUrl'] == "": | |
return item['shortUrl'] | |
else: | |
return item['shortKeywordUrl'] | |
def expand(self, shortURL, params={}): | |
""" Given a bit.ly url or hash, return long source url """ | |
request = self._getURL("expand", shortURL, params) | |
result = self._fetchUrl(request) | |
json = simplejson.loads(result) | |
self._CheckForError(json) | |
return json['results'][string.split(shortURL, '/')[-1]]['longUrl'] | |
def info(self, shortURL, params={}): | |
""" | |
Given a bit.ly url or hash, | |
return information about that page, | |
such as the long source url | |
""" | |
request = self._getURL("info", shortURL, params) | |
result = self._fetchUrl(request) | |
json = simplejson.loads(result) | |
self._CheckForError(json) | |
return json['results'][string.split(shortURL, '/')[-1]] | |
def stats(self, shortURL, params={}): | |
""" Given a bit.ly url or hash, return traffic and referrer data. """ | |
request = self._getURL("stats", shortURL, params) | |
result = self._fetchUrl(request) | |
json = simplejson.loads(result) | |
self._CheckForError(json) | |
return Stats.NewFromJsonDict(json['results']) | |
def errors(self, params={}): | |
""" Get a list of bit.ly API error codes. """ | |
request = self._getURL("errors", "", params) | |
result = self._fetchUrl(request) | |
json = simplejson.loads(result) | |
self._CheckForError(json) | |
return json['results'] | |
def setUrllib(self, urllib): | |
'''Override the default urllib implementation. | |
Args: | |
urllib: an instance that supports the same API as the urllib2 module | |
''' | |
self._urllib = urllib | |
def _getURL(self, verb, paramVal, more_params={}): | |
if not isinstance(paramVal, list): | |
paramVal = [paramVal] | |
params = { | |
'version': BITLY_API_VERSION, | |
'format': 'json', | |
'login': self.login, | |
'apiKey': self.apikey, | |
} | |
params.update(more_params) | |
params = params.items() | |
verbParam = VERBS_PARAM[verb] | |
if verbParam: | |
for val in paramVal: | |
params.append((verbParam, val)) | |
encoded_params = urllib.urlencode(params) | |
return "%s%s?%s" % (BITLY_BASE_URL, verb, encoded_params) | |
def _fetchUrl(self, url): | |
'''Fetch a URL | |
Args: | |
url: The URL to retrieve | |
Returns: | |
A string containing the body of the response. | |
''' | |
# Open and return the URL | |
url_data = self._urllib.urlopen(url).read() | |
return url_data | |
def _CheckForError(self, data): | |
"""Raises a BitlyError if bitly returns an error message. | |
Args: | |
data: A python dict created from the bitly json response | |
Raises: | |
BitlyError wrapping the bitly error message if one exists. | |
""" | |
# bitly errors are relatively unlikely, so it is faster | |
# to check first, rather than try and catch the exception | |
if 'ERROR' in data or data['statusCode'] == 'ERROR': | |
raise BitlyError(data['errorMessage']) | |
for key in data['results']: | |
if type(data['results']) is dict and \ | |
type(data['results'][key]) is dict: | |
if 'statusCode' in data['results'][key] and \ | |
data['results'][key]['statusCode'] == 'ERROR': | |
raise BitlyError(data['results'][key]['errorMessage']) | |
class Stats(object): | |
'''A class representing the Statistics returned by the bitly api. | |
The Stats structure exposes the following properties: | |
status.user_clicks # read only | |
status.clicks # read only | |
''' | |
def __init__(self, user_clicks=None, total_clicks=None): | |
self.user_clicks = user_clicks | |
self.total_clicks = total_clicks | |
@staticmethod | |
def NewFromJsonDict(data): | |
'''Create a new instance based on a JSON dict. | |
Args: | |
data: A JSON dict, as converted from the JSON in the bitly API | |
Returns: | |
A bitly.Stats instance | |
''' | |
return Stats(user_clicks=data.get('userClicks', None), | |
total_clicks=data.get('clicks', None)) | |
def main(argv): | |
if len(sys.argv) < 2: | |
print "Usage: bitly.py '[url]'" | |
return 1 | |
if not API_USERNAME or not API_KEY: | |
print 'API_USERNAME and API_KEY must be set.' | |
return 1 | |
shortUrl = Api(login=API_USERNAME, apikey=API_KEY).shorten(sys.argv[1]) | |
print shortUrl | |
return 0 | |
if __name__ == '__main__': | |
sys.exit(main(sys.argv)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment