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

amakankanが復活した

二週間ほど前に Chrome ウェブストアに再申請したブラウザ拡張 amakankan だが、その後一度棄却されたあと、再度問い合わせを行い、問い合わせへの返答は無かったが代わりに Chrome ウェブストアに再公開された

f:id:r7kamura:20170217064040p:plain

のでお寿司を食べました。

参考リンク

広告を非表示にする

Amazon Product Advertising API 用のクライアントライブラリをつくっている

Amazon Product Advertising API とは

Product Advertising API は、Amazon の商品情報や関連コンテンツをプログラムを通してアクセスできるサービスを提供することで、Web 開発者の皆様が、ご自分の Web サイトでAmazon の商品を紹介することによる紹介料の獲得を可能とします。

https://affiliate.amazon.co.jp/gp/advertising/api/detail/main.html

要は、Amazon のアソシエイトを使って商品を宣伝する目的で、Amazon の商品情報を API 経由で操作することができるサービスが提供されている。この記事は、この APIRuby から利用するためのライブラリをつくっているという話。

GitHub

ソースコードGitHub で管理されている。ライブラリの API 寄りの説明が README に書かれている。

https://github.com/r7kamura/rapa

rubygems.org

rubygems.org にも登録されているので、Gem として取得できる。執筆時点でのバージョンは 0.2.1。

https://rubygems.org/gems/rapa

既存のライブラリ

Amazon Product Advertising APIRuby から利用するための既存のライブラリとして、amazon-ecs がある。

今回ライブラリを新たに開発した動機は、大きく 3 つある。前半の方は個人的な動機で、後半の方はより社会的な動機と言える。

  • API の仕様や振る舞いを調べながら開発することで、API への理解を深めたかった
  • レスポンスの内容を解釈して Ruby として適切なインターフェースで取り出すところまでサポートしたかった
  • レスポンスの内容を取り出す機能を細かく用意することで、動作するドキュメントとしての側面を持ちたかった

これまでも https://amakan.net/ の開発で Amazon Product Advertising API には amazon-ecs を利用してお世話になっていたが、ドキュメントの量も多く、複雑で、果たして適切な使い方が出来ているのかどうか不安だった。今回腰を据えてライブラリをつくることで、ドキュメントや実際の API の挙動の調査にきちんと向き合い、いままで把握できていなかったところも正確に理解できるようになったと思う。

ライブラリの使い方

いま現在開発しているライブラリでは、いまのところ、大きく分けて 2 通りの使い方がある。

  • ASIN コードを与えて検索する
  • キーワードを与えて検索する

ASIN コードを与えて検索する

予め ASIN コードが分かっている場合、そのコードを与えて商品を検索するという API 呼び出しに対応した機能。ASIN コードというのは、Amazon の商品ごとに固有の10桁の ID であり、書籍の商品においては 10 桁表現の ISBN コードと同じものになる。ISBN コードが既に分かっている場合や、既に取得済みの商品データに更新が無いか調べるときなどに利用できる。

例えば、10桁表記の ISBN コードが「4041054443」の本のタイトルをライブラリを利用して調べてみる。

require "rapa"
client = Rapa::Client.new(access_key_id: "...", secret_access_key: "...", associate_tag: "...")
response = client.list_items(domain: "co.jp", asins: ["4041054443"])

Amazon Product Advertising API の利用には、アクセスキー ID、シークレットアクセスキー、アソシエイトタグの発行が必要になる。これらはどれも、Amazon アソシエイトの管理画面から発行できる。

domain に "co.jp" という値を渡しているが、これは日本の Amazon (amazon.co.jp) を対象に検索するように命令しており、この指定は必須である。ASIN コードは asins という名前で配列で指定しているが、1度のリクエストで最大 10 件までの検索結果を取得できるため、配列で指定するようなインターフェースになっている。

item = response.first

レスポンスは、商品の集合として扱いやすいように、 Enumerable のインターフェースを実装している。今回は結果は 1 件しか存在しないはずなので、先頭の要素を取り出すことにする。

puts item.title

商品のタイトルは #title メソッドで取り出せる。出力結果は以下の通り。

けものフレンズBD付オフィシャルガイドブック (1)

キーワードを与えて検索する

検索キーワードを与えて商品を検索するという API 呼び出しに対応した機能。

先程の例とは逆に、今度は「けものフレンズBD付オフィシャルガイドブック」というキーワードを元に商品を検索してみる。

response = client.search_items(domain: "co.jp", keywords: ["けものフレンズBD付オフィシャルガイドブック"])

今回は #search_items というメソッドを利用し、keywords という値で検索キーワードを与えている。さて、検索結果は複数件になるが、前述の通りレスポンスは Enumerable のインターフェースを実装しているということで、ここでは Enumerable#map を利用してタイトルだけを羅列してみることする。

puts response.map(&:title)

結果は以下の通り。

