プログラマでありたい

おっさんになっても、プログラマでありつづけたい

Ruby製のクローラー Anemoneでストレージをファイルに変更する

 シリーズの如く何度かAnemoneの話を書いています。Anemoneは割りと小さなモジュールなので、ソースを読めば直ぐに解ることが多いです。一方で、ドキュメントが充実しているとは言い難いので、少し違うことをしようとすると、ソース嫁という状態になります。今回のエントリーもそんな話の一つです。

デフォルトストレージを変更する



 Anemoneのデフォルトストレージのデフォルトオプションは、nilです。この場合、どういう動作をするかというと、Anemone::Storage.Hashが指定されます。つまりメモリーです。当然ながらクロール量に比例してメモリの使用量が増えます。そうすると、当然動いている端末もしくはサーバのメモリーを圧迫していくという結果になります。
 当然望ましくないので、本格的に使用する場合はストレージを変更した方が良いでしょう。Anemoneはストレージのオプションが豊富で、PStore(ファイル)やMongoDB,Sqlite3などを始めとして、TokyoCabinet,KyotoCabinet,Redisなどが使えます。mongoなどについては、使ってみたよという事例がちょくちょく出ているのですが、一番単純なPStore(ファイル)についての情報は見たことがないです。そんなこんなで、ちょこっと書いてみます。

AnemoneでPStoreを利用する



 使い方は簡単です。起動時のオプションの中の:storegeで、Anemone::Storage.PStoreを指定するだけです。簡単ですね。しかし、それで起動すると、以下のようなエラーが発生します。

pstore.rb:11:in `initialize': wrong number of arguments (0 for 1) (ArgumentError)
        from C:/Ruby200/lib/ruby/gems/2.0.0/gems/anemone-0.7.2/lib/anemone/stora
ge.rb:13:in `new'
        from C:/Ruby200/lib/ruby/gems/2.0.0/gems/anemone-0.7.2/lib/anemone/stora
ge.rb:13:in `PStore'
        from anemone-pstore.rb:7:in `<main>'

 端的にいうと、引数が足りませんよということです。どこに保存するかの情報がないということでしょう。しかし、オプションでは、ファイル等の指定するところがありません。どうしたら良いのでしょうか?pstore.rbを見てみると、initializeでファイルを引数としています。

pstore.rb

      def initialize(file)
        File.delete(file) if File.exists?(file)
        @store = ::PStore.new(file)
        @keys = {}
      end

 親クラスのpage_store.rbを見ると、次のようになっています。オプション変数をそのまま渡しているっぽいですね。

    def initialize(storage = {})
      @storage = storage
    end

 呼び元のcore.rbは、どうしているかというと、次の通りです。

storage = Anemone::Storage::Base.new(@opts[:storage] || Anemone::Storage.Hash)

 ということで、次のような感じで保存先のファイルと一緒にオプションで指定したら良いです。それだけです。

storage => Anemone
:Storage.PStore('file.txt')


 下記が、それを踏まえたソースです。この場合、ファイルは実行したディレクトリに作成されます。ちなみにファイル名は.txtにしていますが、Mashal.dmp形式のバイナリになっている為、殆ど読めません。

require 'anemone'
 
urls = []
urls.push("http://www.yahoo.co.jp")

opts = {
  :storage => Anemone::Storage.PStore('file.txt'),
  :obey_robots_txt => true,
  :depth_limit => 0
}

Anemone.crawl(urls, opts) do |anemone|
  anemone.on_every_page do |page|
    puts page.url
    p page.doc.xpath("/head/title/text()").to_s if page.doc
  end
end

 一方でストレージに保存しているものの、それを再利用するかというと別の話です。PStoreのイニシャライザを見ての通り、既存のファイルがあれば消してから作成しています。このあたりを何とかしたいのであれば、ライブラリに手を加えるしかないですね。訪問済みのページは訪れないとか、一定期間経過した場合のみ訪れるとか。