Python ではてなダイアリーに投稿

Python の atompub で使いやすいライブラリがみつからなかったので
自前でゴリゴリ

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# http://d.hatena.ne.jp/keyword/%a4%cf%a4%c6%a4%ca%a5%c0%a5%a4%a5%a2%a5%ea%a1%bcAtomPub

import base64
import random
import sha
import urllib2
import urllib
from datetime import date,datetime

import sys
# 2.5 までは 201 がエラー扱いになっちゃうので
if sys.version_info[0] < 3 and sys.version_info[1] < 6:
    def __http_response(self, request, response):
        code, msg, hdrs = response.code, response.msg, response.info()
        if not (200 <= code < 300):
            response = self.parent.error(
                'http', request, response, code, msg, hdrs)
            
            return response

    urllib2.HTTPErrorProcessor.http_response = __http_response

class WSSEHeader(urllib2.BaseHandler) :
    
    def __init__(self, userid, passwd):
        self.userid = userid
        self.passwd = passwd

    def get_nonce(self):
        private = str(random.random())
        now = datetime.now()
        timestamp = now.strftime('%Y-%m-%dT%H:%M:%SZ')
        return '%s %s' % (timestamp, sha.new('%s:%s' % (timestamp, private)).hexdigest())

    def get_wsse(self):
        nonce = self.get_nonce()
        base64_encoded_nonce = base64.encodestring(nonce).replace('\n', '')
        now = datetime.now()
        post_creation_time = now.strftime('%Y-%m-%dT%H:%M:%SZ')
        password_digest = base64.encodestring(sha.new(nonce + post_creation_time + self.passwd).digest()).replace('\n', '')
        return 'UsernameToken Username="%s", PasswordDigest="%s", Created="%s", Nonce="%s"' \
            % (self.userid, password_digest, post_creation_time, base64_encoded_nonce)

    def http_request(self,req):
        req.add_header('X-WSSE', self.get_wsse())
        req.add_header('User-agent', 'yoshiori\'s test(yoshiori@gmail.com)')
        return req

class HatenaDiary:
    
    def __init__(self, userid, passwd):
        self.userid = userid
        self.passwd = passwd
        self.url = 'http://d.hatena.ne.jp/%s/atom/' % userid
        
    def postEntry(self, title, text, isDraft=False):
        entry = '''<?xml version="1.0" encoding="utf-8"?>
        <entry xmlns="http://purl.org/atom/ns#">
        <title><![CDATA[%s]]></title>
        <content type="text/plain"><![CDATA[%s]]></content>
        </entry>
        ''' % (title,text)
        print entry
        url = self.url + 'draft' if isDraft else self.url + 'blog'
        return self._open(url,entry)

    def getEntrys(self):
        return self._open(self.url + 'blog')

    def get(self):
        return self._open(self.url)
    
    def _open(self,url,param=None):
        opener = urllib2.build_opener( WSSEHeader(self.userid, self.passwd))
        return opener.open(url,param) if param else opener.open(url)
        
if __name__ == '__main__':
    from pit import Pit
    hatena_conf = Pit.get('hatena.ne.jp',{'require' : {'userid':'Your hatena.ne.jp userid','password':'Your hatena.ne.jp password'}})
    diary = HatenaDiary(hatena_conf['userid'],hatena_conf['password'])
    print diary.postEntry('日本語','**日本語\n-ほげ\n-ふぉお\n>|python|\nimport sys\n||<',True)

とりあえず初 Mercurial
http://bitbucket.org/yoshiori/hatenad/