casperJSでwebスクレイピング


こんにちは。

エンジニアのぽぽです。

スポーツビジネスと技術を関連づけた記事を・・・というのはちょっと難しいので、運営しているサービスの技術的な裏側でも紹介しようと思います。

現在運営しているサービスの一つにCSParkという大学スポーツ総合サイトがあり、大学スポーツのニュースやコラム、ムービー、ブログ等を掲載しているのですが、その中のニュースについて取り上げてみます。

ニュースの記事はスタッフ書く場合もあるのですが、それ以外にも各大学のスポーツ新聞会等のwebページから取ってきてまとめています。(ちゃんと許可は頂いております。)
ただ、webページの更新情報を取ってきたいと思った時に、スタッフが一定時間おきにブラウザをリロードして、もし更新されていたら更新されている記事とレイアウトが同じになるように注意しながら文章をコピペしつつ画像を正しい位置に配置してCSParkで掲載し直す・・・という事をしているといくらスタッフがいても足りません。

そこで、自動的にサーバーにwebサイトを巡回させて、自動的に更新されたニュースを取ってきてもにょもにょする・・・という事をやらせる必要があります。
ということで今回はスクレイピング(Webからの情報抽出)についてちょっと書こうと思います。

僕がスクレイピングで最近良いと思ったのはcasperJS( http://casperjs.org/ )というツールで、サーバ上でjavascriptを使ってスクレイピングが出来てしまいます。
ログインが必要なページだろうがajaxでコンテンツ内容がロードされるページだろうがちゃんと取得できます!素晴らしい。

インストール方法

$ git clone git://github.com/n1k0/casperjs.git
$ cd casperjs
$ git checkout tags/1.0.2
$ ln -sf `pwd`/bin/casperjs /usr/local/bin/casperjs

phantomjsも必要です。
ここからどうぞ。
では、yahooを例にちょっと使い方の解説を。

例えばヤフトピの一番上のタイトルを取得したいと思った場合、以下のようなコードを書けばOKです。

var casper = require('casper').create();

casper.start("http://www.yahoo.co.jp",function(){
    var text = this.evaluate(function(){
        return __utils__.getElementByXPath('//*[@id="topicsfb"]/div[1]/ul[1]/li[1]/a').innerHTML;
    });
    this.echo(text);
});
casper.run();

実行結果。

$ casperjs yahoo.js
MOX燃料到着 抗議の声も

こんな感じでXPathを使って取得できます。this.evaluate内に書かれたjsはページ上で実行されます。

では、yahooにアクセスして、検索ボックスに「cspark」と入力して検索ボタンを押し、検索結果の一番上のタイトルを取得するという作業をcasperJSにやらせみると以下のようになります。

var casper = require('casper').create();
casper.userAgent('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.116 Safari/537.36 ');
casper.start("http://www.yahoo.co.jp",function(){
    this.evaluate(function(searchTxt) {
        document.querySelector('#srchtxt').value = searchTxt;
        document.querySelector("#srchbtn").click();
    }, "cspark");
});
casper.then(function(){
    var text = this.evaluate(function(){
        return __utils__.getElementByXPath('//*[@id="WS2m"]/div[1]/div[1]/h3/a').innerHTML;
    });
    this.echo(text);
});
casper.run();

と、簡単に書けちゃいます。実行してみると・・・

$ casperjs yahoo.js
CSPark -大学スポーツ総合サイト

yahooはuserAgentによって表示が変わるので、自分のブラウザと同じuserAgentにしないとXPathが変わってしまいうまく取れないので注意。

これを応用すると、色々出来そうですね。
アマゾンでのコーラの購入を一発で行うとか・・・w
せっかくなので書いてみると、以下のようなコードになります。

var casper = require('casper').create();
casper.userAgent('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.116 Safari/537.36 ');
casper.start("http://www.amazon.co.jp/dp/B001SES0D6/",function(){ //コーラ購入URL
    this.click('#bb_atc_button');
});
casper.then(function(){
    this.click('#hlb-ptc-btn'); //レジに進むボタンのクリック
});
casper.then(function(){
    this.evaluate(function(id,pass) {
        //ログインフォーム記入
        document.querySelector('#ap_email').value = id;
        document.querySelector('#ap_password').value = pass;
        document.querySelector('#signInSubmit-input').click();
    }, "your id","your password");
});
casper.then(function(){
    this.evaluate(function() {
        document.querySelector('#buybutton input[type="image"]').click();//注文確定!!
    });
});
casper.run();

実行・・・!

$ casperjs coke.js

はい。注文完了しました。

スクリーンショット-2013-06-27-15.04.44

もうこれで今後はめちゃくちゃ忙しくて手が離せない時でも1秒あればコーラが注文できるので安心ですね。

casperJSでは引数を取得する事も可能なので、引数でコーラのケースの数を指定する仕様にするといいかもしれません・・w

ちなみに上のコードは住所やクレジットカードのデータは予め登録されている前提の、一番簡単な遷移で購入する場合です。(もちろんwebサイトがリニューアルしてid等が変わってしまえば使えなくなります。)。あと僕はamazonプライム会員なので、会員で無い人はまたちょっとコードが変わるかもしれません。
さて、実際に使うときは新聞会のニュース情報を取ってきていい感じにデータを修正した後、データベースに入れたいのですが、casperJSからデータベースにアクセスする事は出来ません。

標準出力に出てきた結果をパイプで渡すっていう作戦もありますが、ちょっと無理矢理感があるのであまりやりたくないですね。

そこでspookyJS( https://github.com/WaterfallEngineering/SpookyJS )というものを使います。

spookyJSを使うと、Node.jsからcasperJSを使う事ができるようになります。
ちなみにこれはリファレンスは皆無に等しいです。w

spookyJSを使う為にはphantomjsのnodeモジュールが必要なので一緒にインストールします。

$ npm install phantomjs
$ npm install spooky

リファレンスは全く無いですが、コード的にはcasperJSとほぼ同じです。
先ほどのyahooの検索結果を取ってくるスクリプトをspookyJSを使って書き直すとこうなります。

var Spooky = require('spooky');

var spooky = new Spooky({
    casper: {
        logLevel: 'debug',
        verbose: true
    }
}, function (err) {
    spooky.userAgent('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.116 Safari/537.36 ');
    spooky.start("http://www.yahoo.co.jp",function(){
        this.evaluate(function(searchTxt) {
            document.querySelector('#srchtxt').value = searchTxt;
            document.querySelector("#srchbtn").click();
        }, "cspark");
    });
    spooky.then(function(){
        var text = this.evaluate(function(){
            return __utils__.getElementByXPath('//*[@id="WS2m"]/div[1]/div[1]/h3/a').innerHTML;
        });
        this.emit('complete',text);
    });
    spooky.once('complete', (function (text) {
        console.log(text);
    }).bind(this));
    spooky.run();
});

ほぼ同じですね。これをNode.jsで実行するだけです。
あとはtextとして渡されたものをデータベースにぶち込めばいいと思います。

ではでは。


The following two tabs change content below.
ぽぽ

ぽぽ

エンジニアです。 主に使ってるのはphp, js, java, objective-c, python とかです。