はてな記事の編集/投稿 with vim&python

仕事中に社外秘でない技術メモを取りたいと思ったとき、はてなにすぐに投稿する環境があれば便利じゃね?と思った。エディタはvimvimならわざわざ自作せんでもプラグインあるだろー、と探したらhatena.vimなるものがあった。


https://github.com/motemen/hatena-vim


インストールも簡単だしいいじゃん、と思いきや、:HatenaEditをいざ実行するとエラー。プラグインの中を覗いたらcurl + openssl で通信してた。opensslをローカルにインストールだと?だるい。ならいっそpythonで書いてしまえ。


というわけで、以下、20分で作ったpython版myhatena.vim。


まずはpythonモジュール
最低限のテストはしたよw

#encoding=utf-8

import urllib2
import mechanize            # easy_install mechanize
from common.mys import mylogger, S, U    # ↑で紹介したロガーと文字列変換ね
from datetime import datetime


url = 'https://www.hatena.ne.jp/login'
durl = 'http://d.hatena.ne.jp/'

class HatenaCrawler:
    def __init__(self, loginname, password):
        self.loginname = loginname
        self.password = password
        self.br = self._connect()
        self.enc = 'euc-jp'
        self.content = {}

    # ログイン
    def login(self):
        self.br.open(url)
        self.br.select_form(nr=0)
        self.br['name'] = self.loginname
        self.br['password'] = self.password
        response = self.br.submit()

    # 記事取得
    def loadContent(self, date_obj):
        yyyyMMdd = date_obj.strftime('%Y%m%d')
        self.content = {}
        try:
            response = self.br.open(durl + self.loginname + '/edit?date=' + yyyyMMdd)
            self.br.select_form(name='edit')
            try:
                self.content['timestamp'] = self.br.form['timestamp']
                self.content['date'] = datetime.strptime(self.br.form['date'], '%Y%m%d')
                self.content['rkm'] = self.br.form['rkm']
                self.content['day_title'] = self.br.form['title']

                body = self.br.form['body']
				self.content['body'] = U(body.replace('&gt;', '>').replace('&lt;', '<').replace('&quot;', '"').replace('&amp;', '\&'))
            except Exception, e:
                mylogger.info(e)
                self.content = {}
                self.content['rkm'] = self.br.form['rkm']
                self.content['day_title'] = self.br.form['title']
                self.content['date'] = date_obj

            mylogger.info(self.content)

        except mechanize._form.ControlNotFoundError, e:
            mylogger.error(e)

        return self.content

    # 投稿
    def updateContent(self, date_obj):
        yyyyMMdd = date_obj.strftime('%Y%m%d')
        self.br.open(durl + self.loginname + '/edit?date=' + yyyyMMdd)
        self.br.select_form(name='edit')
        self.br.form['title'] = self.content['day_title']
        self.br.form['body'] = self.content['body']
        self.br.submit()

    def _connect(self):
        br = mechanize.Browser()
        #br.set_proxies({'http': 'hogehoge:80', 'https': 'hogehoge:80'})  # proxy経由なら
        br.set_handle_robots(False)
        return br

if __name__ == '__main__':
    import unittest

    class Tests(unittest.TestCase):
        def setup(self):
            pass

        @unittest.skip('')
        def testInstantiate(self):
            crawler = HatenaCrawler(ログイン名, パスワード)
            crawler.login()

        @unittest.skip('')
        def testLoadContent(self):
            crawler = HatenaCrawler(ログイン名, パスワード)
            crawler.login()
            print crawler.loadContent(datetime(2011, 9, 8))

    unittest.main(verbosity=2)


作りとしては仮想ブラウザでログインして編集画面の情報取得してます。mechanizeモジュール必須ですね。

既知のバグ (直す予定なし)
    ・loadContent() - preタグの中の「'>'」や「'<'」が「>」、「<」に変換されてしまう。

これ使って、vimscript書いてみました。

if !has('python')
    " なければスクリプトロードを中断したい
endif

command! -nargs=* MHatenaLogin :call s:LoginHatena()
command! -nargs=* MHatenaLoad :call s:LoadContent()
command! -nargs=* MHatenaUpdate :call s:PostContent()

let s:buffer_name = 'hatena_local'

python << EOF
import vim
from datetime import datetime
from hatena_vim.hatena import HatenaCrawler
crawler = HatenaCrawler(ログイン名, パスワード)
EOF

" TODO
" ログインはapi呼び出し毎にしないとかなぁ


" ログイン
function! s:LoginHatena()
    python crawler.login()
endfunction

" 取得
function! s:LoadContent()
    let date = input('日付(yyyyMMdd) > ')
    silent execute 'e ' . s:buffer_name . '_' . date
    set filetype=hatena
    silent execute 'set buftype=nofile'
python << EOF
#encoding=cp932
del vim.current.buffer[0:]
s = vim.eval('date')
date_obj = datetime.strptime(s, '%Y%m%d')
crawler.loadContent(date_obj)
if 'body' in crawler.content:
    for s in crawler.content['body'].split('\n'):
        vim.current.buffer.append(s.rstrip().encode('cp932'))
EOF
endfunction

" 投稿
function! s:PostContent()
python << EOF
s = ''
for line in vim.current.buffer:
    s += line + '\n'
crawler.content['body'] = s
crawler.updateContent(crawler.content['date'])
EOF
endfunction


終了。vimscript開いて :so % したら3つのコマンド(MHatenaLogin, MHatenaLoad, MHatenaUpdate) 使える。やっつけだが個人ユースならいけるw
windows上で動かしてるんで、vim側で文字列に落とすときはcp932。

ちなみに、vimシンタックスハイライトは hatena.vim に同梱のものを使わせてもらってます。