Nightmareがv2(Electronベース)になり、使いやすく感動したのでLIGブログのPV/UUデータ取得を自動化してみた。


LIGアドベントカレンダー1日目です！ こんにちは@n0bisukeです。

Nightmareを久々に使ってみたら起動したタイミングでElectronが起動してびびってたらそういうことになったみたいです。
Google Analyticsにログインしてカスタムレポートの値を取得する作業を自動化してみました。

あとでもうちょい細かく書いてLIGブログでまとめてみます(予定)。

## Nightmareとは

> NightmareまたはNightmare.js http://www.nightmarejs.org/

ヘッドレスブラウザでJSの自動テストなどを行う[Phantom.js](http://phantomjs.org/)のラッパーツール・ライブラリです。
ログイン処理やボタンを押した際の挙動などを自動テストする際に使います。

yahooにアクセスして`github nightmare`で検索する処理を書いた時の比較です。左がPhantom、右がNightmareでの書き方。
![](https://i.gyazo.com/387c040f409eaf727af76cd610a32f70.png)

だいぶ簡潔に書けて、スッキリしていることがわかります。

## v2になって更に使いやすくなった

v2になって内部ブラウザで話題の[Electron](http://electron.atom.io/)を使っているみたいです。これによって自動操作している様子を **視覚的に確認することが出来るようになりました。**

![](https://i.gyazo.com/ae4a07a0c18fd511ffedf9b48fab5c2c.gif)
>こんな感じで起動するとElectronなブラウザも立ち上がります。

なおかつ、ES6な書き方に対応したので **yield/generatorで書くことができます。**
モダンな雰囲気。

※昔は、APIも揃ってなくて使う気になれませんでした

## 使ってみましょう
### 準備

サンプルコードがes6な書き方に対応したものなので、Node.jsはv5系を利用しています。

```bash
npm install nightmare
```

インストールはけっこう時間がかかります。

```app.js
var Nightmare = require('nightmare');
var vo = require('vo');

vo(function* () {
  var nightmare = Nightmare({ show: true });
  var link = yield nightmare
    .goto('http://yahoo.com')
    .type('input[title="Search"]', 'github nightmare')
    .click('.searchsubmit')
    .wait('.ac-21th')
    .evaluate(function () {
      return document.getElementsByClassName('ac-21th')[0].href;
    });
  yield nightmare.end();
  return link;
})(function (err, result) {
  if (err) return console.log(err);
  console.log(result);
});
```

こんな感じのサンプルでapp.jsを作り実行しましょう。

```bash
node app.js
```

実行すると、Yahooにアクセスして`github nightmare`で検索するというフローが自動実行されます。
その様子がElectronブラウザで見れます。

ブラウザでの視覚的チェックが入らない場合は以下のようにfalseにすればOKです。

```
  var nightmare = Nightmare({ show: true });
```

↓

```
  var nightmare = Nightmare({ show: false });
```

### Google Analyticsの情報を抜き出してみましょう。

GAの通常のPV/UUとかはAPI経由やアドオンなどで割とサクッと取れるのですが、カスタムレポートの情報取得がけっこうつらい。
ということで、Nightmareを使ってGAの一部のデータを自動抽出してみます。

![](https://i.gyazo.com/c4cc0d35185105a96af504632c32928b.png)

```ga.js
'use strict'

const Nightmare = require('nightmare');
const vo = require('vo');
const URL = 'https://accounts.google.com/ServiceLogin?service=analytics&continue=https://adwords.google.com/analytics/web/?authuser%3D0%23croverview%2Fcr-overview%2Fa44078732w74268487p76720523%2F&followup=https://adwords.google.com/analytics/web/?authuser%3D0%23croverview%2Fcr-overview%2Fa44078732w74268487p76720523%2F#identifier';
const USER_NAME = 'Googleのメールアドレス';
const PASSWD = 'パスワード';
const STARTDAY = 20151122; //2015年11月22日から
const ENDDAY = 20151128; //2015年11月28日まで
const GA_URL = 'https://adwords.google.com/analytics/web/?authuser=0#my-reports/';
const DELAY_COUNT = 4000;
const DEV_SELECTOR = '.ID-row-7-0-0 ._GAWY';
const PV_REPO_ID = 'htFZSE4TT76nGXLr4v4afw'; //カスタムレポートのIDその1
const UU_REPO_ID = 'WppUuYkJSI6WWxcKup8QSQ'; //カスタムレポートのIDその2

function getGaInfo(nightmare, repo_id){

  return nightmare
    .wait('._GAEY')
    .goto(GA_URL+repo_id+'/a44078732w74268487p76720523/%3F_u.date00%3D'+STARTDAY+'%26_u.date01%3D'+ENDDAY+'/')
    .wait(DELAY_COUNT)
    .evaluate((selector) => {
      return document.querySelector(selector).textContent;
    },DEV_SELECTOR);
}

function googleLogin(nightmare){
  return nightmare
    .goto(URL)
    .type('input#Email', USER_NAME)
    .click('input#next')
    .wait('input#Passwd')
    .type('input#Passwd', PASSWD)
    .click('input#signIn')
}

//メインのフロー
vo(function* () {
  let nightmare = Nightmare({ show: true });
  let page = {};
  yield googleLogin(nightmare); //1.Googleアカウントでログイン
  page.pv = yield getGaInfo(nightmare, PV_REPO_ID); //2.PVをゲット
  page.uu = yield getGaInfo(nightmare, UU_REPO_ID); //3.UUをゲット
  yield nightmare.end();
  return page;
})((err, result) => {
  if (err) return console.log(err);
  console.log(result);
});

```

こんな感じで取得できます。

メインのフローの部分でコメントしてある1,2,3の流れが同期的に記述できるようになっているので yield/generatorすげーなぁって感じです。

```bash
node ga.js
{ pv: '******(0.69%)', uu: '******(0.89%)' }
```

この辺りのログイン処理は何かライブラリみたいなのがあってもいいですよね。

```
function login(nightmare){
  return nightmare
    .goto(URL)
    .type('input#Email', USER_NAME)
    .click('input#next')
    .wait('input#Passwd')
    .type('input#Passwd', PASSWD)
    .click('input#signIn')
}
```

↑これでgoogleにサインインできます。(2015年11月30日現在)

ソースコードの詳細説明は別途書きたいです。

##まとめ
NightmareがElectronベースになって視覚的になり、es6な書き方に対応して同期的に書けるようになっていたのでNightmare v2すげーなぁって感じでした。

去年くらいにつかってv1系で落胆してた人も、もう一回使ってみてはどうでしょうか！

明日は店長 @Im0_3ですね。