Amazon KDP Kindle 電子書籍 個人作家ランキング


表題のものがみたいと思い、実はAmazonの検索窓に正規表現の入った論理条件を入れてみたり
色々試していたのだが、無理であった。問い合わせた結果として、無理だと分かったので
作ってみた。


Amazon APIの絞り込み条件で、出版社が空欄である場合など、
色々試してみたが、そういう絞り込みを許していないようだったので、
忌川タツヤさんという方がまとめられた個人作家リストを用いることにした。


ただ、当たり前であるが、絞り込み条件に6000名をOR条件で指定しても
Amazonはそんな長大なリクエストを受け付けない。


ギリギリの長さのクエリを発行する、という試みもあり得るかもしれないが
私は単純に、一名毎のリクエストを発行するループで検討した。
だが、実はAmazon APIには制約があり、一時間に2000リクエストまでしか受け付けない。
しかも、2000リクエスト以内なら一気にドカンと打っても良いのかというと、
実は1リクエスト毎に1.8秒の間隔を空けないとダメなのだそうである。


Amazon APIは書籍の情報を返すリクエストと、売上ランクを返すリクエストも分かれている。
一名について2回リクエストを投げることにしたのだが、この2回の間に、2秒間sleepする処理を入れた。
その上で、6000名の名簿を300名ずつの20分割で考え、
順次300名ずつを処理する600リクエストを投げるスクリプトを、CRONバッチで一時間ごとに走らせる仕組みを考えた。


 ・MySQLデータベースに持つ書籍データを定期実行のスクリプト処理で更新していくバックエンド
 ・MySQLだけにアクセスして、上位300件をレイアウトに埋め込むフロントエンド


スクリプト中のリクエストの間隔に2秒ずつのsleepがあるので、このスクリプトは300名を処理するために20分かかる。
phpのmax_execute_timeは初期値が30だったが、ini_set(3600)で一時間に延ばし、ローカルのApacheサーバでは正常終了を確認できた。


だが、無料レンタルサーバのHOSTINGERの環境では、CRONが最短1時間間隔、2つまでしかセットできない。
そして、ini_set()で変更したphp.iniの値とは関係なく、スクリプトは30秒でプロセスをkillされてしまう。
(それはそうだ。そうしなければ誰かが無限ループを仕込んでサーバを停止させてしまう)


この時点で、非常に悩んだ。
ローカル環境ではphp.iniは変更でき、長時間処理も動くので、グローバルIP自宅サーバを立てる可能性、
制約のゆるい別の無料レンタルサーバを探す選択肢、スクリプトGoogle App Engineに置き、Google App EngineとHOSTINGERとの間で
リモートの接続をする可能性、などなど検討していた。
が、自分は365日/24時間の稼働率でサーバを運営するつもりはなく、一番条件がいい無料レンタルサーバが現時点でHOSTINGERに思え、
Google App Engineを使っても長時間バッチは(Backend APIなどはあるらしいが)切られるしHOSTINGERはリモートからのMySQLアクセスができなくなっている。


長時間処理を許している環境などなさそうなので、ここで私は、1時間おきに20分かける処理ではなく、
20秒で終了するスクリプトを一分おきに実行させる方法に改めた。
一時間1回、20分かけて300名ずつを処理する20段階のスクリプトではなく、
毎分1回、20秒かけて5名ずつ処理する1200段階のスクリプトになった。
HOSTINGERのCRONが最短一時間間隔しか許していないが、cron-job.orgというサービスを使えば、一分毎に指定URLを叩いてくれる。
これを引き金につかって毎分実行が可能なので、毎分スクリプトが叩かれ、
叩かれたスクリプトの中で現在時刻に応じて今、どこからどこまでの5名を処理すべきかを分岐させる。
そうすれば、スクリプトも1個で済む。同じスクリプトを毎分呼ぶが、現在時刻に合わせて適切な範囲のデータを処理してくれる。


下記のように指定した上で、$startと$endの中身で
作家リストのidを範囲検索し、出てきた作家リストをforeach()で回しながら
名前の値でAmazon APIにリクエストを投げ処理の最後にsqlでテーブルに結果をつっこむ。
一回の実行でMySQLに対して取得と削除と更新を1回ずつ、
リクエストは最大で10回投げるだけ。負荷もかからない。

    //300人毎時、5人毎分単位で分割処理する。その両端の変数
    $start = (date("H")*300)+(date("i")*5);
    $end = (date("H")*300)+(date("i")*5)+4;

そして処理の死活監視を行うために、終了時に自分あてのメールに完了報告を飛ばすように書いておいた。


しばらく眺めていて、ようやく本番環境でもMySQLにデータが取り込まれ出したのを確認し、寝た。
すると、朝になると、HOSTINGERからアカウント一時停止の連絡が来ていた。
一日1440通ものメールを送られると、HOSTINGERドメインがスパムに分類され、問い合わせ対応業務などに支障がでる。
考えてみればその通りだった。至急対応し、メールでの死活監視をなくして、HOSTINGERアカウントを復旧して頂けた。


20時間かけて6000名を処理する上記のバックエンドが書籍情報をかきあつめている間にフロントエンド側を作ったので、
翌日に本番環境のデータでテストを行ってみた。
ところが、驚いたことに、上位の書籍は出版社の作品でうまっていた。
著者名をみてきづいたのだが、みな、名前が短い場合と、姓と名の間にスペースが入っている場合におかしい。
Amazonで確認してみた。結果分かった事は、
そもそもAmazonは、完全一致検索ではないということである。「山田 太郎」と検索したときに出てくる一覧は
山田太郎氏の著作ではない。それぞれが含まれていれば、山田花子と鈴木太郎さんの共著、みたいなものでも出る。
おそらくは、あえてそうしているのだろう。だが、今回の用途では完全一致検索が条件なので、フロントエンド側で処理を加えた。
なるべく作家リストはマスタデータとして手を加えず、表示側で対応することにした。
だが、それでもまだおかしいデータは少し残っていた。同じレコードがなんども重複している所がある。
調べてみると、作家リストに重複があることが分かった。例えば勝間和代さんが、二度登録されている。
この場合も、実はマスタデータである名簿には手を加えていない。フロントエンド側で重複を除去している。


そんなこんなで、一応完成したと思うのでここに公開しました。
http://kdp.url.ph/cakephp/ranking


注: 無料サーバなので、あまりアクセスが集中した場合、HOSTINGERのトップページにリダイレクトされるようです。
   その場合は最大で一日半ぐらいまっていると、またアクセス出来るようになると思います。