Node.jsでWEBすくれいぴんぐ!(Hello World編)

Node.jsでWEBすくれいぴんぐ!(Hello World編)

こんにちは。初ブログ投稿のワタナベです。 最近は春なのにまだ寒い時期が続いていますね。 梅は咲きましたが、桜はまだかな・・・満開予想は今月末だけど咲くかな。

・・・

さてさて、花が咲こうが嵐が来ようが、我々はシステムエンジニア。 時期を選ばず、ディスプレイに向かってごりごりコーディングせねばなりませんね。 これを見ているあなたもきっと春夏秋冬ごりごりとしていることでしょう。

今回は会社の同期とツールを作る際にNode.jsでやってみるか、となったので Node.jsでのWEBスクレイピングのことについてぽつりぽつりと書いてみます(´ω`)

この記事の目標としては「Googleのトップページのタイトルタグからタイトルを取得して表示すること」とします。

This is 前提

  • OSはCentOS 6.4
  • Node.jsはバージョン4.4.1(LTS)

今回はNode.jsということで、HTMLパーサーのcheerioではなく、WEBページ上のJavaScriptも動かせるということを念頭に置き、Selenium + WebdriverIOで作成します。

また、サーバーには必要最低限のものしかインストールしたくありません。 GUI環境などは必要ならば入れますが、いれなくても済むならばそれがクリーンで良い気がします。というか、個人的にそっちのほうが好き 実際に、探してみるとLinuxにはCLIでもGUIのツール(firefoxなど)を仮想的に動かしてくれるXvfbという素敵なツールがあったのでそれを利用することとします。先人はすごいですね、なんでも作りますね。

INSTALL

まずはサーバーに必要なツールを入れていきます。

$ sudo yum -y groupinstall "Japanese Support"    # 日本語フォントが文字化けしないように
$ sudo yum -y install xorg-x11-server-Xvfb       # Xvfbをインストール
$ sudo yum -y install firefox                    # firefoxをインストール
$ sudo yum -y install java-1.8.0-openjdk         # Seleniumサーバーを動かすために必要

Firefoxは2016年3月25日時点ではバージョン38.7.0がインストールされました。

次はNode.js側の準備

$ mkdir ~/scraping
$ cd !$ && npm init    # プロジェクトルートにpackage.jsonを作成する

# PhantomJS(Seleniumサーバー) + WebdriverIO
$ npm install --save selenium-standalone
$ ./node_modules/selenium-standalone/bin/selenium-standalone install
$ npm install --save webdriverio

package.jsonを編集して以下の内容を取り込みます。

"scripts": {
    "start": "node index.js"
}

index.js

'use strict';

const webdriverio = require('webdriverio');

const options = {
  desiredCapabilities: {
    browserName: 'firefox'
  },
  logLevel: 'verbose'
};

const client = webdriverio.remote(options);

client
  .init()
  .url('http://www.google.com')
  .title().then(function (title) {
    console.log(`Title was: ${title.value}`);
  })
  .end();

実行☆(カチッカチッ、ターンッ!)

$ sudo Xvfb :10 &
$ export DISPLAY=localhost:10   # export DISPLAY=:10でも可
$ ./node_modules/selenium-standalone/bin/selenium-standalone start &
$ npm start

firefox起動による大量のログメッセージとWebdriverIOが吐き出すログメッセージが混ざっていますが、 無事「Title was: Google」のメッセージが出力されました。

余談

Node.js経由でスクレイピングに至るまで紆余曲折ありました。 モジュールをどれを使っていけばいいかわからず、とりあえず聞いたことのあるPhantomJSから入ったのが間違いだったかもしれません。

WebdriverIOモジュール発見に至るまで

  1. PhantomJS
    • Qt/Webkitで作られたツール。
    • GUI環境なしのウェブブラウザ。いわゆるヘッドレスブラウザ
  2. CasperJS
    • PhantomJSを使いやすくしたもの
    • CasperJSという環境で動くものであって、Node.jsのライブラリではない
    • CasperJSのFAQ 「Node.jsから使いたかったらSpookyJS使えば?」 はい・・・
  3. SpookyJS

    • CasperjsをNode.jsから直接使えるようにしたもの
    • SpookyJSさん「PhantomJSがWebDriverをネイティブでサポートしたからそっちを使ったほうがいいんじゃない? ( issue )」 はい・・・(小声)
  4. Selenium

    • ブラウザを各プログラミング言語で動かすことができるツール
    • バージョン2.0からは直接ブラウザのAPIを叩いているらしい。すごい
    • 脳内のSelenium氏「なんで最初にここに来なかったの?」 すみません(涙)
  5. WebdriverIO ← イマココ
    • WebdriverIOさん「Selenium WebDriver使いやすくしたよ!」 お、おう・・・(虫の息)

Railsでは関連するgemなどはメンテナンスされて使われ続けているものが多い印象がありましたが、Node.jsは取っ替え引っ替えですね。やはり、ES6という新しい言語仕様がでてきてどんどん進化してきているというがいうのが大きいのでしょうか。古い情報を区別して新しい情報を仕入れていかないと途端に立ち行かなくなってしまいそうです。

Node.jsによくある非同期I/Oの書き方もまだまだ慣れるのに時間がかかりそうです。欲しかったデータがコールバック関数から取り出せずどんどんネストしていって・・・噂のコールバック地獄も体験できました。JavaScriptではES6 Promisesなどで非同期I/Oの書き方などがどんどん統一されてきてはいますが、Promiseを使っていないモジュールも数多くあります。

ES.stage3のasync/awaitが実装されたらもっと書きやすくなりそうですね。

Babelなども触ってみたので次回の記事にしてみたいなぁ。。。

では、今回はこのへんでお暇させていただきます。 それでは、良きJSライフを。

敬具