WWW::MechanizeとSeleniumIDEで勝手APIをかんたんに作る

Selenium IDE で生成したテストケースに従ってWWW::Mechanizeを動かすためのラッパーモジュールMechanizedSelenium(仮称)を作ってみて、どれくらい使えるか試してみました。

背景

スクレイピングはもっと簡単にならなければいけないの最後でちょこっと触れていた、ウェブアプリケーションのテストケースを簡単に生成する Selenium IDE は、ブラウザでクリックしていくだけでデータを抜き出せてしまう Dapper: The Data Mapper と並んで考えさせられたツールです。

一度、ウェブアプリケーションのテストをするために HTTP::WebTest を使ってテストケースを書いたことがありました。しかし、ブラウザをマウスでちょこっとつつくだけの動作をperlで書き直すのは本当に退屈な作業ですし、テストケースを書くこと自体が困難で、アプリケーションにバグはないのにテストケースにバグがあるせいでテストがこける、ということもしばしばでした。

そんなところにやってきた Selenium IDE は、ブラウザでテストしたい機能をマウスでつついていくだけでテストケースが作成され、テストケースを作る作業を100倍くらい効率的にしてくれた画期的なツールです。出会ってしばらくはテストケースを動かすのが楽しくてテストケースばかり作っていました。

もうひとつ、蛇足ですが Yahoo! Pipes も、やりたいことはアタマの中ではっきりとわかっていて、作るのは難しくないはずなのになんかめんどくさいフィードの加工をかんたんにできてしまうところに、同じ種類の感動を覚えました。

これらのみっつのツールは、立体をどの平面で切り取るかによってかたちが変わるのを連想させます。切り取る平面によって、切り取られてできた二つの立体の接合面は大きくなったり、小さくなったり、複雑なかたちになったり、シンプルなかたちになったりします。同じように、なにか作りたいものを、どういう視点で切り取るかによって、かんたんか、難しいか、めんどくさいか、すぐにできるか、が変わってくる、のだと意識するようになりました。

すごく手間がかかっていたことが、かんたんにできるようになることほど楽しいことはありません。

きっかけ

最近リリースされた Yahoo!ブックマーク がハイテクで、ブックマークしたものがキャッシュされて残るのです。いままでずっとブックマークしたけど後で見たらなくなってた、というのを恐れていたので、HTML全体がキャッシュを知ってから(CSSや画像はキャッシュされないのでもとのページがなくなるとレイアウトが崩れたりします)、自分のbule dot のアカウントをミラーしてページがなくなっても大丈夫なようにしようと画策しました。

そのとき Perlモジュール/WWW::Mechanize - Walrus, Digit. を見て、モジュールのメソッドがjavascriptに近い気がして、そして"かんたんなことをかんたんに"ルールを思い出しました。javascriptのコードに似ているということは Selenium IDE の吐き出すコードでWWW::Mechanizeをドライブできるはず。

というわけで Perlモジュール/WWW::Mechanize - Walrus, Digit. に載っている mixiへの日記の投稿 をためしにやってみました。

Selenium IDE でログをとる

まず Firefox extension の Selenium IDE を使って日記投稿操作のログをとります。 今回は、トップページでログインした後、上部にある画像の日記を押して、日記を書くボタンを押して、日記を書いて確認画面を押して投稿確認画面ではいを押して日記を投稿しました。 デフォルトではSelenium用のHTMLコードが出力されますが、今回はperlのコードとして出力させます。perlのコードとして出力させるには、操作を記録した後、Selenium IDE のメニューのOptionsFormatで Perl - Selenium RCを選択します。
As Perl
そうすると、以下のようなコードが表示されます。(メールアドレスとパスワードの部分は本物から変更してあります)
use strict;
use warnings;
use Time::HiRes qw(sleep);
use Test::WWW::Selenium;
use Test::More "no_plan";
use Test::Exception;

my $sel = Test::WWW::Selenium->new( host => "localhost", 
                                    port => 4444, 
                                    browser => "*firefox", 
                                    browser_url => "http://localhost:4444" );

$sel->open_ok("/");
$sel->type_ok("email", "myaddress\@example.com");
$sel->type_ok("password", "password");
$sel->click_ok("sticky");
$sel->click_ok("//input[\@type='image']");
$sel->wait_for_page_to_load_ok("30000");
$sel->click_ok("menu3");
$sel->wait_for_page_to_load_ok("30000");
$sel->click_ok("//input[\@value=' 日 記 を 書 く ']");
$sel->type_ok("diary_title", "MechanizedSelenium初日記");
$sel->type_ok("diary_body", "SeleniumIDEとWWW::Mechanizeを使って、perlから簡単に日記を投稿するテストです。");
$sel->click_ok("//input[\@value=' 確認画面 ']");
$sel->wait_for_page_to_load_ok("30000");
$sel->click_ok("//input[\@value=' は い ']");
$sel->wait_for_page_to_load_ok("30000");

このコードはSeleniumをリモートのマシンから動かしてテストする Test::WWW::Selenium モジュールのためのコードになっているので、これを解釈するためのラッパーを用意する必要があります。

MechanizedSelenium.pm

Selenium IDE が生成したコードを実行できるようにするためにWWW::Mechanizeを継承してMechanizedSelenium.pmを作りました。中で Test::WWW::Selenium のメソッドをハンドルして WWW::Mechanize を動かします。SeleniumがHTMLのエレメントを指定する書式がXPathと同じだったり違ったりするのをHTML::TreeBuilder::XPath などを使って吸収したり、文字コードの変換をしたり、METAタグのRefreshをサポートしたりしています。

実行

MechanizedSeleniumを使ってmixiに日記を投稿してみましょう。 自分でMechanizedSeleniumオブジェクトを生成するコードを書いたら、あとははじめに Selenium IDE で生成したスクリプトから、操作を表している部分を切り取って貼付けます。

