プログラマでありたい

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

JavaScriptにも対応出来るruby製のクローラー、Masqueを試してみる

 ちょっと前に試そうと思って、そのまま放置していたruby製のクローラー「Masque」を試してみました。ruby製のクローラーは、他にはAnemoneという優秀なものがあります。その上で何故というと、Anemoneにはない特性があるからです。
 MasqueはCapybaraのDSLで記述出来るWebクローラーです。つまりCapybaraを動かす為のものなので、JavaScriptも解釈が出来るということです。また場合によっては、レスポンシブルデザインのサイトの確認も出来ます。一方で、Anemoneはあくまで個別個別のHTMLを取得する為のクローラーなので、JavaScriptを多用しているサイトでの情報取得に向きません。どちらが優れているという訳ではないので、用途に応じて使いこなせばよいでしょう。

Masqueのインストール


$ gem install masque
Fetching: headless-1.0.1.gem (100%)
Successfully installed headless-1.0.1
Fetching: mini_portile-0.5.2.gem (100%)
Successfully installed mini_portile-0.5.2
Fetching: nokogiri-1.6.1.gem (100%)
Building native extensions.  This could take a while...
Successfully installed nokogiri-1.6.1
Fetching: mime-types-2.0.gem (100%)
Successfully installed mime-types-2.0
Fetching: rack-1.5.2.gem (100%)
Successfully installed rack-1.5.2
Fetching: rack-test-0.6.2.gem (100%)
Successfully installed rack-test-0.6.2
Fetching: xpath-2.0.0.gem (100%)
Successfully installed xpath-2.0.0
Fetching: capybara-2.1.0.gem (100%)
IMPORTANT! Some of the defaults have changed in Capybara 2.1. If you're experiencing failures,
please revert to the old behaviour by setting:

    Capybara.configure do |config|
      config.match = :one
      config.exact_options = true
      config.ignore_hidden_elements = true
      config.visible_text_only = true
    end

If you're migrating from Capybara 1.x, try:

    Capybara.configure do |config|
      config.match = :prefer_exact
      config.ignore_hidden_elements = false
    end

Details here: http://www.elabs.se/blog/60-introducing-capybara-2-1

Successfully installed capybara-2.1.0
Fetching: capybara-webkit-1.0.0.gem (100%)
Building native extensions.  This could take a while...
ERROR:  Error installing masque:
	ERROR: Failed to build gem native extension.

    /Users/username/.rvm/rubies/ruby-2.0.0-p247/bin/ruby extconf.rb
Command 'qmake -spec macx-g++' not available


Gem files will remain installed in /Users/username/.rvm/gems/ruby-2.0.0-p247@masque/gems/capybara-webkit-1.0.0 for inspection.
Results logged to /Users/username/.rvm/gems/ruby-2.0.0-p247@masque/gems/capybara-webkit-1.0.0/./gem_make.out


qmakeがないとエラーが出ます。インストール方法を探してみると、Homebrewでインストール出来るようです。パッケージ名は、qmakeではなくqtです。

$ brew install qt
Warning: Building source; cellar of qt's bottle is /usr/local/Cellar
==> Downloading http://download.qt-project.org/official_releases/qt/4.8/4.8.5/qt
######################################################################## 100.0%
==> ./configure -prefix /Users/username/tools/homebrew/Cellar/qt/4.8.5 -system-zli
==> make
==> make install
==> Caveats
We agreed to the Qt opensource license for you.
If this is unacceptable you should uninstall.
==> Summary
🍺  /Users/username/tools/homebrew/Cellar/qt/4.8.5: 2778 files, 119M, built in 23.6 minutes

コマンド一発でインストール出来るのですが、割と時間が掛かります。


気を取り直して、再度gemでmasqueのインストール

$ gem install masque
Building native extensions.  This could take a while...
Successfully installed capybara-webkit-1.0.0
Fetching: http_parser.rb-0.5.3.gem (100%)
Building native extensions.  This could take a while...
Successfully installed http_parser.rb-0.5.3
Fetching: eventmachine-1.0.3.gem (100%)
Building native extensions.  This could take a while...
Successfully installed eventmachine-1.0.3
Fetching: faye-websocket-0.4.7.gem (100%)
Building native extensions.  This could take a while...
Successfully installed faye-websocket-0.4.7
Fetching: poltergeist-1.3.0.gem (100%)
Successfully installed poltergeist-1.3.0
Fetching: masque-0.3.1.gem (100%)
Successfully installed masque-0.3.1
Parsing documentation for capybara-webkit-1.0.0
Installing ri documentation for capybara-webkit-1.0.0
Parsing documentation for eventmachine-1.0.3
unable to convert "\xCF" from ASCII-8BIT to UTF-8 for lib/fastfilereaderext.bundle, skipping
unable to convert "\xCF" from ASCII-8BIT to UTF-8 for lib/rubyeventmachine.bundle, skipping
Installing ri documentation for eventmachine-1.0.3
Parsing documentation for faye-websocket-0.4.7
unable to convert "\xCF" from ASCII-8BIT to UTF-8 for lib/faye_websocket_mask.bundle, skipping
Installing ri documentation for faye-websocket-0.4.7
Parsing documentation for http_parser.rb-0.5.3
unable to convert "\xCF" from ASCII-8BIT to UTF-8 for lib/ruby_http_parser.bundle, skipping
Installing ri documentation for http_parser.rb-0.5.3
Parsing documentation for masque-0.3.1
Installing ri documentation for masque-0.3.1
Parsing documentation for poltergeist-1.3.0
Installing ri documentation for poltergeist-1.3.0
6 gems installed


