PHPド素人がYahoo!検索プラグインを開発〜リリースするまでのまとめ

LUXURY LAGARE

Yahoo!検索 スタッフブログ

Yahooプラグインとは?

一言で言えばYahooの検索結果に独自情報を付加できます。
プラグインを入れた人以外はそのままの検索結果が表示されますが、便利なので
今回の公開を機にユーザーも対応サイトも徐々に増えていくと思います。


早速簡単なプラグインを開発して、公開するところまでやってみました。

意外と簡単

私はperlプログラマですがphpは名前を聞いたことがあるだけで今回はじめて触りました。
そんな私でも2-30分でプラグインを開発して公開することができたので、
この喜びを一人でも多くの人に体験してもらいたいと思っています。

用意するもの

  • Yahooアカウント

今回はエディタすら使いません!!

プラグイン作成に必要と思われるスキル

  • HTMLの基本的な知識
  • いずれかのプログラミング言語でif文を書けるレベル
  • 自分でサイトやブログを作成した経験があれば尚良し

このブログを見ているなら、多くの人が上記にあてはまると思います。

せっかくなので是非チャレンジしてみてください!!

今回作成するプラグインについて

今回は入門向けということで、とあるサイトの検索結果に
「画像を貼り付ける」というプラグインを作成し、公開します。

スクリーンショットは明日貼り付ける予定。
スクリーンショットが必要な方は一旦ブックマークして明日以降ご覧下さい。

下準備

  • Yahooアカウントを用意します。
  • 自分が所有するサイトのプラグインを作る場合はサイトエクスプローラーで認証しておきます。
  • アイコンがあったほうがいいので作成するサイトの16*16 85*65のアイコンを準備します。

http://developer.yahoo.co.jp/other/searchplugin/
に詳細情報があります。ブックマークしておきましょう。


ここまでについては解説しません。

Yahoo!検索プラグイン 開発ツール 開発ガイド

http://developer.search.yahoo.co.jp/
にて作業をすすめます。ここでYahooIDが必要です。

  1. 表示用プラグイン→新規作成をクリック
  2. 基本情報の入力
    1. 名称にサイト名を入力
    2. 表示は標準型のままでOK
    3. 内容説明にプラグインの説明
    4. カテゴリ選択
    5. アイコンアップロード
    6. 利用規約に同意して次へ
  3. 基本情報の入力
    1. URLマッチングパターンの入力(例の通り)
    2. テストURLの入力(最大10個だが3個でOK)
    3. 画像取得ドメイン(サブドメは指定しなくて良い)
    4. リンク先ドメイン(今回は空でOK)
    5. 保存し次へ
  4. データサービスの追加
    1. なにもせず保存し次へ
  5. 表示の調整
    1. ブラウザでPHPのソースをいじる(下記参照)
    2. 正しく表示されているか例を見て確認する
  6. 完了
    1. この表示用プラグインを公開し、ほかの利用者からも利用できるようにする。 をクリック
  7. プラグインの公開
    1. プラグインをほかの利用者も利用できるように公開し、あわせてプラグイン一覧への掲載を希望する を選択
    2. ソースコードの公開を選択
    3. ロゴをアップロード
    4. 適用対象サイトを記入
    5. 完了をクリック

ソース全文

public static function getOutput() {
    $linkurl = Data::get('yahoo:index/dc:identifier');
    $itemkey = 0;
    $match;
    if(preg_match('/luxury-la.com\/\d+\/\d+\/(.+?)\.html/', $linkurl, $match)){
        $itemkey = $match[1];
    }
    $ret = array();

    /* If you leave these blank, the default title and summary will be shown */
    $ret['title'] = Data::get('yahoo:index/rel:Posting/dc:title');
    $ret['summary'] = Data::get('yahoo:index/rel:Posting/dc:description');

    /* Now you fill in the rest. Use Data::get and Data::xpath to get data  */

    // Image
    if($itemkey){
        $ret['image']['src'] = 'http://a248.e.akamai.net/f/248/37952/1d/image.shopping.yahoo.co.jp/i/g/' . $itemkey;
    }else{
        $ret['image']['src'] = 'SM_VAL_DEFAULT';
    }
    $ret['image']['alt'] = SM_VAL_DEFAULT;
    $ret['image']['title'] = SM_VAL_DEFAULT;

    // Deep links - up to 4
    $ret['links'][0]['text'] = SM_VAL_DEFAULT;
    $ret['links'][0]['href'] = SM_VAL_DEFAULT;
    $ret['links'][1]['text'] = SM_VAL_DEFAULT;
    $ret['links'][1]['href'] = SM_VAL_DEFAULT;
    $ret['links'][2]['text'] = SM_VAL_DEFAULT;
    $ret['links'][2]['href'] = SM_VAL_DEFAULT;

    // Key Value pairs - up to 4
    $ret['dict'][0]['key'] = SM_VAL_DEFAULT;
    $ret['dict'][0]['value'] = SM_VAL_DEFAULT;
    $ret['dict'][1]['key'] = SM_VAL_DEFAULT;
    $ret['dict'][1]['value'] = SM_VAL_DEFAULT;
    $ret['dict'][2]['key'] = SM_VAL_DEFAULT;
    $ret['dict'][2]['value'] = SM_VAL_DEFAULT;
    $ret['dict'][3]['key'] = SM_VAL_DEFAULT;
    $ret['dict'][3]['value'] = SM_VAL_DEFAULT;

    /* This is for infobar apps
       You can put a subset of HTML in here
       See the docs for more details */
    $ret['infobar']['blob'] = SM_VAL_DEFAULT;

    return $ret;
}

