LoginSignup
22
19

More than 5 years have passed since last update.

AWS Lambda上でPhantomJSを使う

Last updated at Posted at 2016-12-03

PhantomJS Lambda Templateの開発者が phantomjs-lambda-pack を新しくリリースしていたので、これを使ってLambda上でPhantomJSを動作させてみます。

環境構築と初期設定

$ mkdir lambda-phantomjs && cd  lambda-phantomjs
$ npm init
$ npm install phantomjs-lambda-pack --save

index.jsはREADME.md のusageに書かれている内容でまずは試します。

% cat index.js 
'use strict';
const phantomjsLambdaPack = require('phantomjs-lambda-pack');
const exec = phantomjsLambdaPack.exec;

exports.handler = (event, context, callback) => {
    exec('-v', (error, stdout, stderr) => {
        if (error) {
            console.error(`exec error: ${error}`);
            return;
        }

        console.log(`phantom version: ${stdout}`);
        console.log(`Should have no error: ${stderr}`);

        callback(error, 'fin!!');
    });
};

Lambda Functionの設定と実行

Function Packageをアップロードしたら以下のように設定します。
(使用するスクリプトは、Code entry typeで、『Upload a .ZIP file』を選択してアップロードすることができるサイズ(10MB以下)になっているかと思います。)

設定項目 設定値
Runtime Node.js 4.3
Handler lambda-phantomjs/index.handler
Memory(MB) 1024
Timeout 5 min

初回実行時は 1min だと Timeout エラーが発生したので、5min としています。
実行結果を見ると2分ぐらいを見ておけば大丈夫かもしれません。
(2回目以降の実行時は 4000 ms 前後ぐらいで安定します。)

実行結果

Log output を確認すると以下のように実行されたことがわかります。

2016-11-24T10:02:46.845Z    f39f30db-b22c-11e6-94f0-653737dc93be    exec phantom:  -v
2016-11-24T10:02:47.105Z    f39f30db-b22c-11e6-94f0-653737dc93be    phantom version: 2.1.1

2016-11-24T10:02:47.105Z    f39f30db-b22c-11e6-94f0-653737dc93be    Should have no error: 
END RequestId: f39f30db-b22c-11e6-94f0-653737dc93be
REPORT RequestId: f39f30db-b22c-11e6-94f0-653737dc93be  Duration: 75160.69 ms   Billed Duration: 75200 ms   Memory Size: 1024 MB    Max Memory Used: 401 MB PhantomJS

phantomjs-lambda-pack を使うと PhantomJS の 2.1.1 が使われていることがわかります。
内部では phantomjs-prebuilt が使われているので、phantomjs-prebuilt が対応しているバージョンの PhantomJS が使われることになります。

実際にPhantomJSを使ってScraping

GoogleにアクセスしてPageのTitleを取得、および表示したページのスクリーンショットをS3にアップロードしてみます。
ディレクトリ構造は下記の通りです。
index.js と同じディレクトリ階層に phantomjs-script.js というスクリプトを作成しています。

.
├── index.js
├── node_modules
│   ├── aws-sdk
│   └── phantomjs-lambda-pack
│
├── package.json
└── phantomjs-script.js

事前に aws-sdk のインストールおよび、Lambdaの実行ロールにS3 への書き込み権限を付与しておきます。
handler の中で phantomjs-script.js を呼び出しています。

index.js
'use strict';
const phantomjsLambdaPack = require('phantomjs-lambda-pack');
const exec = phantomjsLambdaPack.exec;
const path = require('path');
const fs = require('fs');
const AWS = require('aws-sdk');
AWS.config.region = 'ap-northeast-1';

exports.handler = (event, context, callback) => {
    const scriptPath = path.join(__dirname, 'phantomjs-script.js');
    exec(scriptPath, (error, stdout, stderr) => {
        if (error) {
            console.error(`exec error: ${error}`);
            return;
        }

        console.log(`Result: ${stdout}`);
        console.log(`Should have no error: ${stderr}`);

       // スクリーンショットをS3にアップロードする
       const tmp_file_path = path.join(__dirname, 'google.png');
       const params = {Bucket: 'bucketName', Key: 'screen/google.png', Body: fs.createReadStream(tmp_file_path),};
       new AWS.S3().upload(params, (error, data) => {
           callback(error, 'fin!!');
       });
    })
}
phantomjs-script.js
// headlessブラウザを作成
var page = require('webpage').create();
page.settings.userAgent = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.157 Safari/537.36';
page.settings.javascriptEnabled = true;
page.settings.loadImages = false;//Script is much faster with this field set to false
phantom.cookiesEnabled = true;
phantom.javascriptEnabled = true;

//URLを開く
page.open('http://www.google.com', function(status) {
  //ブラウザ内でJSを介してデータを取得  
  var title = page.evaluate(function() {
    return document.title;
  });
  page.render('google.png');
  console.log('Page title is ' + title);
  phantom.exit();
});


実行結果として Log output に Result: Page title is Google が出力され、以下のようなスクリーンショットが確認できれば、問題なく実行されたことになります。

google.png

Tipsとか

jQueryを使いたい

jQueryを使ってDOMをパースしたいときは以下のように includeJs を使って書けます。

phantomjs-script.js
//省略

page.open('http://www.google.com', function(status) {
    page.includeJs('https://code.jquery.com/jquery-3.1.1.slim.min.js', function() {
        //ブラウザ内でJSを介してデータを取得  
        var title = page.evaluate(function() {
            return $('title').text();
        });
        console.log('Page title is ' + title);
        phantom.exit();
    });
});

PhantomJSのスクリプトにパラメータを渡したい

execの引数として配列を渡し、PhantomJSのスクリプトで system.args を利用できるようにしておくことでパラメータが渡せます。

index.js
// 省略

exports.handler = (event, context, callback) => {
    const scriptPath = path.join(__dirname, 'phantomjs-script.js');
    const childArgs = [
        scriptPath,
        'args1',
    ];
    exec(childArgs, (error, stdout, stderr) => {
        if (error) {
            console.error(`exec error: ${error}`);
            return;
        }

        console.log(`Result: ${stdout}`);
        console.log(`Should have no error: ${stderr}`);
    });
phantomjs-script.js
var system = require('system');
var args = system.args;

console.log(args.length);
console.log(args[1]);

// 省略

evaluateに外部で定義した変数を使用する

外部で定義した変数を使う場合は以下のようにします。

phantomjs-script.js
  var hoge = 'fuga';
  var title = page.evaluate(function(s) {
    console.log(s);
    return document.title;
  }, hoge);
22
19
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
22
19