PhantomJS といえば、WebKit を headless でうごかせて便利なやつですが、PhantomJS 1.8 から Ghost Driver がくみこまれるようになりました。
わかる人むけにかくと「JSONWire Protocol をサポートする httpd が phantomjs にくみこまれた」ということです。
GhostDriver は WebDriver Wire Protocol の1実装です。で、そのクライアントライブラリとして Selenium::Remote::Driver が CPAN にあがっていますから、これをつかって簡単に phantomjs とやりとりができます。
Selenium::Remote::Driver という名前のとおり、Selenium もつかえますんで、selenium をつかって、対応しているブラウザでのテストもできるのがいいかんじです。
というわけでためしてみましょう。
brew install phantomjs
cpanm Selenium::Remote::Driver
でOK。
自分でコンパイルするのはすごい時間かかるので、バイナリを利用するのが吉です。
参考:
phantomjs、P4のcentosで2時間近くがんばってbuildしている横でmacにbrew installしたらバイナリが3秒で入ったでござる — hidek (@hidek) January 9, 2013
phantomjs ghost driver をたちあげるには、以下のようにするかんじです。
phantomjs --webdriver=9999
ここで 9999 はポート番号です。
ここではシェルからたちあげることを想定していますが、実際は Test::TCP とかでたちあげちゃう方が楽だとおもいます。
use Selenium::Remote::Driver;
my $driver = Selenium::Remote::Driver->new(
remote_server_addr => '127.0.0.1',
port => 9999,
);
my $res = $driver->get('http://mixi.jp');
say $driver->get_title();
こんなかんじで OK です。簡単ですね。
簡単なんだけど、問題があります。なんか文字化けするんです。
????????롦?ͥåȥ????? ?????ӥ? [mixi(?ߥ?????)]
とか。。
なんでかな、っておもいます。
ちょっと Selenium::Remote::Driver があやしいんじゃないかとうたがってしまいますね。
なんかあやしいので、シンプルな実装をつくってためしてみましょう。
#!/usr/bin/env perl
use strict;
use warnings;
use utf8;
use 5.010000;
use autodie;
use LWP::UserAgent;
use JSON::XS;
{
package JSONWire::Client;
use LWP::UserAgent;
our $VERSION = '1.0.0';
use Moo;
use JSON;
has port => (
is => 'ro',
required => 1,
);
has json => (
is => 'ro',
default => sub {
JSON->new
},
);
has host => (
is => 'ro',
required => 1,
);
has agent => (
is => 'ro',
default => sub {
LWP::UserAgent->new(
agent => __PACKAGE__ . "/" . $VERSION,
);
},
);
sub create_session {
my $self = shift;
my $res = $self->agent->post(
"http://$self->{host}:$self->{port}/session",
Content => $self->json->encode( { desiredCapabilities => {} } )
);
if ($res->code ne 303) {
JSONWire::Client::Exception::HTTP->throw($res);
}
my $base = $res->header('Location') // die "Missing location";
return JSONWire::Client::Session->new(
base => $base,
agent => $self->agent,
json => $self->json,
);
}
package JSONWire::Client::Session;
use Moo;
has base => (
is => 'ro',
required => 1,
);
has agent => (
is => 'ro',
required => 1,
);
has json => (
is => 'ro',
required => 1,
);
has last_response => (
is => 'rw',
);
sub get {
my ($self, $path) = @_;
$path =~ s!^/+!!;
my $res = $self->agent->get($self->base . "/" . $path);
$self->last_response($res);
unless ($res->is_success) {
JSONWire::Client::Exception::HTTP->throw(
$res
)
}
return $self->json->decode($res->content);
}
sub post {
my ($self, $path, $data) = @_;
$path =~ s!^/+!!;
my $res = $self->agent->post($self->base . "/" . $path, Content => $self->json->encode($data));
$self->last_response($res);
unless ($res->is_success) {
JSONWire::Client::Exception::HTTP->throw(
$res
)
}
return $self->json->decode($res->content);
}
package JSONWire::Client::Exception::HTTP;
use Moo;
has response => (
is => 'ro',
);
use overload q{""} => \&stringify;
sub throw {
my ($class, $response) = @_;
die $class->new(response => $response);
}
sub stringify {
my $self = shift;
$self->response->status_line;
}
}
my $driver = JSONWire::Client->new(
host => '127.0.0.1',
port => 9999,
);
my $session = $driver->create_session;
$session->post('/url', {url => 'http://mixi.jp'});
my $data = $session->get('/title');
say $data->{value};
で、実行結果は。。
¥½¡¼¥·¥ã¥ë¡¦¥Í¥Ã¥È¥ï¡¼¥¥ó¥° ¥µ¡¼¥Ó¥¹ [mixi(¥ß¥¯¥·¥£)]
なんてこった!!
+NOTE: Currently, I don't use Perl for my day job & support for this module is falling behind. If you want to take over
2
+ maintenance of this module, please contact me.
とかかいてある!
https://github.com/aivaturi/Selenium-Remote-Driver/commit/82ac94841a7fe1a8d8c0ff59ab5b061c47d20eda
だれかメンテナンスしないのかな?
https://github.com/tokuhirom/JSONWire-Client
需要ありそうなら CPAN にあげます。
Selenium::Remote::Driver は日本語のscrapingができない。
あと、Wight 的なのよりこの路線が今後主流かなーとおもったり。
ref. http://blog.64p.org/entry/2012/10/13/144648
あと、phantomjs 1.8 では Ghost Driver では日本語がとおりません! だれか bug report とかしてあげてください。
【追記】
どうも OSX 用のバイナリが腐ってるっぽくて、ひできさん方式で、自前コンパイルすれば問題ないので、みなさん3時間かけてコンパイルしましょう。