Edited at
Node.js 2Day 25

HerokuにPuppeteerの実行環境を構築する

More than 1 year has passed since last update.

HerokuにPuppeteerの実行環境を構築する


はじめに

本文は Node.js 2 Advent Calendar 2017 25日目の投稿です。

またクリスマス直前で寂しく記事を書いていますが、皆さんの役に立つ情報であれば嬉しいです。


主要なキーワード


Puppeteer

PuppeteerはGoogleが開発したChromeのヘッドレス版のNode.js APIです。

ややこしいと思いますが、Phantom.jsのChrome版と思っていただくとわかりやすいです。


Heroku

Herokuはsalesforce.com傘下のPaaS提供者であり、条件付きで無料のDocker Runtimeも提供しています。


Herokuの上にPuppeteer実行環境を展開

Herokuは公式でもNode.js実行環境の構築方法(英語)を書いていますが、その環境でPuppeteerをインストールすると、様々な不具合(例えば日本語フォントが消えるとか)が生じるという報告がありました。

嬉しいことは、この問題はすでに有志により解決はしました。

それで早速、試してみましょう。

まず、こちらのページを参考して、Herokuのアカウントにログインしましょう。

そして、アプリ用レポジトリを作りましょう。

$ mkdir MYAPP # ここは自分のappの名前です

$ cd MYAPP
$ git init
$ npm init -y

# Herokuプロジェクトの作成
$ heroku create MYAPP
$ git push heroku master

# メインビルドパックをNode.jsに設定
$ heroku buildpacks:set heroku/nodejs

# Puppeteer用追加パケージのインストール
$ heroku buildpacks:add https://github.com/CoffeeAndCode/puppeteer-heroku-buildpack

注意点は、puppeteer-heroku-buildpack系のビルドパックはheroku/nodejsという土台が必要で、直接は使えません。


Puppeteerのインストール

$ npm i puppeteer

上記コマンド叩くと、node_moduleに最新のChromeがインストールされますので、少々時間かかります。


ローカルでPuppteerを動かしてみる

Googleさんのデモコードを少し変更してみました。


puppteer_demo.js

const puppeteer = require('puppeteer');

(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://news.ycombinator.com', { waitUntil: 'networkidle2' });
const newsTitles = await page.evaluate(() => {
const elenemts = document.querySelectorAll('.itemlist .title > a');
return [].map.call(elenemts, el => el.innerText);
});
console.log(newsTitles.slice(0, 5));
await browser.close();
})();


実行してみると、https://news.ycombinator.com から、トップ5の記事のタイトルが戻ってきます。

$ node puppteer_demo.js

[ 'The Effect of Atmospheric Nuclear Testing on American Mortality Patterns [pdf]',
'How to Print Integers Really Fast',
'Papercraft with Blender',
'Karnaugh map',
'A preview of the U.S. without pensions' ]

ご覧のように、Phatom.jsと比べるとモダーンな構文で非同期処理が書けます。


PuppeteerをHerokuにデプロイ


Koa.jsでHeroku用入り口の追加

別にKoaにこだわることはありませんが、async/awaitスタイルで統一したいのでこちらを使わせていただきます。

$ npm i koa koa-router koa-bodyparser

index.jsを用意し、下記のようにロジックを追加します。


index.js

const Koa = require('koa');

const Router = require('koa-router');
const bodyParser = require('koa-bodyparser');

const app = new Koa();
const router = new Router();

router.get('/', async (ctx, next) => {
ctx.body = 'Hello World';
});

app.use(router.routes());
app.use(router.allowedMethods());
app.use(bodyParser());
app.listen(process.env.PORT || 3000);


下記コマンドを実行すると、localhost:3000Hello Worldが表示されます。

$ node index.js


Puppeteer Launch Optionの修正

HerokuでPuppeteerを実行するには、少し工夫が必要です。

まず、真・ヘッドレス環境で実行する時、デフォルトのLaunch Optionだとエラーが発生します。

また、ローカルで動かす時、ヘッドレスよりも画面表示した方がデバグしやすいので、puppteer_demo.jsの一部修正して、index.jsに追記します。


index.js

const Koa = require('koa');

const Router = require('koa-router');
const bodyParser = require('koa-bodyparser');

const app = new Koa();
const router = new Router();

router.get('/', async (ctx, next) => {
ctx.body = await crawler(); // クローラーの実行
});

app.use(router.routes());
app.use(router.allowedMethods());
app.use(bodyParser());
app.listen(process.env.PORT || 3000);

// ここからはクローラーのロジック
const puppeteer = require('puppeteer');
// Heroku環境かどうかの判断
const LAUNCH_OPTION = process.env.DYNO ? { args: ['--no-sandbox', '--disable-setuid-sandbox'] } : { headless: false };

const crawler = async () => {
const browser = await puppeteer.launch(LAUNCH_OPTION); // Launch Optionの追加
const page = await browser.newPage();
await page.goto('https://news.ycombinator.com', { waitUntil: 'networkidle2' });
const newsTitles = await page.evaluate(() => {
const elenemts = document.querySelectorAll('.itemlist .title > a');
return [].map.call(elenemts, el => el.innerText);
});
await browser.close();
return newsTitles;
}


修正したindex.jsを実行すると、localhost:3000でタイトル一覧が(json形式で)表示されます。


Herokuにプッシュ

Herokuの環境はProcfileというファイルで本番環境で実行するコマンド記載する必要があるので、MYAPP直下で作ります。

web: node index.js

そしてHerokuのプロジェクトは結局Gitのレポジトリなので、gitでプッシュすれば更新されます。

$ git add .

$ git commit -m 'init crawler'
$ git push heroku master

Herokuにプッシュするたびに、実行環境が作り直されるので、完成まで待ちます。

環境の作成が完成できたら、下記コマンドを叩くとブラウザーが開き、https://MYAPP.herokuapp.com/でタイトル一覧が(json形式で)表示されます。

$ heroku open

もし何か異常が出されたら、heroku logs --tailでログが確認できます。


参考

https://devcenter.heroku.com/

https://elements.heroku.com/buildpacks/jontewks/puppeteer-heroku-buildpack

https://timleland.com/headless-chrome-on-heroku/