ドメインを正しく扱うのは意外と難しい
ドメインの扱いって意外と難しいんですよね。仕様が決められているし文字列なんだしちゃちゃっと正規表現で、なんて考えで実装しちゃうと後で作り直すことになります。
とても幸運な事に今のRubyはドメインを扱うライブラリがとても充実しています。この記事ではRubyからドメインを操作する便利なライブラリとその使い方を説明します。
標準ライブラリのURIを使う
URIクラスのsplitを使うとURLからドメインを簡単に取り出せます。
以下サンプルコードはmodule URIからの引用です。
require 'uri'
p URI.split("http://www.ruby-lang.org/")
#=> ["http", nil, "www.ruby-lang.org", nil, nil, "/", nil, nil, nil]
ドメインだけ取り出せていますね。URI#parseを使うとオブジェクトでパース結果を取り出せて便利です。
以下サンプルコードはmodule URIからの引用です。
require 'uri'
p uri = URI.parse("http://www.ruby-lang.org/")
# => #<URI::HTTP:0x201002a6 URL:http://www.ruby-lang.org/>
p uri.scheme # => "http"
p uri.host # => "www.ruby-lang.org"
p uri.port # => 80
p uri.path # => "/"
簡易的に扱うにはこれで十分だと思います。ドメインが正しいかを厳密に検証したり、日本語ドメインに対応するには少し物足りません。
日本語ドメインとIDN - ピュニコードの扱い
IDNとはInternationalized Domain Namesの略で日本語ドメインを扱う場合に必要になります。
日本語ドメインはxn--から始まるピュニコードのドメインに置き換えられるのは知っている人も多いと思います。Punycode(ピュニコード)は国際化ドメイン名で使われる文字符号化方式でRFC3492にて定義されています。
GNU LibIDN へのバインディング - idn
このピュニコードへのエンコーディングを行うライブラリがGNU LibIDNで、このGNU LibIDNへのバインディングをするgemがidnです。
Ruby1.9以降を使っていてエラーになった方はrubyからGNU LibIDNを利用するgem「idn」をruby1.9以降で動くようにするに解決方法をまとめてありますのでご参照ください。
ピュニコードを直感的に扱う - simpleidn
simpleidnを使うとピュニコードを直感的に扱えます。以下 https://github.com/mmriis/simpleidn からサンプルコードの引用です。
require 'rubygems'
require 'simpleidn'
SimpleIDN.to_unicode("xn--mllerriis-l8a.com")
=> "møllerriis.com"
SimpleIDN.to_ascii("møllerriis.com")
=> "xn--mllerriis-l8a.com"
to_unicode <=> to_ascii でドメイン名を相互に変換出来ているのが分かると思います。
サブドメイン、TLDなどドメインを部分的に抽出
ドメインとサブドメインと分けて扱う場合や、.com/.jp/.orgとトップレベルドメインだけを取り出したい場合にはpublicsuffix-rubyを使うと幸せになれます。PublicSuffixリストを元にドメインをパースしてくれます。
以下はpublicsuffix-rubyからサンプルコードの引用です。
domain = PublicSuffix.parse("www.google.com")
# => #<PublicSuffix::Domain>
domain.tld
# => "com"
domain.sld
# => "google"
domain.trd
# => "www"
domain.domain
# => "google.com"
domain.subdomain
# => "www.google.com"
このライブラリ名にもなっているPublicSuffix一覧は以下URLをご確認下さい。
他言語からPublicSuffixを扱うライブラリについては以下URLをご確認下さい。
サブドメイン、TLDなどドメインを部分的に抽出2
PublicSuffixと類似したライブラリにdomainatrixがあります。
以下サンプルコードはdomainatrixからの引用です。
require 'rubygems'
require 'domainatrix'
url = Domainatrix.parse("http://www.pauldix.net")
url.url # => "http://www.pauldix.net" (the original url)
url.public_suffix # => "net"
url.domain # => "pauldix"
url.canonical # => "net.pauldix"
url = Domainatrix.parse("http://foo.bar.pauldix.co.uk/asdf.html?q=arg")
url.public_suffix # => "co.uk"
url.domain # => "pauldix"
url.subdomain # => "foo.bar"
url.path # => "/asdf.html?q=arg"
url.canonical # => "uk.co.pauldix.bar.foo/asdf.html?q=arg"
ソースコードを見てもらうと分かるのですが、とてもシンプルな実装になっています。ただ欠点としてPublixSuffixリスト、TLDリストですが更新されていません。そのためこのライブラリを使う場合はeffective_tld_names.datを自分で更新した方が良いです。
publicsuffix-rubyの方もPublixSuffixリストが自動更新される仕組みにはなっていないので定期的にgemを更新してあげる必要があります。
URLのパースでドメインの抽出と日本語ドメインにも対応する優れもの - Addressable
AddressableはRuby標準ライブラリURIの代替えとして利用できる上に日本語ドメインの解決も行ってくれる便利なライブラリです。
以下サンプルコードはAddressableからの引用です。
require "addressable/uri"
uri = Addressable::URI.parse("http://example.com/path/to/resource/")
uri.scheme
#=> "http"
uri.host
#=> "example.com"
uri.path
#=> "/path/to/resource/"
uri = Addressable::URI.parse("http://www.詹姆斯.com/")
uri.normalize
#=> #<Addressable::URI:0xc9a4c8 URI:http://www.xn--8ws00zhy3a.com/>
normalizeでピュニコードに変換されたURIが作られているのが確認できますね。addressableは上述したidnに依存しています。
Addressable::URIには便利な機能が他にも備わっていて、例えば以下サンプルコードでのomitを使うと特定箇所だけをキレイに取り除けます。
uri = Addressable::URI.parse("http://example.com/path#flag")
#=> #<Addressable::URI:0xcc5e7a URI:http://example.com/path#flag>
uri.omit(:fragment)
#=> #<Addressable::URI:0xcc4d86 URI:http://example.com/path>
既存のライブラリを使って楽をする
今の時代にドメインをパースするコードを新しく作り直す必要はないと思うので、この記事で紹介したようなライブラリを使うと楽が出来ます。
他言語のライブラリにあって便利な機能もゼロから作るのではなく、元コードを移植するのが楽です。
もしもドメインのパースを遅いと感じる場合
PublicSuffixリストがファイルに格納されてパースの都度毎回ファイルを読み込むライブラリだと、ドメインのパースが多い場合に処理が遅く感じると思います。
この場合PublicSuffixリストファイルのタイムスタンプを見て更新されていればロード、更新されていなければロード済みのものを使う等、ライブラリのコードをいじってキャッシュの仕組みを入れると快適になると思います。
さいごに
質問などあればコメント欄にお願いします。直接メッセージや質問をしたい方はTwitterアカウント宛てにお願いします。