今回はすんなりインストール出来ます。早速サンプルソースを走らせてみましょう。サンプルソースは以下の通りです。

require "masque"
m = Masque.new(:display => 99, :driver => :webkit) # or :driver => :poltergeist
m.run do
  # Capybara::DSL syntax
  # https://github.com/jnicklas/capybara#the-dsl

  visit "http://www.google.com/"
  fill_in("q", :with => "capybara")
  find('*[name="btnG"]').click

  titles = evaluate_script <<-JS
    (function(){
      var titles = Array.prototype.map.call(
        document.querySelectorAll('h3 a'),
        function(a) {
          return a.innerText;
        }
      );

      return titles;
    })();
  JS
  puts titles.join("\n")
end


サンプルソースを実行してみると、下記のようなエラーが出ました。Xvfbがないとのことです。Xvfbは仮想フレームバッファで、Xを起動していないマシンでもXの仮想的なXフレームバッファを使えるようにする為のものです。これを入れておけば、Xのライブラリが使えるようになります。

$ ruby masque_sample.rb 
/Users/username/.rvm/gems/ruby-2.0.0-p247@masque/gems/headless-1.0.1/lib/headless/cli_util.rb:9:in `ensure_application_exists!': Xvfb not found on your system (Headless::Exception)
	from /Users/username/.rvm/gems/ruby-2.0.0-p247@masque/gems/headless-1.0.1/lib/headless.rb:68:in `initialize'
	from /Users/username/.rvm/gems/ruby-2.0.0-p247@masque/gems/masque-0.3.1/lib/masque.rb:50:in `new'
	from /Users/username/.rvm/gems/ruby-2.0.0-p247@masque/gems/masque-0.3.1/lib/masque.rb:50:in `initialize'
	from masque_sample.rb:2:in `new'
	from masque_sample.rb:2:in `<main>'


 さてどうしたものかと考えたのですが、基本的にはXがあれば良いはずです。MacでXはどうなっているかというと、入っていないのでXQuartzを使えと公式ページに書いています。XQuartzはdmg形式で配布されてるので、ダウンロードしてインストールしましょう。インストール後に再起動して環境を反映さえます。


 再度、実行してみます。何やら結果が出ていますね。これは、masqueがGoogleでcapybaraを指定して検索ボタンを押して、その結果を取得しているのです。

$ ruby masque_sample.rb 
jnicklas/capybara &#183; GitHub
capybara の画像検索結果
Capybara - Wikipedia, the free encyclopedia
フレームワークで実践! JavaScriptテスト入門(5):Capybara ...
capybara で快適なテスト生活を - SlideShare
Capybara Games
Capybara - GitHub Pages
Capybara の README 意訳 - おもしろWEBサービス開発日記
Capybara Madness
RSpecとCapybaraでJavaScript/Ajaxをテストする - Rails 雑感 - Ruby ...
capybara | RubyGems.org | your community gem host

Masqueの構造



 最初に書いた通り、MasqueはCapybaraのDSLを記述出来るクローラーです。それを念頭にソースを見ると、以下のソースの意味が直ぐ解ります。
  visit "http://www.google.com/"
  fill_in("q", :with => "capybara")
  find('*[name="btnG"]').click


 とここまで書いて、Capybaraとの違いが気になってきます。同じ処理をCapybaraで書くと、下記のようなソースになります。

require 'capybara'
require 'capybara-webkit'

include Capybara::DSL

Capybara.default_driver = :webkit
visit "http://www.google.com/"
fill_in("q", :with => "capybara")
find('*[name="btnG"]').click

titles = evaluate_script <<-JS
  (function(){
    var titles = Array.prototype.map.call(
      document.querySelectorAll('h3 a'),
      function(a) {
        return a.innerText;
      }
    );

    return titles;
  })();
JS
puts titles.join("\n")

 DSLの宣言など、一部の違いがあるだけです。結論としては、MasqueはCapybaraのラッパーとして一部の値を上書きして使いやすくしているだけです。CapybaraのDSLを使いますよという時点で、そりゃそうだという結論です。

まとめ



 結論的には、Capybaraの構造をちゃんと知っていれば、特に必要のないライブラリです。一方でMasqueを使うことによって、余計な宣言の手間も必要なくなります。また必要なところは上書きで設定できるようになっています。まぁ使っても良いかなぁというレベルです。方向性としては、Capybaraの設定を便利にするより、クローラーとしての機能を盛り込んだ方が面白いと思います。リンクを解析して再帰的に情報を収集する機能などを、付加するのも1つだと思います。

See Also:
オープンソースのRubyのWebクローラー"Anemone"を使ってみる
複数並行可能なRubyのクローラー、「cosmicrawler」を試してみた
あらためてRuby製のクローラー、"anemone"を調べてみた


参照:
About X11 and OS X
XQuartz
uu59/masque · GitHub
jnicklas/capybara · GitHub


Rubyによるクローラー開発技法

Rubyによるクローラー開発技法

Spidering hacks―ウェブ情報ラクラク取得テクニック101選

Spidering hacks―ウェブ情報ラクラク取得テクニック101選