Python で bit.ly API モジュール的なもの

python-twitter 見てたら、API のラッパモジュール作るのが楽しそうだなと思いつつ、ちょっと bit.ly の API を使いたかったので、ザックリと書きなぐってみた。


もちろん、既に多くの先達の皆さんが、もっと素敵なコードで書いてらっしゃいますが!そして当然のように参考にさせてもらってますが!書いて参考にして直して勉強、ということで、そこはあえて!!


今のとこ

  • shorten - longUrl を渡して shortUrl だけを返す
  • expand - shortUrl を渡して longUrl だけを返す(hash とか知らない)

って感じで、自分が欲しい機能だけ実装してしまい、厳密な API のラッパじゃありません。ホントすいません。


あ、あと simplejson を使用してます。


ということで晒します。


bitly.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import urllib
import urllib2
import logging
import simplejson as json

#logging.basicConfig(level=logging.DEBUG)

class BitlyError(Exception):
    """Base class for Bitly errors
    """
    pass

class BitlyRequestError(BitlyError):
    """Bitly API request error class
    """
    def __init__(self, code, msg):
        self.code = code
        self.msg = msg
    def __str__(self):
        return "Bit.ly API request error: %s (code: %s)" \
               % (self.msg, self.code)
    
class BitlyResponseError(BitlyError):
    """Bitly API response error class
    """
    def __init__(self, code, msg):
        self.code = code
        self.msg = msg
    def __str__(self):
        return "Bit.ly API response error: %s (code: %s)" \
               % (self.msg, self.code)

class Api(object):
    def __init__(self):
        self.base_url = 'http://api.bit.ly'
        self.login = None
        self.apikey = None

    @property
    def base_query_dict(self):
        return {
            'login': self.login,
            'apiKey': self.apikey,
            'version': '2.0.1',
            'format': 'json',
            'history': 1,
            }

    def mk_query(self, **kwd):
        """クエリ文字列を生成
        """
        query_dict = self.base_query_dict
        query_dict.update(kwd)
        return urllib.urlencode(query_dict)

    def set_account(self, login, apikey):
        """API 実行アカウントを設定
        """
        self.login = login
        self.apikey = apikey

    def shorten(self, longUrl):
        """Given a long url, returns a shorter one.
        """
        command_name = 'shorten'
        
        query = self.mk_query(longUrl=longUrl)
        url = "%s/%s?%s" % (self.base_url, command_name, query)
        logging.debug("[%s] url: %s", command_name, url)

        data = self._get_data(command_name, url)
        logging.debug("[%s] data: %s", command_name, data)
        
        return data['results'][longUrl]['shortUrl']

    def expand(self, shortUrl):
        """Given a bit.ly url, return long source url. 
        """
        command_name = 'expand'
        
        query = self.mk_query(shortUrl=shortUrl)
        url = "%s/%s?%s" % (self.base_url, command_name, query)
        logging.debug("[%s] url: %s", command_name, url)

        data = self._get_data(command_name, url)
        logging.debug("[%s] data: %s", command_name, data)
        
        shortened = shortUrl.split('/')[-1]
        return data['results'][shortened]['longUrl']

    def _get_data(self, command_name, url):
        """指定APIを実行して結果を返す
        """
        data = {}
        f = urllib2.urlopen(url)
        try:
            if not (200 <= f.code < 300):
                e = BitlyRequestError(f.code, f.msg)
                logging.error("[%s] BitlyRequestError: %s", command_name, e)
                raise e
            data = json.load(f)
        finally:
            f.close()
        self._check_api_success(command_name, data)
        return data

    def _check_api_success(self, command_name, data):
        """API 処理が失敗したら例外を発生させる
        """
        if data.get('statusCode', '') == 'OK':
            return
        code = data.get('errorCode', 0)
        msg = data.get('errorMessage', '')
        e = BitlyResponseError(code, msg)
        logging.error("[%s] BitlyResponseError: %s", command_name, e)
        raise e


使い方は、bit.ly にサインインすると取得できる API Key を利用して、

>>> import bitly
>>> api = bily.Api()
>>> api.set_account('bitly_login', 'bitly_apikey')
>>> api.shorten("http://google.co.jp")
'http://bit.ly/LhudI'
>>> api.expand("http://bit.ly/LhudI")
'http://google.co.jp/'

てな感じです。


http://bitbucket.org/ae35/python-bitly/overview/