PHPでAtomPub(Atom Publishing Protocol)を使って、テキストファイルから はてなブログに投稿するスクリプトを書きました。
はてなブログAtomPub - Hatena Developer Center
先に言っておきますと、画像は使わず文章のみ書ければいい、 PHPが使いたいという人だけに限定されます。とりあえずやってみました感が満載です。
2014/04/11 0:22 追記
この記事ではOpenSSLを使用しており、PHP5.5.11においても配布されているdllが脆弱性に対応していないバージョンのため、dllを最新版(1.0.1g以降)のものに差し替えるか、PHPが更新されるまでは念の為利用しないでください
Shining Light Productions - Win32 OpenSSL
追記ここまで
実行環境
- Windows 7 Professional 64bit
- PHP 5.5.4*1
投稿処理の概要
処理の大ざっぱな流れは以下の通り。
- エントリ用のXMLを作成
- WSSE認証を通してエントリをPOST
エントリ用のデータをXMLで作成
冒頭のAtomPubのページに説明がありますが、テキストファイルから 投稿用のXMLを作成するため、以下の書式にしました。
- 記事のタイトル
- 記事のカテゴリ(カンマ区切りで複数設定可)
- 空行
- 本文
ファイル名をそのままタイトルに使わなかったのは、Windowsだとファイル名の文字コードが 内部的にShift-JISで、UTF-8に変換すると文字化けするケースがあるためです。 この書式に従ってテキストファイルを読み込んで、XMLにデータを流しこんでいます。
エントリ用のXML(hatena-blog-entry.php)
<?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><?= $hatena_id ?></name></author> <content type="text/plain"> <?= $content ?> </content> <updated><?= $update ?></updated> <?php foreach($category as $val){ echo " <category term=\"$val\" />\n";} ?> <app:control> <app:draft><?= $draft ?></app:draft> </app:control> </entry>
WSSE認証を通してエントリをPOST
はてなサービスにおけるWSSE認証 - Hatena Developer Center
AtomPubを使用するためには、「OAuth認証」「WSSE認証」「Basic認証」のいずれかが必要。 WSSE認証を選択したのは、OAuth認証はアプリケーションの登録が必要なので除外、 Basic認証よりは安全そうだという理由からです。
スクリプトからのPOSTについてはPEARの「HTTP_Request2」というパッケージを使います。 HTTP_Requestをインストールしようとしたら、こちらはすでに更新が止まっているらしく、 HTTP_Request2を使うよう促されました。
準備
HTTP_Request2のインストール
PEARで以下のコマンドを実行。
pear install HTTP_Request2
Windows版PHPでのPEARの導入についてはこちらをどうぞ。
Windows版のPHPでPEARを使うためのメモ - Sprint Life
php.iniの変更
httpsで接続するためopen_sslを使用します。 php.iniの以下の行の;を削除。
変更前 ;extension=php_openssl.dll 変更後 extension=php_openssl.dll
投稿処理のスクリプトの変更
コメントにも書いてありますが、以下の変数を自分のものに置き換えてください。 管理画面の「詳細設定」→「AtomPub」に記載されています。
$hatena_id = ""; // はてなID $blog_id = ""; // ブログID $api_key = ""; // APIキー
また、投稿か下書きかについては$draft(yes: 下書き no: 投稿)で制御しています。
使い方
- 投稿処理のスクリプトとXML、テキスト(entry.txt)を同じディレクトリに置く
- コマンドプロンプトからスクリプトを実行する
実行例
php hatena-atom-post.php > result.txt
処理が正常終了すると、投稿または下書きされたエントリがレスポンスとして返ってくるので、 実行結果を別のファイルに出力するようにしています。
注意事項
ローカルPCで自分だけが使うことを想定していますので、入力値のチェックなどは行なっていませんが、 試しにこの記事を投稿してみたところ、以下の状況を確認しました。投稿の内容によっては、 意図した結果にならない可能性があります。
- 本文中にHTMLが含まれている場合は、あらかじめ特殊文字の変換をしないとエラーになる
- 本文中のPHPのソースが表示されない
残念ながらこの記事自体は管理画面からの投稿です。
また、WSSE認証にはパスワードではなくAPIキーを使用しているので、 万が一APIキーがバレてもアカウントがのっとられることはないと思いますが、 APIキーを変更するまで、第三者がブログに投稿し放題の状態になってしまいます。
はてな各種APIでのパスワードによるWSSE認証を2014年3月5日に終了します(開発者向け) - Hatena Developer Blog
ソース
投稿処理のスクリプト(ファイル名は任意で拡張子はPHP。文字コードはUTF-8)
<?php mb_internal_encoding("UTF-8"); mb_language("Japanese"); date_default_timezone_set("Asia/Tokyo"); require_once("HTTP/Request2.php"); // https://blog.hatena.ne.jp/{はてなID}/{ブログID}/atom/entry // 管理画面の「詳細設定」→「AtomPub」を参照 $hatena_id = ""; // はてなID $blog_id = ""; // ブログID $api_key = ""; // APIキー // テキストファイルからエントリを作成 $fp = fopen('./entry.txt', 'r'); $title = trim(fgets($fp, 4096)); $category = fgetcsv($fp, 4096,','); $blank = fgets($fp, 4096); $content = ""; while (($buffer = fgets($fp, 4096)) !== false) { $content = $content . $buffer; } fclose($fp); $update = date('c'); $draft = "yes"; // yes: 下書き no: 投稿 ob_start(); include('./hatena-blog-entry.php'); $entry = ob_get_contents(); ob_end_clean(); //WSSEヘッダをつけてPOST $nonce = sha1(md5(time()),true); $nonce_base64 = base64_encode($nonce); $created = date('c'); $pass_digest = base64_encode(sha1($nonce . $created . $api_key,true)); $wsse_header = "UsernameToken Username=\"$hatena_id\", PasswordDigest=\"$pass_digest\", Nonce=\"$nonce_base64\", Created=\"$created\" "; $req = new HTTP_Request2(); $req->setConfig('ssl_verify_peer', false); $req->setURL("https://blog.hatena.ne.jp/$hatena_id/$blog_id/atom/entry"); $req->setMethod(HTTP_Request2::METHOD_POST); $req->setHeader('Accept','application/x.atom+xml, application/xml, text/xml, */*'); $req->setHeader('Content-Type', 'application/atom+xml'); $req->setHeader('Authorization', 'WSSE profile="UsernameToken"'); $req->setHeader('X-WSSE',$wsse_header); $req->setBody($entry); $response = $req->send(); echo $response->getBody(); ?>
テキストファイルについては固定にしていますが、スクリプト実行時に引数として ファイルのパスを渡すことで、任意のファイルを使用できます。
変更前: $fp = fopen('./entry.txt', 'r'); 変更後: $fp = fopen($argv[1], 'r');
おわりに
本当はエディタから直接投稿するところまでやるつもりでいたんですが、 スクリプトそのものの使いどころが、文章のみを投稿する場合に限定されそうなのでやめました。
PHPでAtomPubを使うサンプルとしてどこか役に立つところがあれば幸いです。
参考
- 第3回 RESTなWebサービスをPHPで操作する | Think IT
- PHPではてなダイアリーAtomPubを使って日記を投稿するサンプル - phaの日記
- Manual :: HTTP_Request2
- HTTP_Request2 でのSSL通信でのPOST送信 - クマーのえんじにありんぐ
*1:最低でも5.0以上。4系だと動きません