ソースの解説

$linkurl = Data::get('yahoo:index/dc:identifier');
でリンク先のURLを取得しています。

if(preg_match('/luxury-la.com\/\d+\/\d+\/(.+?)\.html/', $linkurl, $match)){
    $itemkey = $match[1];
}

でURLからアイテムのキーを取得しています。

if($itemkey){
        $ret['image']['src'] = 'http://a248.e.akamai.net/f/248/37952/1d/image.shopping.yahoo.co.jp/i/g/' . $itemkey;
}

でアイテムのキーから画像のURLを作って渡します。

独自のプラグインを作るヒント

$ret['links'][0]['text'] = SM_VAL_DEFAULT;
$ret['links'][0]['href'] = SM_VAL_DEFAULT;

をいじると関連リンクを4件まで設定できます。
トップページへのリンクを入れてもいいでしょうし、
少々プログラムの知識は必要ですが、カテゴリページ等にリンクすることも可能です。

$ret['dict'][0]['key'] = SM_VAL_DEFAULT;
$ret['dict'][0]['value'] = SM_VAL_DEFAULT;

をいじるとキーバリュー形式で任意の値を4件まで表示できます。
商品ページであれば価格を入れる、とか
ブックマークされた数を入れる、とかあとはあなたのアイデア次第です。

プラグインの宣伝

出来上がったプラグインのURLを自分のサイトに書いておけば
プラグインを使ってくれる人が出てくるかもしれません。
ちなみに私が作ったプラグイン
http://gallery.search.yahoo.co.jp/application?smid=jA9.s
で公開しております。

あなたが作ったプラグインの情報も是非教えてください。

ご不明な点がありましたら、

twitter @noritaka_okabe

までどうぞ。

お疲れ様でした。長文をお読みいただきありがとうございました。

PHPド素人がYahoo!検索プラグインを開発〜リリースするまでのまとめ - クリティカルスピード開発日誌 PHPド素人がYahoo!検索プラグインを開発〜リリースするまでのまとめ - クリティカルスピード開発日誌

Tokyo (Cabinet|Tyrant|Dystopia)で作る全文検索の色々な方法まとめ

前提としてTokyoCabinet(若しくはTokyoTyrant)にテーブルDBがあり、
その複数カラムにまたがって全文検索ができるようにしたい。

例えばブログでいえばタイトルと本文と追記から全文検索をしたいとする。

Tokyoシリーズを使うということは平林幹雄氏の製品を信頼しているということなので、
できればmade by mikioで検索できればいいなと思って調べてみたところ、
想像以上に様々な方法があって迷った。
有力そうな4案について勝手に長所と短所をまとめてみるので参考にして下さい。

http://hyperestraier.sourceforge.net/index.ja.html

mixi engineer blog

mixi engineer blog

  • Tokyo Dystopiaを使う

mixi engineer blog

Hyper Estraier案の長所

  1. まあまあ簡単
  2. 柔軟性もある
  3. 検索CGIのサンプルがついている
  4. 歴史があるのでネット上に情報が多い

Hyper Estraier案の短所

  1. 本体とインデックス以外に別途est形式のデータを作って保持する必要がある
  2. Hyper Estraierについての知識が別途必要
  3. リアルタイム検索にはあまり向かない
  4. 最近は更新されていない(最終更新2007年)
  5. 速度的にはそこそこ

