Nightmare.JS を触ってみた
最近多くのクラウドサービスを利用するようになりました。
- Slack
- AWS
- などなど
多くのクラウドサービスは API を公開しており、少しプログラムを書いたり Zapier や Integromat など使えばある程度が自動化できます。便利ですね。
ですが、中には API を公開していないクラウドサービスもあります。API はあるものも契約等をしなければ使えない API もあります。でもそんなクラウドサービスも自動化したい!と考えのあなた。できます。できます。
みんな大好き Selenium の出番です。これで仮想的にブラウザをポチポチしてもらえばいいんです!でも Selenium はコーディングが大変だし、記録も面倒。もっといい感じにコーディングできるのはないのか。ということで見つかりました。
Nightmare.js – A high-level browser automation library.
Phantom.js を聞いたことがあるかもしれないですが、これをもっと簡単に使えるようにしてくれたライブラリです。細かいことはドキュメントを読んでもらって、実際のコードを見てみましょう!
const Nightmare = require('nightmare');
const main = async () => {
const today = new Date();
const year = today.getFullYear();
const month = today.getMonth(); // 1月 なら 0
const date = today.getDate();
const nightmare = Nightmare({
show: true, // ここを false にすると Window が新たに開きません
});
await nightmare.goto('https://www.ana.co.jp/');
await nightmare.viewport(1024, 768);
await nightmare.click('.select-dep'); // 出発地の空港クリック
await nightmare.wait(500);
await nightmare.wait('#depApo_ticket_modal');
await nightmare.click('#depApo_ticket_modal .modal-list-cat li[data-val="3"]'); // 関東・甲信越
await nightmare.wait(500);
await nightmare.click('#depApo_ticket_modal .reslut-box li[data-val="HND"]'); // 羽田
await nightmare.wait(500);
await nightmare.wait('#arrApo_ticket_modal');
await nightmare.click('#arrApo_ticket_modal .modal-list-cat li[data-val="4"]'); // 東海・北陸
await nightmare.wait(500);
await nightmare.click('#arrApo_ticket_modal .reslut-box li[data-val="KMQ"]'); // 小松
await nightmare.wait(500);
await nightmare.wait('#calid1');
await nightmare.click('.calendar-nav .next a'); // 翌月
await nightmare.click(`#calid1 td[onclick="cal1.chgSet(${year}, ${month + 3}, ${date})"] a`); // 3ヶ月後の今日
await nightmare.click(`#calid1 td[onclick="cal1.chgSet(${year}, ${month + 3}, ${date + 2})"] a`); // 3ヶ月 + 2日後の今日
await nightmare.click('#module-dom .btn-search'); // 検索
await nightmare.wait('#availabilityResultRecommendFare');
await nightmare.screenshot(`./${year}-${month + 4}-${date}__${year}-${month + 4}-${date}__HND__KMQ.png`);
const table = await nightmare.evaluate(() => {
// ここのコードは実際にブラウザで実行されるコードです
const innerTextTrim = element => element.innerText.trim().replace(/\r?\n/g, '');
const table = [[]];
const headerThList = document.querySelectorAll('#availabilityResultRecommendFare thead tr th');
for (let i = 0; i < headerThList.length; i += 1) {
if (headerThList[i].getAttribute('class') === 'availabilityResultTime') {
table[0][i] = '結果';
} else if (headerThList[i].getAttribute('class') === 'availabilityPremiumLabel') {
table[0][i] = 'プレミアムクラス';
} else {
table[0][i] = innerTextTrim(headerThList[i]);
}
}
const trList = document.querySelectorAll('#availabilityResultRecommendFare tbody tr');
for (let j = 0; j < trList.length; j += 1) {
const tr = trList[j];
const flightTime = innerTextTrim(tr.querySelector('th p.availabilityResultFlightTime'));
const flightDetailSpanList = tr.querySelectorAll('th p.availabilityResultFlightDetail span');
const body = [
flightTime + ' ' + innerTextTrim(flightDetailSpanList[0]) + ' ' + innerTextTrim(flightDetailSpanList[1]),
];
const bodyTdList = tr.querySelectorAll('td');
for (let k = 0; k < bodyTdList.length; k += 1) {
const fee = innerTextTrim(bodyTdList[k]);
const yenIndex = fee.indexOf('円');
body.push(yenIndex !== -1 ? fee.substring(0, yenIndex + 1) : fee);
}
table.push(body);
}
return table;
});
table.forEach(row => console.log(row.join('\t')));
await nightmare.end();
};
main();
今回のコードは ANA サイトから国内線羽田 – 小松間の実行日の3ヶ月後の今日から3ヶ月 + 2日後の今日の行きの航空券を探すところを自動化してみました。
実行結果はこちら
Tab を区切り文字にしているのでこのままコピーして Excel に貼り付ければいい感じに見れるようになります。
こんな感じで、ブラウザの動作も自動化ができるぞ!
えっ?帰りの航空券の値段がわからないって?それはみなさんへの宿題です!わからないことがあれば質問箱か問い合わせに書いてあるメールアドレスまでお願いします