w3mでサーチエンジン呼び出し - 一般化編

w3mユーザなら普通 urimethodmap を設定して g:keywordgoogle検索をやってると思うが、いろんなサーチエンジンで使えるよう一般化してみる。簡単に設定できるようDSLを定義しよう。

defsearch :google, "UTF-8", "http://www.google.com/search?q=%s&hl=ja&num=100"
defalias :g, :google

たとえば上のように宣言すると google:keyword や g:keyword で keyword をgoogle検索する。keywordはUTF-8に変換されて%sを置き換える。サーチエンジンの設定で言いたいことはたったこれだけ。ようつべやニコニコ動画google mapなどにも簡単に対応できる。

さぁ、CGIスクリプトを書こう。俺はw3mのLocal CGIとして使うが、普通のCGIスクリプトなので他のCGI実行環境でも動くはず。*1

#!/usr/bin/env ruby
require 'cgi'
require 'iconv'

CONFIG_FILE = File.expand_path "~/.search-engine.rb"
DEFAULT_ENCODING = 'EUC-JP'
class SearchEngines
  def encoding(encoding)
    @from_encoding = encoding || DEFAULT_ENCODING
  end

  def defsearch(name, to_encoding, url_format)
    (@searches||={})[name.to_s] = lambda {|q|
      q = Iconv.iconv(to_encoding, @from_encoding, q).first if to_encoding
      format(url_format, CGI.escape(q))
    }
  end

  def defalias(alias_, original)
    @searches[alias_.to_s] = @searches[original.to_s]
  end

  def load_config(config_file=CONFIG_FILE)
    eval(File.read(config_file), binding)
  end

  def search(site, query)
    url = @searches[site][query]
    print "Location: #{url}\r\n\r\n"
  end
end

site, query = CGI.unescape(ENV['QUERY_STRING']).split(/:/, 2)
se = SearchEngines.new
se.load_config
se.search(site, query)

ポイントは設定ファイル ~/.search-engine.rb を SearchEngines オブジェクトのコンテキストでevalしていること。defsearch宣言は、ハッシュ@searchesのエントリに変換される。エントリはlambdaによるクロージャーだ。なのでto_encodingとurl_formatは呼び出し時でも見える。デフォルトエンコーディングEUC-JPなので、異なる人は変えておこう。

設定ファイルのサンプルをどうぞ。他のサーチエンジンの追加は簡単だ。適当にキーワードを検索して、キーワードの部分を%sに置き換え、「=」でDocument Charsetを調べよう。

# ~/.w3m/urimethodmap への追加を忘れずに
# g: file:/cgi-bin/search-engine.cgi?%s
# エンコーディングはEUC-JP
encoding "EUC-JP"

# Google search
defsearch :google, "UTF-8", "http://www.google.com/search?q=%s&hl=ja&num=100"
defalias :g, :google

# Google code search
defsearch :gcode, "UTF-8", "http://www.google.com/codesearch?q=%s&hl=ja&num=100"

# ようつべ
defsearch :youtube, "UTF-8", "http://www.youtube.com/results?search_query=%s&search_type=search_videos&search_sort=relevance"

# Google map
defsearch :map, "Shift_JIS", "http://maps.google.co.jp/maps?f=q&hl=ja&q=%s&output=html"

# ニコニコ動画検索
defsearch :nico, "UTF-8", "http://socia.lix.jp/lab/nicosearch/?key=%s"

# 価格.com
defsearch :kakaku, "Shift_JIS", "http://kakaku.com/ksearch/search.asp?Query=%s"

最後に ~/.w3m/urimethodmap に以下を追加することを忘れずに。

g:      file:/cgi-bin/search-engine.cgi?%s
google: file:/cgi-bin/search-engine.cgi?%s
youtube: file:/cgi-bin/search-engine.cgi?%s
map: file:/cgi-bin/search-engine.cgi?%s
nico: file:/cgi-bin/search-engine.cgi?%s
kakaku: file:/cgi-bin/search-engine.cgi?%s
gcode: file:/cgi-bin/search-engine.cgi?%s

今思ったんだが、urimethodmapに指定されてないschemeURIをリクエストしたら、Unknown URIと怒るのではなくてLocal CGIを呼び出せればいいなと思った…Rubyにmethod_missingがあるように。

*1:セキュリティは考えていないのでローカルにサーバを立てよう。