Tokyo Tyrantにインデックスを張る案の長所

  1. 非常に簡単
  2. 本体のデータをそのまま活かせる
  3. ネット上にある程度情報がある

Tokyo Tyrantにインデックスを張る案の短所

  1. インデックスが非常に大きくなる
  2. 他の方法と比べると柔軟性が低い
  3. 大規模データを扱うのには向かない(主に速度的な面で)
  4. リアルタイム検索には全く向かない

Tokyo Tyrant+Lua拡張案の長所

  1. リアルタイム検索に向く
  2. 速度的にもオンメモリなら超高速
  3. 柔軟性あり(Luaスキルに依存)

Tokyo Tyrant+Lua拡張案の短所

  1. 大規模データを扱うのには向かない(メモリに乗らなくなってメリットが消える)
  2. 十分なメモリが必要
  3. 多少難しい(Luaの知識があればそうでもないがある人は少ない)
  4. ドキュメントが少ない

Tokyo Dystopia案の長所

  1. 最高速が見込める
  2. 極めて高い柔軟性
  3. 大規模データでも安心
  4. リアルタイム検索も可能

Tokyo Dystopia案の短所

  1. 難易度が極めて高い
  2. 実装に時間がかかる
  3. ドキュメントは無いに等しい

間違いや他の選択肢があれば是非教えてください。

はじめてのRuby(Rack)挑戦記録

5:07
おはようございます。
今日は今までやろうやろうと思って放置していたRubyに初挑戦します。
perl使いなので書き方や考え方がperlに偏っているかもしれませんがご了承下さい。

5:10
まずはRubyのインストール

sudo apt-get install librack-ruby


5:13
hello world
参考にしたのはこのページ
http://route477.net/d/?date=20080716

mkdir rack
cd rack
vi criticalspeed.rb

rbというのはperlでいうところのpmなのだろうか?
→plのようなものらしい(by IRC)

criticalspeed.rb

require 'rubygems'
require 'rack'

class CriticalSpeed
  def call(env)
    [200, {"Content-Type" => "text/plain"}, ["Hello, World"]]
  end
end
vi criticalspeed.ru

