試してみたら動かせました。調べてもそれっぽいの無さそうだったのでメモです。
参考にしてるコードとお断り
ググったらこちらのプルリクが当たったので、こちらをもとに書いてますが、現状の公式なやり方かはわかりません。
その他参考: VercelでLINE BOTを動かす 2020年5月版
実装
chrome-aws-lambda + puppeteer-coreを利用
vercelはAWS Lambda上で動いてる模様なので、動作するChromium Binaryもchrome-aws-lambdaを使うと良いっぽいです。
また、バイナリはchrome-aws-lambdaを利用するということで、バイナリが梱包されてないpuppeteer-coreを使います。
$ yarn add chrome-aws-lambda
ここで最新バージョンのchrome-aws-lambda
がインストールされますが、この時のバージョンにあわせてpuppeteer-core
もインストールしましょう。
僕の場合、chrome-aws-lambdaをインストールしたら3.1.1だったのでpuppeteer-coreもバージョン指定してインストールします。(なにも指定しないでインストールしたら3.2.0
がインストールされて、デプロイ後に怒られました。
$ yarn add puppeteer-core@3.1.1
- package.jsonのdependenciesで確認
こんな感じでこの二つのモジュールのバージョンが合ってれば大丈夫です。
"dependencies": {
"chrome-aws-lambda": "^3.1.1",
"puppeteer-core": "3.1"
}
プログラムの作成
プロジェクトのルートにapiフォルダを作成し、その中に任意のjsファイルを作成します。
api/run-puppeteer.js
とします。
Node.jsなコードもLambdaぽい書き方になります。
const chrome = require('chrome-aws-lambda');
const puppeteer = require('puppeteer-core');
module.exports = async (req, res) => {
const { URL = 'https://twitter.com/n0bisuke' } = req.query;
const browser = await puppeteer.launch({
args: chrome.args,
executablePath: await chrome.executablePath,
headless: chrome.headless,
});
const page = await browser.newPage();
await page.goto(URL); //URLにアクセス
// Get the "viewport" of the page, as reported by the page.
const dimensions = await page.evaluate(() => {
return {
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight,
title: document.title,
deviceScaleFactor: window.devicePixelRatio
};
});
console.log('Dimensions:', dimensions);
await browser.close();
//アクセスしたページのタイトルを取得
res.send(`${URL}のページタイトルは「${dimensions.title}」だよー!`);
}
こんな感じになりました。
補足
apiフォルダにjsファイルを作ってこんな感じに書くのが基本ぽいです。AWS Lambdaをちゃんと使ったことないけど、一つ上の階層でrequire/importしてる処理があるんでしょうね。
module.exports = (req, res) => {
const { name = 'World' } = req.query
res.send(`Hello ${name}!`)
}
参考: https://vercel.com/docs/runtimes#official-runtimes
デプロイして試す
$ vercel
デプロイして、 https://発行されたドメイン/api/jsファイル名
にアクセスします。
今回の場合はhttps://発行されたドメイン/api/run-run-puppeteer
です。
api/run-puppeteer.js
の中身参照ですが、アクセスすると、puppeteerが動いて指定したURL(デフォはhttps://twitter.com/n0bisuke
)にアクセスしてタイトルを取得してきます。
その他: scheenshotの画像保存が出来なかった
これは設定問題かもしれないですが、ログにこんな感じのが出てたので、Vercel上でファイル書き込みは出来ないのかも。
"errorMessage":"EROFS: read-only file system, open 'example.png'
(出来そうだって話あれば教えて下さい!)
補足: 毎回デプロイが面倒なのでローカルで試す
api/run-puppeteer.js
をこんな感じで書いてローカルでも試せました。
- ローカル環境
- mac os catalina
- Node.js v14.3.0
puppetterを追加します。
$ yarn add puppetter
process.env.AWS_LAMBDA_FUNCTION_VERSION
があればVercel上での動作と判断してます。
let chrome = {};
let puppeteer = {};
//puppeteer main process
const run = async (puppeteer, chrome={}, URL='https://twitter.com/n0bisuke') => {
const browser = await puppeteer.launch({
args: chrome.args,
executablePath: await chrome.executablePath,
headless: chrome.headless,
});
const page = await browser.newPage();
await page.goto(URL);
// Get the "viewport" of the page, as reported by the page.
const dimensions = await page.evaluate(() => {
return {
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight,
title: document.title,
deviceScaleFactor: window.devicePixelRatio
};
});
await browser.close();
return dimensions;
}
if(process.env.AWS_LAMBDA_FUNCTION_VERSION){
//Vercel
chrome = require('chrome-aws-lambda');
puppeteer = require('puppeteer-core');
}else{
//Local Test
puppeteer = require('puppeteer');
const URL = `https://protoout.studio`;
run(puppeteer,{},URL).then(res => console.log(res));
}
module.exports = async (req, res) => {
const { URL='https://twitter.com/n0bisuke' } = req.query;
const dimensions = await run(puppeteer, chrome, URL);
res.send(`${URL}のページタイトルは「${dimensions.title}」だよー!`);
}
手元で実行
$ node api/run-puppeteer.js
vercel dev
コマンドでも試せるかもですが、サーバー起動などで時間かかるのでこの手の処理だけならこっちの方が早いかなぁという感触です。
まとめや所感など
最終形のコードはディレクトリごとあげておきました。
chrome-aws-lambdaというのがあることを知れたのが収穫かも。あと、process.env.AWS_LAMBDA_FUNCTION_VERSION
で判定してるけどもっとよいやり方ないのかな......
あとやはりファイル書き込みが出来ない(スクショ保存)のかも気になるので誰か分かる方いたらコメントを...笑
Herokuなどでも問題ないけどVercel上でもpuppeteer動かしたい場合もあるかもしれないので誰かの参考になれば幸いです。