けものフレンズBD付オフィシャルガイドブック (1)
けものフレンズBD付オフィシャルガイドブック (2)
けものフレンズBD付オフィシャルガイドブック (3)
けものフレンズBD付オフィシャルガイドブック (6)
けものフレンズBD付オフィシャルガイドブック (5)
けものフレンズBD付オフィシャルガイドブック (4)
けものフレンズBD付オフィシャルガイドブック (1)(Blu-ray Disc)
けものフレンズ ‐ようこそジャパリパークへ!‐(1)<けものフレンズ ‐ようこそジャパリパークへ!‐> (角川コミックス・エース)
ようこそジャパリパークへ(初回限定盤)
ようこそジャパリパークへ(通常盤)

利用にあたっての前提・仮定など

実装にあたって Ruby 2.3 からの新機能である Hash#dig を試してみたので、このライブラリを使うには Ruby 2.3 以降が必要になる。また、HTTP クライアントとして Faraday、XML パーサとして MultiXML が実装に利用されているので、これらのライブラリに依存していることは注意されたい。

想定する利用者

https://amakan.net/、及び現在開発中の amakan video で利用するために開発したので、主な利用者は自分だろうが、Amazon Product Advertising API を利用する他の開発者が再利用できる実装にしたいとも考えている。如何せん API の仕様が大きく、開発者が利用していないが故に実装されていないという機能が沢山あるので、現実的なユースケースを収集することがライブラリの成長に繋がると思う。

何が達成できたらゴールか

ライブラリを開発するときは「どこまでやるか」というのを決めておいた方が、利用者視点では更新されず、開発者視点では要望に対応できずという宙ぶらりな状態になることが防げると思っている。今回の場合、このライブラリを利用して amakan と amakan video を稼働させられるところまで持っていければ、ライブラリのショーケースの 1 つにもなるし、ひとまずのゴールになると考えている。

しょぼいカレンダー API 用のライブラリでキャスト、スタッフ、楽曲の情報を取得できるようにした

https://github.com/r7kamura/syoboi_calendar の改良を重ね、キャスト、スタッフ、楽曲の情報も取得できるようにした。

コメント

しょぼいカレンダーはシリーズ作品を「タイトル」という単位で扱っていて、そのタイトルの中にコメントという形で色々な情報が含まれている。例えばたまたま直近で更新された作品「龍の歯医者」を例にすると、コメントにはこういう情報が含まれている。

