読者です 読者をやめる 読者になる 読者になる

ottatiのブログ

無職学生がネットにクソアプリをまき散らしていく様子

Pythonで作る、はてなブログAtomPub APIを使った簡易投稿クライアントアプリ

やり方 コードレシピ python hatena api wsse

f:id:ottati:20150103125718g:plain     🍣

はじめに

f:id:ottati:20130905230321p:plain

9月4日、はてなブログ開発ブログではてなブログAtomPub APIが公開されたことが発表されました。

はてなブログAtomPub APIを公開しました。サードパーティのブログ投稿ツールを利用・作成できます - はてなブログ開発ブログ

本日、はてなブログの記事を投稿・編集等できるAPIはてなブログAtomPub」を公開しました。はてなブログAtomPubは、Atom Publishing P...

ということで今回は、このAPIを使ったはてなブログの簡易クライアントアプリを作る方法を説明していきます。

今回作るクライアントアプリってどんなの?

まず始めに今回作るはてなブログクライアントアプリについて説明します。

今回の作るアプリの名前は「はてぽす!」です。

投稿専用の簡易クライアントアプリで、一行で書いたエントリを投稿することができます。もちろんタイトル付きです。

実際の投稿画面はこんな感じです。

f:id:ottati:20130905230321p:plain

今回作るアプリの処理の流れ

作る前にどのような動作で投稿するか、整理しておきましょう。

  1. タイトル、本文の入力から記事データを作成
  2. WSSE認証用コードを生成
  3. http://blog.hatena.ne.jp/{はてなID}/{ブログID}/atom/entryへPOST

プログラミングの下準備

今回はPython 2.7.5 を使っていきます。Download Python | Python.org

更に今回はrequestsというHTTPライブラリを使っていきます。なぜなら標準HTTPライブラリは人間に向けて作られたものではないからです。Requests: HTTP for Humans — Requests 2.7.0 documentation

なのでこれに従ってrequestsをインストールしてくださいInstallation — Requests 2.7.0 documentation

これにて下準備は完了です。

では早速作っていきましょう。コードはとても短いため、すぐに終わるでしょう。

今回はhatepos.pyというファイルに記述していきます。はてぽす!の実行は

python hatepos.py

で行います。

hatepos.pyを書いていく

完成したコードをGistにアップしました。

基本的には上のソースコードと照らしあわせてこの記事を見てください。

エンコーディング

(Emacsと)PythonにこのファイルはUTF-8であることを教えてあげましょう。

# -*- coding: utf-8 -*-

必要なモジュールをimportする

必要なモジュールを最初にimportしましょう。

import base64
import datetime
import hashlib
import random
import requests
import time

WSSE認証の準備をしよう

WSSE認証のために必要な情報を設定していきます。

API_KEYはブログ管理画面の詳細設定に記述されてあるので、コピペしましょう。

USER_NAME = "******"              # はてなID (ex: "ottati")
BLOG_ID = "******.hatenablog.com" # ブログID (ex: "ottati.hatenablog.com")
API_KEY = "**********"            # APIキー ブログ管理画面 => 詳細設定より取得

投稿用テンプレートを書こう

次にテンプレートを書きます。テンプレートと言ってもあとで.format()するだけですが。

投稿用テンプレートの書式ははてなブログAtomPub - Hatena Developer Centerの「ブログエントリの投稿」に記載されています。

こちら

POST http://blog.hatena.ne.jp/{はてなID}/{ブログID}/atom/entry

<?xml version="1.0" encoding="utf-8"?>
<entry xmlns="http://www.w3.org/2005/Atom"
       xmlns:app="http://www.w3.org/2007/app">
  <title>エントリタイトル</title>
  <author><name>name</name></author>
  <content type="text/plain">
    ** エントリ本文
  </content>
  <updated>2008-01-01T00:00:00</updated>
  <category term="Scala" />
  <app:control>
    <app:draft>{yes | no}</app:draft>
  </app:control>
</entry>

これをコピーして、あとで.format()でタイトル、著者名、本文、下書きかどうかを整形できるようにしました。

TEMPLATE = """<?xml version="1.0" encoding="utf-8"?>
<entry xmlns="http://www.w3.org/2005/Atom"
       xmlns:app="http://www.w3.org/2007/app">
  <title>{title}</title>
  <author><name>{name}</name></author>
  <content type="text/plain">{content}</content>
  <updated></updated>
  <app:control>
    <app:draft>{draft}</app:draft>
  </app:control>
</entry>
"""

例えば

entry = TEMPLATE.format(title="test", name="ottati", content="Hello World", draft="yes")

のようにして整形することができます。これはタイトルをtitle, 著者名をottati、本文をHello World, 下書きとして投稿をyesとして投稿することを表しています。

X-WSSEヘッダの内容を作成する関数を作ろう

WSSE認証って?

WSSE認証はHTTPのX-WSSEヘッダを用いて認証用文字列を送信する認証手段です。WSSE認証用文字列にはユーザー名とパスワードが含まれます。このとき、パスワードはSHA1アルゴリズムによって暗号化されたダイジェストとして送信されるため、HTTP基本認証などに比べてセキュアな認証が可能です。

X-WSSEヘッダのサンプル

X-WSSE: UsernameToken Username="hatena", PasswordDigest="ZCNaK2jrXr4+zsCaYK/YLUxImZU=", Nonce="Uh95NQlviNpJQR1MmML+zq6pFxE=", Created="2005-01-18T03:20:15Z"

参考: はてなサービスにおけるWSSE認証 - Hatena Developer Center

WSSEヘッダ認証用の関数を作る

では作っていきましょう。基本的にはてなサービスにおけるWSSE認証 - Hatena Developer Centerに沿って作っていきます。しかし参照先はPerlのコードしかないので、それをPythonに書き換えたものをここに示します。