ひとつだけ注意点があります。スクリプトの文字コードはUTF-8で書いて、でも use utf8 はつけないでください!(このへんいまだによくわかっていないため、つけると動かなくなります....)

できあがったコードはこんなかんじになります。

#!/usr/bin/perl
use strict;
use warnings;
use MechanizedSelenium;

my $sel = new MechanizedSelenium();
$sel->baseurl("http://mixi.jp/");
$sel->open_ok("/");
$sel->type_ok("email", "myaddress\@example.com");
$sel->type_ok("password", "password");
$sel->click_ok("sticky");
$sel->click_ok("//input[\@type='image']");
$sel->wait_for_page_to_load_ok("30000");
$sel->click_ok("menu3");
$sel->wait_for_page_to_load_ok("30000");
$sel->click_ok("//input[\@value=' 日 記 を 書 く ']");
$sel->type_ok("diary_title", "MechanizedSelenium初日記");
$sel->type_ok("diary_body", "SeleniumIDEとWWW::Mechanizeを使って、perlから簡単に日記を投稿するテストです。");
$sel->click_ok("//input[\@value=' 確認画面 ']");
$sel->wait_for_page_to_load_ok("30000");
$sel->click_ok("//input[\@value=' は い ']");
$sel->wait_for_page_to_load_ok("30000");

これを実行すると、ブラウザでの操作を Selenium IDE が記録した通りの動作をエミュレートして日記が投稿されます。

Mixi Diary

まとめ

"かんたんなことをかんたんに"というのを念頭に、ブラウザの操作をそのままWWW::Mechanizeで再現させるためのラッパーを作ってみました。Selenium IDE とWWW::MechanizeをMechanizedSeleniumで組み合わせることで、視覚的に、かんたんにAPIもどきを作ることができます。

Selenium IDE の生成するエレメントの選択ルールは単純なのでうまくエレメントが選択できなかったり、ターゲットとしているサイトのHTMLが壊れているためHTML::TreeBuilder::XPathでうまくマッチできなかったり(今回TABLEタグの直下にFORMタグが挟まっていて、そのあとにTRが書かれていたためにうまく動かないところがありました)、最近のjavascriptでばりばり動くサイトには無力だったりしますが、サービスによっては十分有効なアプローチになるはずです。

現状ではとりあえず日記が投稿できるようにしたレベルの実装ですが、ほかでも使えるようにしていこうと思います。

関係ないですが、そんな画期的な Selenium IDE を作られた方が第6回オープンソーステクノロジー勉強会でお話されていたのをさっき知りました。作った動機とかお聞きしたかったです....


*

ちょこっと調べたら perl.com: Web Testing with HTTP::Recorder なるものが既にありました。こちらはHTTPのProxyとして動作して、ブラウザから送られてきたリクエストを記録することでWWW::Mechanize用のコードを生成するものです。最終的に送られるデータを解析してコードを生成するアプローチの方が実装がシンプルになるのでHTTP::Recorderのほうが断然よいです....


tags

  • Firefox
  • perl
  • tool
  • 「WWW::MechanizeとSeleniumIDEで勝手APIをかんたんに作る」のはてなブックマーク数
  • 「WWW::MechanizeとSeleniumIDEで勝手APIをかんたんに作る」deliciousブックマーク数
  • 「WWW::MechanizeとSeleniumIDEで勝手APIをかんたんに作る」をはてなブックマークに追加
  • save "WWW::MechanizeとSeleniumIDEで勝手APIをかんたんに作る" to del.icio.us
  • 「WWW::MechanizeとSeleniumIDEで勝手APIをかんたんに作る」をリアルタイムブログ検索
  • permalink
  • atimeの記録を省略してディスクのパフォーマンスを改善する
  • php.netのあいまい検索を使ってJavaScriptのリファレンスを検索する

comments

TypeKey Enabled
スタイル用のHTMLタグが使えます。
*required

trackbacks

トラックバック元エントリにこのエントリへのリンクがない場合はトラックバックを受け付けません。

http://labs.gmo.jp/mt/mt-tb.cgi/121
©2010 Kentaro Kumagai, GMO Internet Labs., GMO Internet, inc.
bits and bytes
2007 .04. 18 22:24

tagcloud

  • API1
  • C/C++2
  • E4X1
  • FUSE2
  • Firefox18
  • HTML4
  • IE1
  • MySQL1
  • OSX4
  • Opera2
  • PHP4
  • XML1
  • XPCOM4
  • XPath3
  • apache2
  • binary2
  • book1
  • data11
  • debug4
  • design1
  • experiments3
  • extension10
  • google gears1
  • google maps API1
  • greasemonkey3
  • httpd5
  • javascript17
  • linux1
  • logging2
  • mobile3
  • perl4
  • tips4
  • tool11
  • vim2
  • visualization2
  • widget1
  • wii1
  • windows7
  • サービス6
  • 和訳1

Archives

  • 2008.02 (6)
  • 2008.01 (3)
  • 2007.12 (4)
  • 2007.11 (5)
  • 2007.10 (4)
  • 2007.09 (4)
  • 2007.08 (4)
  • 2007.07 (8)
  • 2007.06 (7)
  • 2007.05 (4)
  • 2007.04 (5)
  • 2007.03 (6)
  • 2007.02 (4)
  • 2007.01 (6)

about

  • bits and bytesのXML
  • 「bits and bytes」のCreative Commons
  • Powered by Movable Type
  • イベントと地図 - モグ
  • Use ecto to blog!
  • bits and bytesのはてなブックマーク数
  • bits and bytesをMy Yahoo!に追加
  • Subscribe with Bloglines