*リンク
-[[公式 http://www.nhk.or.jp/anime/ryu/]]
-[[日本アニメ(ーター)見本市 http://animatorexpo.com/]]
-[[ニコニコチャンネル http://ch.nicovideo.jp/anime-ryu]]

*メモ
-全2話

*スタッフ
:原作:舞城王太郎
:監督:鶴巻和哉
:脚本:舞城王太郎、榎戸洋司
:キャラクターデザイン・作画監督:井関修一
:動画検査:村田康人
:色彩設計:長尾朱美
:美術監督:藤井綾香
:CG監督:鈴木貴志、宮城健
:撮影監督:山田豊徳
:音響監督:庵野秀明
:効果:野口透
:音響演出:山田陽
:音楽:TOMISIRO、ナカムラヒロシ
:アニメーション制作:スタジオカラー

*主題歌「ぼくらが旅に出る理由」
:作詞・作曲:小沢健二
:編曲:ナカムラヒロシ
:歌:RINKU
:コーラス:MASUMI

*挿入歌「かくれんぼ」
:作詞・歌:DAOKO
:作曲・編曲:ナカムラヒロシ

*キャスト
:岸井野ノ子:清水富美加
:ベルナール・オクタビアス:岡本信彦
:悟堂ヨ世夫:山寺宏一
:夏目柴名:林原めぐみ
:サルバトール・ブランコ:松尾スズキ
:有栖川カンネ:名塚佳織
:佐藤修三:徳本恭敏
:家村宗達:高木渉
:小澤:櫻井孝宏
:ギレリ:津田健次郎

記法のルール

数千件ほどデータを見た限り、以下のようなルールが存在することが分かった。

  • * で始まる行は Lv1 の見出し、つまり Markdown における # で始まる行と同等の存在ということになる。稀に Lv2 の見出しとして ** が登場することもある
  • : で始まる行は辞書形式で、- で始まる行は順序無しリスト形式になる。メモの項目などは、稀に行頭に何も付けず平文が書かれることもある
  • 見出しの内容は「リンク」「メモ」「スタッフ」などが多いが完全に固定されている訳ではなく、任意の項目になる可能性がある
  • 楽曲は挿入歌 1 、挿入歌 2 などのように、複数件存在する場合もある。曲の役割を表現する部分と鍵括弧との間には、半角スペースを1個含む場合と含まない場合がある
  • Lv1 の見出し間では、空行を設ける場合と、設けない場合がある。およそタイトルID 3000以前のものは空行を設けないものが多いが、それ以降では空行を設けるものが多い
  • URL は角括弧 2 つで囲み、タイトルと URL との間に半角スペースを1つ置く。プロトコルを archive:// のようにすると、Web Archive へのリンクとなる

ライブラリの使い方

syoboi_calendar gem では以下のようなインターフェースでコメントの情報を利用できる。

タイトルの取得

require "syoboi_calendar"

client = SyoboiCalendar::Client.new
title = client.list_titles(title_id: 4482).first
title.class #=> SyoboiCalendar::Resources::TitleResource

キャスト

title.casts.count #=> 10
title.casts[0].class #=> SyoboiCalendar::Personality
title.casts[0].name #=> "清水富美加"
title.casts[0].role #=> "岸井野ノ子"

スタッフ

title.staffs.count #=> 17
title.staffs[0].class #=> SyoboiCalendar::Personality
title.staffs[0].name #=> "舞城王太郎"
title.staffs[0].role #=> "原作"

主題歌

title.songs_theme[0].class #=> SyoboiCalendar::Song
title.songs_theme[0].name #=> "ぼくらが旅に出る理由"
title.songs_theme[0].role #=> "主題歌"
title.songs_theme[0].personalities[0].class #=> SyoboiCalendar::Personality
title.songs_theme[0].personalities[0].name #=> "小沢健二"
title.songs_theme[0].personalities[0].role #=> "作詞・作曲"

挿入歌

title.songs_inserted[0].class #=> SyoboiCalendar::Song
title.songs_inserted[0].name #=> "かくれんぼ"
title.songs_inserted[0].role #=> "挿入歌"
title.songs_inserted[0].personalities[0].class #=> SyoboiCalendar::Personality
title.songs_inserted[0].personalities[0].name #=> "DAOKO"
title.songs_inserted[0].personalities[0].role #=> "作詞・歌"

Ruby 製の DMM Web サービス用のライブラリをつくりはじめた

DMM Web サービスとは

https://affiliate.dmm.com/api/ に公式の情報があります。下手な説明はできないので、こちらを読んでください。

ホスティング

今回つくりはじめたライブラリ rdmm のソースコードは、GitHubホスティングされています。最低限の使い方も README に(自分が忘れたときに思い出せる程度には)記述してあります。

https://github.com/r7kamura/rdmm

利用時は rubygems.org から Gem として取得できます。執筆時点でのバージョンは 0.0.3。

https://rubygems.org/gems/rdmm

rdmm の機能

現時点では以下の2つのエンドポイントに対応しています。

  • 商品情報検索 API
  • シリーズ検索 API

商品情報検索 API は、名前の通り DMM の扱っている商品を検索できる API です。例えば自分の用途ではアニメの動画を検索するのに利用していて、「幼女戦記 1話」「幼女戦記 2話」「けものフレンズ 1話」のような単位で検索ができます。「検索」といってもキーワードを与えて検索する機能だけではなくて、沢山あるデータを様々な条件で絞り込めるというイメージです。floor というカテゴリのような単位があり、DMM.com のアニメを表す floor で絞り込んで検索できたりするので、これを利用しています。

シリーズ検索 API は、特定の商品群をまとめるシリーズという単位を検索できる API で、例えば前述した例で言えば「幼女戦記」「けものフレンズ」という単位で検索できます。これも floor を利用して検索できるので、DMM の扱っているアニメのシリーズを検索する、といった使い方ができます。

余計な変換をする

例えば日時データなんかは JSON で表現しようとすると文字列や整数として表現することが多いですが、各言語ごとに日時を表すための適切なクラスが存在しているはずで、Web API のクライアントライブラリではその辺りの変換を行ってくれると便利そうなので実装しています。

そんな勝手な変換は要らないというのであれば、汎用的な HTTP クライアントライブラリを利用すればいいはずで、特定の Web API に特化したライブラリを選択しているということは、つまりその辺りの特別な計らいが求めているのだという考えでやっていっています。

余計な変換という点で言えば、名前の変換というのもそうで、分かりづらい略語を利用した名前のパラメータは、ライブラリのレベルでは分かりやすい名前のパラメータとして扱うとか(例: cid → content_id)、データの型に適した名前でアクセスできるようにするとか、(例: actor → actors)、その辺りの変換もある程度好き勝手にやることにしています。

そのままのデータを保存できるようにする

Web API から商品情報を表す何らかのデータ(例: XMLの要素の集合)が返ってきたとして、そのデータを解釈するロジックがクライアントライブラリ側に実装される訳ですが、クライアントライブラリの機能改善に伴ってロジックが徐々に変更されていくことも、珍しくはありません。

しかしライブラリがバージョンアップされたからといって、データの再取得が必要になっては困ります。例えばこれは別のサービスの例ですが、Amazon の商品情報を扱う Web API は、1度に最大10件しかデータを取得できないし、毎秒1リクエスト程度しか許されていないので、再取得しようとするとかなりの時間が必要になります。

今回クライアントライブラリを実装するときにも、出来るだけ Web API から得たそのままのデータを取り出して DB などに保存できるように、また保存したデータを取り出して再度解釈できるように、ということに気を付けています。

追記

商品情報検索 API、シリーズ検索 API 以外のエンドポイントにも対応しました。