nonceはリクエスト毎に発行するランダムな文字列です。これはセキュリティトークンとして使います。time.time()は現在時刻を浮動小数で表したもの、random.random()は乱数でそれぞれを文字列にし、結合。それをsha1アルゴリズムでダイジェスト化します。

createdはNonceが作成された時刻を表す文字列です。ISO-8601表記で示されます。

2005-01-18T03:20:15Z

このような形となります。

digestはnonceとcreatedとパスワード(今回はAPIキー)を結合し、sha1アルゴリズムでダイジェスト化後、base64エンコードして生成された文字列です。

digest作成時にはbase64エンコードしていないnonceを、WSSE認証用文字列作成時にbase64エンコードすることがポイントです。

def create_wsse(username, password):
    """X-WSSEヘッダの内容を作成
 
    ユーザネームとAPIキーからWSSE認証用文字列を作成し、返します。
    
    Args:
        @username: はてなID
        @password: はてなブログで配布されるAPIキー
    Returns:
        WSSE認証用文字列
    """
    
    nonce = hashlib.sha1(str(time.time()) + str(random.random())).digest() # セキュリティトークン
    created = datetime.datetime.now().isoformat() + "Z" # Nonceの作成時刻
    digest = base64.b64encode(hashlib.sha1(nonce+created+password).digest()) # PasswordDigest
    # WSSE認証用文字列として整形して返す
    return 'UsernameToken Username="{0}", PasswordDigest="{1}", Nonce="{2}", Created="{3}"'.format(username, digest, base64.b64encode(nonce), created)
 

これにてWSSE認証用文字列を返す関数が完成しました。あとはこれの関数を使ってPOSTするだけです。

文字コード修正用関数を作ろう

メイン処理を記述する前にもう一つ関数をつくります。普通はこの関数は必要ないのですが、日本語Windowsのコマンドプロントを使ってアプリを実行した場合、ユーザの入力がShift-JISになってしまう場合があるので、それをUTF-8に変換する関数です。

f:id:ottati:20130905230350p:plain

この関数は文字列がUTF-8だったら何もせずに返し、Shift-JISであったら一度デコードし、UTF-8エンコードして返します。

def fix_encoding(string):
    """Shift_JISであればUTF-8に修正する関数"""
    try:
        return string.decode('shift-jis').encode('utf-8')
    except:
        return string

※ ちなみに今回作るアプリは入力がUTF-8、Shift-JISの2つ以外であった場合はうまく投稿できないでしょう。なので環境に合わせてfix_encoding関数を修正してみてください。ここなどを参考にするといいかもしれません→技術志向 |Python 文字コードの判定と変換

メイン処理を書いて完成

最後にメイン処理を書きましょう。

fix_encoding(raw_input())で入力を(必要であればShift-JISからUTF-8に変換して)受け取ります。

投稿用テンプレートを整形します。draftは"no"か"yes"が指定でき、yesにすると下書きとして投稿できます。noにすると下書きではなく直接投稿されるので注意してください。

urlは

http://blog.hatena.ne.jp/{はてなID}/{ブログID}/atom/entry

です。これにPOSTすることで記事を投稿できます。

dataにentry(整形された投稿用テンプレート)、headersにX-WSSEを追加してPOSTです。

if __name__ == "__main__":
    """メイン処理"""
    print u"\nはてぽす! - はてなブログAtomPubを使った簡易投稿アプリ\n"
    title = fix_encoding(raw_input("Title: "))     # タイトルの入力
    content = fix_encoding(raw_input("Content: ")) # 本文の入力
    entry = TEMPLATE.format(
        title = title,          # 記事タイトル
        name = USER_NAME,       # 著者名
        content = content,      # 記事本文
        draft = "yes"           # 下書きとして投稿 (yes / no)
        )
    url = "http://blog.hatena.ne.jp/{0}/{1}/atom/entry".format(USER_NAME, BLOG_ID) # リクエストURL
    wsse =  create_wsse(USER_NAME, API_KEY) # WSSE認証用文字列
    r = requests.post(url, data=entry, headers={'X-WSSE': wsse}) # POST
    if r.status_code == 201:                                     # 投稿成功判定
        print u"\n投稿しました。" # 成功メッセージ
    else:
        print u"\n投稿に失敗しました。" # 失敗メッセージ

rはレスポンスです。内容が気になる場合はprint r.textで全文を見ることができます。

投稿に成功した場合はr.status_codeが201になります。HTTPステータスコード、201 Createdは作成の意味です。リクエストが完了したことを意味します。つまり投稿が成功したということです。

はてぽす!の完成!

いかがでしたか?

これではてなブログ簡易投稿アプリが完成しました。

python hatepos.py

で実行して遊んでみて下さい。

このように「はてなブログAtomPub API」が公開されたことによって簡単にはてなブログに投稿や、記事やカテゴリの取得などをできるようになりました。

何か他の要素と連携して面白いアプリを作ってみてはいかがでしょうか。

はてなブロガーのためのツール作りました

クリック率の高い「他の記事」へのリンクを生成する『はてなブログ記事紹介ジェネレータ』をリリースしました! - あのねノート。

つくってみた記事を書いている時、前に書いた記事へのリンクを貼りたくなりました。他の記事へのリンクを貼ると直帰率は下がり、更にリピータも増えるのはもうブロガー界で...

ブログの反響を一発で確認するウェブアプリ「BuzzWatcher」をリリースしました。 - あのねノート。

ブログへの反応を一発で見れるウェブアプリ、BuzzWatcherを公開しましたリリースっていうと格好いい!///さて、ブロガーなら誰しもが自分の書いた記事へのブ...