多分ruというのはperlでいうところのpl。
→.psgi相当とのこと(by twitter

criticalspeed.ru

require 'criticalspeed'
run CriticalSpeed.new
rackup criticalspeed.ru

no such file to load rubygems

sudo apt-get install rubygems
rackup criticalspeed.ru

無事wgethello world成功。9292番portが標準みたい。

9:36
もう30分経過。新しいことをやると時間が経つのがはやいなあ。
次はKyoto Cabinet入れる。
実はこれのrubyバインディングが触りたくて今日rubyをはじめたようなもの。
参考にしたのはこのページ
http://1978th.net/kyotocabinet/spex-en.html
http://1978th.net/kyotocabinet/rubydoc/

sudo apt-get install g++
cd /tmp
wget http://1978th.net/kyotocabinet/kyotocabinet-0.9.8.tar.gz
tar zxvf kyotocabinet-0.9.8.tar.gz
cd kyotocabinet-0.9.8
./configure
make
make check
sudo make install
cd ..
wget http://1978th.net/kyotocabinet/rubypkg/kyotocabinet-ruby-1.0.tar.gz
tar zxvf kyotocabinet-ruby-1.0.tar.gz
cd kyotocabinet-ruby-1.0
ruby extconf.rb

no such file to load mkmf
extconf.rb は Makefile.pl相当?
ruby のバージョンは1.8.7みたい。1.9を入れるべきかもしれないけど、よくわからないので保留。

sudo apt-get install ruby1.8-dev
ruby extconf.rb
make
sudo make install

インストールに成功した模様

6:02
開始から約1時間経過。
今度はKyotoCabinetに値を入れる練習。

cd ~/rack
vi make_kyotocabinet_benchmark_htmls.rb

make_kyotocabinet_benchmark_htmls.rb

require 'kyotocabinet'
include KyotoCabinet

DB::process('casket.kch', DB::OWRITER | DB::OCREATE) do |db|
  db['www.example.com/index.html'] = 'hello kyoto cabinet'
end

何故DBを閉じなくていいのかわからない。do |db|ってなんだ?tie?
IRCで教えてもらったけどまだ???状態
エラーが出ていないので値が入ったと仮定して先に進む。
→作者のブログで解説が出て理解できた
mixi Engineers’ Blog » 京都収納棚紅玉束縛: Rubyで簡単、DBプログラミング mixi Engineers’ Blog » 京都収納棚紅玉束縛: Rubyで簡単、DBプログラミング

6:27
KyotoCabinetから値を取り出してRack経由で表示してみる。

criticalspeed.rb

require 'rubygems'
require 'rack'
require 'kyotocabinet'
include KyotoCabinet

class CriticalSpeed
  def call(env)
    req = Rack::Request.new(env)
    fullpath = req.fullpath().sub(/^\//, '')
    output = nil
    DB::process('casket.kch', DB::OREADER) do |db|
      output = db[fullpath]
    end
    if output
      [200, {"Content-Type" => "text/html"}, [output]]
    else
      [404, {"Content-Type" => "text/plain"}, ['404 not found']]
    end
  end
end

置換の違いにとまどったけど、なんとかできた。
perl版とちょっと違うけどrubyのほうが短く書けるなあ。

7:25
別のマシンからベンチマークを取ってみる

/usr/sbin/ab -n 100 -c 10 http://192.168.1.123:9292/www.example.com/index.html
Request per second: 15.81 [#/sec] (mean)

Rack(WEBrick)が遅いなあと感じる。Rackで使えるウェブサーバーって何がオススメ?
いずれにしても私のrubyスキルでは最適化なんてとても無理っぽい。

7:35
@j_kinjou さんからThinやEventedMongrelがいいとの情報 thx。
twitter便利。

sudo apt-get install gem
sudo gem install rack
sudo gem install thin
rackup --env production -s thin criticalspeed.ru
/usr/sbin/ab -n 10000 -c 100 http://192.168.1.123:9292/www.example.com/index.html
Request per second: 1193.70 [#/sec] (mean)

Mongrelを入れて何これはやい。もしかしてperl版よりはやいんじゃないか?
と思ったらデータが帰って来ていない。
Thinも何故か動かない
本日初のハマリなので朝食を食べる

9:00
とりあえずこの記事を公開する。
あとRubyIRCに入ってみた。日本語が使えるのが嬉しい。

はまった原因は --env productionだった模様。その遠因としてRackのバージョンが0.1だった。
というわけでRuby Enterprize EditionというのをIRCで教えてもらいインストールした。
その後流れで、Passengerをインストール
http://www.modrails.com/install.html

11:04
IRCで教わって環境を新しくした結果Thinが動いた

sudo apt-get install gem
sudo gem install rack
sudo gem install thin
rackup --env production -s thin criticalspeed.ru
/usr/sbin/ab -n 10000 -c 100 http://192.168.1.123:9292/www.example.com/index.html
Total transferred: 190000 bytes
Request per second: 591.91 [#/sec] (mean)

16:00
お昼寝していたらこんな時間になった。
50000ページKyotoCabinetに突っ込む時間を計測することにする。

make_kyotocabinet_benchmark_htmls.rb

require 'kyotocabinet'
require 'rubygems'
require 'random_data'
require 'net/http'
include KyotoCabinet
Net::HTTP.version_1_2

html_body = Net::HTTP.get('www.real-unreal.info', '/readmejp100.html')
DB::process('casket.kch', DB::OWRITER | DB::OCREATE) do |db|
  domain = 'www.example.com'
  db[domain + '/index.html'] = html_body;
  10000.times do
    rand_word = Random.alphanumeric(5)
    dir_name = domain + '/' + rand_word
    ["index", *"index2".."index5"].each{|base| db["#{dir_name}/#{base}.html"] = html_body}
  end
end
time ruby make_kyotocabinet_benchmark_htmls.rb
1.34s user 1.76s system 12% cpu 24.262 total
/usr/sbin/ab -n 10000 -c 100 http://192.168.1.123:9292/www.example.com/index.html
Total transferred: 138440000 bytes
Request per second: 182.61 [#/sec] (mean)

というわけではじめてのRubyプログラミングとしてやりたいことは一応できました。
IRCで助けてくれたRubyコミュニティの方々ありがとうございました。
ソースでRubyらしくない書き方とかあると思うので気がついたら教えてください。

WEBrickとThinでパフォーマンスが一桁以上違うんですね。

18:00
RubyIRCでソースを見てもらう。
Net::HTTPが1行で書ける事をおしえてもらったので修正

18:13
10000.times.do end という書き方を教えてもらったので修正
もはや最後のは元々自分で書いたコードと別物に修正

追記:作者の方の解説(ちょっと難しいです)
mixi Engineers’ Blog » 京都収納棚紅玉束縛: Rubyで簡単、DBプログラミング mixi Engineers’ Blog » 京都収納棚紅玉束縛: Rubyで簡単、DBプログラミング

KVSをWebサーバーとするとどうなるのか?ベンチマークを取ってみた。

一昨日公開した拙作のクリティカルスピードですが、ベンチマークを公開していなかった為、
どの程度使えるのかよくわからないというご意見を頂きました。
クリティカルスピードについてはMOONGIFT様の紹介をご覧下さい
KVSを使った高速配信Webサーバ「クリティカルスピード」


そこで古いサーバーでベンチマークを取ったので結果のみご紹介いたします。

Celeron 430 1.80GHz メモリ2Gのサーバーで実験

WRITE性能

ファイルベースの場合(普通のウェブサーバー)

静的に1万ディレクトリ5万ページを作成する

time perl make_static_benchmark_htmls.pl
27.81s user 6.59s system 93% cpu 36.935 total

クリティカルスピードの場合(KVSをウェブサーバーとして使う)

1万ディレクトリ5万ページを作成する

time perl make_criticalspeed_benchmark_htmls.pl
4.08s user 1.57s system 22% cpu 24.678 total

1万ディレクトリ5万ページを別のサーバーから作成する

time perl make_criticalspeed_benchmark_htmls.pl
5.01s user 3.23s system 6% cpu 2:05.33 total

READ性能

ファイルベースの場合(普通のウェブサーバー)

Plack::Middleware::Staticでサーバーを立て、ローカルネットワーク内から10000回アクセスする

/usr/sbin/ab -n 10000 -c 100 http://192.168.1.123:7777/index.html
Time taken for tests: 14.398395 seconds
Total transferred 139290000 bytes
Requests per second: 694.52 [#/sec](mean)
Time per request: 1.440ms

クリティカルスピードの場合(KVSをウェブサーバーとして使う)

Plackでサーバーを立て、ローカルネットワーク内の別PCから10000回アクセスする

/usr/sbin/ab -n 10000 -c 100 http://192.168.1.123:7778/www.example.com/index.html
Time taken for tests: 25.397102 seconds
Total transferred 139030000 bytes
Requests per second: 393.75 [#/sec](mean)
Time per request: 2.540ms

TokyoTyrantをWebサーバーとみなし、ローカルネットワーク内の別PCから10000回アクセスする

/usr/sbin/ab -n 10000 -c 100 http://192.168.1.123:1979/www.example.com/index.html
Time taken for tests: 12.56123 seconds
Total transferred 138348192 bytes
Requests per second: 829.45 [#/sec](mean)
Time per request: 1.206ms

まとめ

以上WRITE性能については改善したといって良いと思います。

実行時間は2/3になっていますし、CPU負荷が1/4になっています。

READ性能については40%ほど遅くなっていますが
古いサーバーで秒間400アクセス捌けているので、
実用に耐えると考えています。

そして、TokyoTyrantを直接Webサーバーとして使った場合は
ファイルベースの時と比較しても12%速い結果となっています。

速度を目指してKVSをウェブサーバーとして使うという選択肢も
十分ありえることを示唆していると思います。

但し、TokyoTyrantを直接ウェブサーバーとして使う場合設定が複雑になる為、実用を考えると
PlackやRack等でWebサーバーをたてて、そこからKVSにアクセスするのが良いと考えます。

また、クリティカルスピードの場合、localhostにファイルを置く時と
同じような感覚で別のサーバーにファイルを配置することができます。

速度的にも5万ページを2分なら十分実用的といえると思います。

以上のベンチマークの結果、クリティカルスピードは書込時の負荷を減らしたい人、
あるいは別のサーバーに素早く書込みたい人には実用的な選択肢として検討していただけると思います。


ベンチマークのソースは次回バージョンアップ時に
比較表等をつけて同梱する予定です。

クリティカルスピードやこのベンチマークについて
ご質問やご意見がありましたら、twitterにてお気軽にどうぞ。
twitter

良かったらあなたのサーバーでのベンチマークも是非教えてください。

4月14日追記
OSS ipediaにて事例紹介
http://ossipedia.ipa.go.jp/case/102/

自己紹介

はじめまして岡部典孝と申します。
http://twitter.com/noritaka_okabe

はてなダイアリーでは主に技術的なことを書いていく予定です。

プログラミング perl plack TokyoTyrant nginx KVSあたりが好きな方、
今後ともよろしくお願いします。