ブログメディアにおけるdata apiの活用について
TRANSCRIPT
1
S
wonderdriving.comブログメディアにおけるData API の活用について
ワンダーツー株式会社上野初仁
2
本日の内容
自己紹介(個人、会社)
wonderdriving.com とは
Data API と wonderdriving.com
人気記事ランキングスクリプト
アセット登録スクリプト
Data API 2.0 対応
今後
3
S
発表者 自己紹介
4
上野初仁 a.k.a. @uehatsu
twitter : @uehatsu
現 Wonder2 Inc. エンジニア UNIX, Apache, MySQL, Perl, PHP, JavaScript, Rails,
Objective-C, Swift, etc. フルスタックエンジニアという名の何でも屋
(自分で言うなと、、、)
元 Six Apart エンジニア 2003 - 2011 TypePad (現 lekumo )チーム Movable Type チーム
5
趣味
コンピュータ全般
DTM TM Network カバー
OED C4U (Crazy for you)http://youtu.be/og0W4Mp3QC0
Perfume カバーチョコレイト・ディスコhttp://youtu.be/393kJwne88E
soundcloudhttps://soundcloud.com/uehatsu
6
SA 時代に自分の残した物
Movable Type 開発者向けガイドhttps://github.com/movabletype/Documentation/wiki プラグイン開発ガイド MTML ガイド
今でも自分で良く使います プラグイン開発ガイドは、関根元和さん( CHEEBOW さ
ん)の「 Movable Type プラグイン開発入門」を MT5 の流儀( config.yaml など)に対応、拡張させたドキュメント
MT6 でも使えます、 PSGI 周りとか書き足したいなぁ、、、
7
S
Wonder2 Inc. 紹介
8
ワンダーツー株式会社 会社概要
代表者 代表取締役 野間恒毅
設立 2011/04/25
資本金 300 万円
主な事業内容 システム開発、 Movable Type 開発・カスタマイズ、スマートフォ
ンアプリ開発、メディア事業、コンサルティング
加盟団体 ProNet Japan (Six Apart Co. Ltd.)
9
ワンダードライビング(運営:ワンダーツー)
ブログメディア(車、ホビー、 etc. )
Movable Type 6
http://wonderdriving.com/
10
株式会社アドヴァン
EC サイト
Movable Type 5 +MT コマース
http://www.advan.co.jp/
11
ハジー物産株式会社
商品紹介サイト
Movable Type 5
http://hagypack.com/
12
株式会社シーフォ
会社紹介、メディア
Movable Type 5 +ソーシャルメディア運用
http://www.sifo.jp/
13
iPhone アプリ レーダーマップ
位置がわかるだけのシンプルな iPhone アプリ
ID 不要、登録不要
匿名利用
その他にも iPhone アプリ受託開発も行っています
14
S
wonderdriving.comとは
15
ワンダー・ドライビングとは
スーパーカーからラジコン、ミニ四駆、美女まで男の子の夢もりだくさん「ワンドラ」 車、バイク、ラジコン、ミニ四駆、ボート、ガジェット、
美女、などホビー全般についてのオウンドメディア 弊社野間やゲストブロガーの方々による更新 特にミニ四駆、ラジコン、車のショーレポートなどのコン
テンツが人気
16
ワンドラが SA 導入事例に紹介されました
Six Apart 社の導入事例にワンドラが紹介されました。
http://www.sixapart.jp/business/wonder2.html
17
S
Data API とワンドラ
18
Data API を積極導入する事になったきっかけ
ワンドラ リニューアルのタイミングが MT6 発表とほぼ同タイミングだったこと
リニューアル時に PV ランキングのウィジェットデザインがデザイン会社からアップされたが、この時点で未実装だった
19
旧 PV ランキングページ
デザインリニューアル前の PV ランキングページ Apache 生ログを集計するスクリプトを日に1回程度実行
し、インクルードする HTML を生成していた 処理時間が長時間かかり、実行間隔が長いためデータの即
時性がなかった 別途 Google Analytics でアクセス解析をしていたが誤差
が大きかった
→ リニューアル後は別の方法で作成することに
20
新 PV ランキングページ
新デザインになってからしばらくは PV ランキングページはありませんでした 新デザイン移行時に MT6 にアップグレード 弊社野間から「上野さん PV ランキングページ作るスクリプ
ト作って」と話があり、半分冗談で「じゃMT6 の Data APIで作りましょうか、 GA の集計機能あるし」と言ったら、そのまま企画が通る
さくっとツール作成 紆余曲折はありましたが、技術的な話は後ほど、、、
21
人気記事ランキングサイドバー
・ 1 位〜 5 位 ・順位 ・カテゴリー ・投稿日 ・タイトル
22
人気記事ランキング 一覧
1 位〜 30 位 サムネール 投稿日 順位 タイトル
23
S
人気記事ランキングスクリプト
24
2つのスクリプト
サイドバーと一覧を作成しているスクリプトは別 作成時間を優先させたため共通化せず
実行間隔 2 つのスクリプトとも 5 分間隔
それぞれインクルードファイルを出力し保存 インクルードファイルを SSI を使って各ページに挿入
25
Data API にアクセスするための共通関数
_request() 関数を作成 $content = _request( $method, $api_url, $params,
$token ); $method : ‘POST’, ‘GET’ $api_url : ‘http://foo.com/mt/mt-data-api.cgi/v1/... $params : 例) { username => $user, password => $pass,
clientID => ‘aaaa’ }
$token : accessToken $content : Data API からのレスポンスを
MT::Util::from_json() した戻り値 my $token = $content->{accessToken};
26
_request() ver.1 の中身
sub _request { my ( $method, $url, $params, $accessToken ) = @_; if ( $method eq 'GET' ) { $url .= '?' . join( '&', map{ $_ . '=' . $params->{$_}} keys %$params ); } my $request = HTTP::Request->new($method, $url); if ( $accessToken ) { $request->header('X-MT-Authorization' => "MTAuth accessToken=$accessToken"); } if ( $method eq 'POST' ) { $request->content( join( '&', map{ $_ . '=' . $params->{$_}} keys %$params ) ); } my $ua = MT->new_ua; my $res = $ua->request( $request ); unless ( $res->is_success ) { if ( $res->code == 500 ) { my $content = MT::Util::from_json( $res->content ); my $message = $content->{error}->{message}; $message =~ s/\\x{([0-9a-z]+)}/chr(hex($1))/ge; die $message; } else { die $res->as_string; } } return MT::Util::from_json( $res->content );}
27
スクリプトの動作
authentication
カテゴリー一覧の取得
PV一覧の取得
エントリーの取得
サムネール URL の取得(サムネールの作成)
HTML の生成
28
PV一覧の取得
api_url : http://foo.com/mt/mt-data-api/v1/${blog_id}/stats/path/pageviews startDate, endDate : YYYY-MM-DD, 昨日〜今日に設定 limit : 今回は 200
レスポンスの中に {item} として結果が格納されている
$item->{archiveType} で結果の内容が分かる 今回は個別アーカイブ’ Individual’ のみを対象とした 個別アーカイブのエントリー id は $item->{entry}-
>{id}
29
サムネール URL の取得
サムネール URL を取得する API は存在しないため自作 DataAPIAssetInfo プラグイン
エンドポイント (/sites/:site_id/assets/:asset_id/info ) を追加
thumb_width, thumb_height, thumb_square, thumb_scale
アクセスがあると指定のあった Asset のサムネールを作成 実際には MT テンプレートのビルドが走る <mt:Asset id="$asset_id"><mt:AssetThumbnailURL
width="$width" height="$height" square="$square" /></mt:Asset>
30
アセットが登録されていないエントリーがたくさん
エントリーアセットが未登録の物はデフォルト画像(ワンドラロゴ)が表示される
リニューアル前はアセットに画像を登録していなかった flickr に画像をアップし、そこでタグを生成して貼り付けて
いたため、アセットに登録していなかった ランキングは古いエントリーが出る事が多かったためロゴ画像がずらっと並ぶ残念なことに
「過去画像アセットに登録できたらいいですねー」「じゃ作って」「(えっ?!)」で作成する事に
31
S
アセット登録スクリプト
32
アセット登録スクリプト
アセット登録をするだけなら Data API を利用する必要は無いけど、、、 外部ファイル( flickr画像)を MT にアップロードしてア
セットとして登録 そのアセットをエントリーアセットとして登録 デザインの関係でカスタムフィールド画像も同時に登録し
たい →トータルで考えて Data API で作るのが早そう
33
スクリプトの動作
authentication
エントリーの取得(複数エントリーを一括取得)
本文、追記を HTML::TagParser で解析し img タグを抽出
img タグの src が flickr の物だったらダウンロード
Data API を使って画像をアセットとしてアップロード
エントリーアセットとして登録
カスタムフィールド画像として登録
34
ファイルアップロード用に_request() 関数を変更
request作成に HTTP::Request::Common の GET と POSTを利用するように変更
$params = { path => $upload_path, file => [ $save_path ], autoRenameIfExists => 1 };
path : ${WEB_ROOT} からのアップロード先のパス file : ダウンロード済みの flickr画像の保存先パス、 [ ] で囲む autoRenameIfExists => 1 : 既に存在していた場合自動でリネームした上でアップロード
$request = POST( $url, Content_Type => 'form-data', Content => $params );
35
エントリーアセットとして登録
アセットをエントリーに紐付ける機能がないため自作 DataAPIAssetInfo プラグインを拡張 /sites/:site_id/assets/:asset_id/set_entry my $params = { entry_id => $entry_id }; これを MT::ObjectAsset->get_by_key() で
object_asset を取得し save
36
アセットを登録するコード
本来なら entry_id の存在チェックなどする必要がありますが、ある物としてコードを書いています。
my $object_asset = MT::ObjectAsset->get_by_key({ asset_id => $asset->id, blog_id => $blog->id, object_ds => 'entry', object_id => $app->param('entry_id'), }); $object_asset->save();
37
カスタムフィールド画像の設定コード
my $field = "<form mt:asset-id=\"$asset_id\" class=\"mt-enclosure mt-enclosure-image\" style=\"display: inline;\"><a href=\"$asset->{url}\">$asset->{filename}</a></form>";
my $params = { __method => 'PUT', entry => MT::Util::to_json( { customFields => [ { basename => 'cfmainimage', value => $field, } ], }, ), };
my $content = _request( 'POST', $api_url, $params, $token );
38
S
Data API 拡張プラグイン作成
39
config.yaml 抜粋
applications: data_api: endpoints: - id: get_asset_information route: /sites/:site_id/assets/:asset_id/info verb: GET version: 1 handler: $DataAPIAssetInfo::DataAPIAssetInfo::EndPoint::Asset::get_asset_info requires_login: 1 - id: set_entry_asset route: /sites/:site_id/assets/:asset_id/set_entry verb: GET version: 1 handler: $DataAPIAssetInfo::DataAPIAssetInfo::EndPoint::Asset::set_entry_asset requires_login: 1
40
DataAPIAssetInfo::EndPoint::Asset
sub set_entry_asset { my ($app, $endpoint) = @_;
return $app->error(403) unless $app->can_do('upload'); return $app->error(403) unless $app->param('entry_id');
my ( $blog, $asset ) = context_objects(@_) or return;
run_permission_filter( $app, 'data_api_view_permission_filter', 'asset', $asset->id, obj_promise($blog)) or return;
my $object_asset = MT::ObjectAsset->get_by_key({ asset_id => $asset->id, blog_id => $blog->id, object_ds => 'entry', object_id => $app->param('entry_id'), }); $object_asset->save();
return $asset;}
41
S
MT6.1Data API 2.0 対応
42
大幅なリファクタリング
共通化できるコードをプラグイン化
2つに分かれていたランキング作成スクリプトを1つに
accessToken が必要なエンドポイントの場合は自動的に token を取得するよう実装
blog_id を複数渡せるように変更 @blog_ids = [ 3, 4 ] : で複数回 Data API にアクセスし、ブログにまたがって結果を
集計できるように拡張
カテゴリーアーカイブのパスを getCategory の archiveLink から取得するように変更
サムネールの作成、エントリーアセットの登録を v2 で拡張された getThumbnail, updateEntry の機能を使うように変更
43
複数ブログ対応
ひとまず getBlog と PageviewsForPath のみ対応
blog_id が配列で複数指定された場合 内部オブジェクト W2DataAPI::Blogs,
W2DataAPI::Pageviews に結果を格納し、まとめて返す Blogs の結果は getBlog を配列にしたもの Pageviews の結果は blog_idごとにマージし {items} を
{pageviews} でソートしたもの(ブログを横断してランキングを取得)
44
カテゴリーアーカイブパス
v1 ではカテゴリーの情報にパスが含まれていなかったため、 basename からパスを生成する必要があった カテゴリー basename の生成方法によって所作が変わるた
め、コードの一般化が難しい
v2 ではカテゴリーに {archiveLink} が追加された 1つのカテゴリーアーカイブに対して一意の URL が取得で
きるようになった
45
サムネールの作成
エンドポイント getThumbnail が追加された(あれ、 v2 のドキュメント getThubmnail になってる?) /v2/sites/:site_id/assets/:asset_id/thumbnail params => width, height, scale, square
独自拡張のエンドポイントをやめ、こちらに書き換え
46
エントリーアセットの登録
updateEntry でアセットの紐付けができるようになった
こちらも独自拡張をやめた( APIへのアクセス回数も減った)
my $entry_params = { entry => MT::Util::to_json( { customFields => [ { basename => 'cfmainimage', value => $field, } ], assets => [ { id => $asset_id } ], } ),};
47
S
今後
48
今後の目標 完全プラグイン化
現在、スクリプトを cron でまわしている スクリプトの配置がハードルが高い MT クラウドでは利用出来ない
完全プラグイン化 print() で HTML タグを出力している部分を MT テンプレー
ト化 cron スクリプトを廃止しタスク化 諸々の設定を管理画面のプラグイン設定から変更可能に