Puppeteerで行う画面スクリーンショット

目次
Puppeteerの概要と用途
今回ご紹介するPuppeteer(パペティア)はheadless-Chrome(GUIがないGoogle Chrome)やChromiumを制御するAPIを提供するGoogle製ライブラリです。
ブラウザで手動実行できるほとんどのことは、Puppeteerを使用して実行できるそうですが、紹介例で使用されるのは大抵がスクリーンショットです。
今回は『WEBページの一部分をスクリーンショットしその画像を保存する』という機能を既存のlaravel環境に追加する形でご紹介します。
導入内容の概要
今回はJS/TSのランタイム環境にdenoを利用する為、puppeteerからフォークされたdeno-puppeteerを利用します。
※DenoはJSやTSのランタイム環境でNodeの後継のような存在です
今回はdockerで利用する為、deno-puppeteerから取得したDockerfileを利用してdenoコンテナを作り、Web APIとして実行できる形にしていきます。
構成
既存の構成にdenoコンテナ追加する形で進めます。
※要点のみを抜粋
┏ docker-compose.yml //(既存)
┣ php //(既存)
┣ deno //(新規作成)
┃ ┗ Dockerfile //※deno-puppeteerからgit clone
┣ puppeteer //(新規作成)
┃ ┣ app
┃ ┃ ┣ router.ts
┃ ┃ ┣ server.ts
┃ ┃ ┗ Controllers
┃ ┃ ┗ controller.ts
┃ ┗ puppeteer
┃ ┣ screenshot.js
┃ ┗ screenshots
┣ laravel //(既存)
┃ ┗ app
┃ ┣ Console
┃ ┃ ┗ Command
┃ ┃ ┗ GetScreenshot.php
┃ ┗ Libraries
┃ ┗ Screenshot.php
┗ その他 //(既存/省略)
実装してみる
WEBサーバとスクリーンショット処理の準備
※要点のみを抜粋
※セキュリティ面等を考慮していない為、ご注意ください
deno側
- 8888ポートでリクエストを受け付ける
//puppeteer/app/server.ts
await app.listen({ port: 8888 });
- ルーティングでエンドポイントを作成
- スクリーンショットを行いたいページのIDを引数として取るので:page_idとしておく
//puppeteer/app/router.ts
const router = new Router();
router.get('/api/screenshot/create/:page_id', controller.screenshot);
- スクリーンショット処理の関数を作成
//puppeteer/app/controllers/Controller.ts
async screenshot(ctx: RouterContext) {
const { page_id } = helpers.getQuery(ctx, { mergeParams: true });
const p = Deno.run({
cmd: ["deno", "run", "-A", "--no-check", "--unstable", "/root/puppeteer/screenshot.js", page_id]
});
p.close();
}
- スクリーンショット処理を作成
- スクリーンショットを取得したいHTML要素をidで指定(#target_ele)
//puppeteer/puppeteer/screenshot.js
// スクリーンショットするURLを指定
await page.goto(
`http://example.com/page/${Deno.args[0]}`
);
// スクリーンショットするHTML要素、スクリーンショットサイズ、スクリーンショット位置を指定
const dimensions = await page.evaluate(() => {
const dom_selector = "#target_ele";
const el = document.querySelector(dom_selector);
return {
width: el.clientWidth,
height: el.clientHeight,
deviceScaleFactor: window.devicePixelRatio,
x: 20,
y: 232,
};
});
// 実行
await page.screenshot({
path: `/public/images/screenshots/${Deno.args[0]}.png`,
clip: dimensions,
});
await browser.close();
PHP(laravel)側
- laravel側から実行できる形を作成
- コンテナ間通信はコンテナ名で接続できる為、URLではなくコンテナ名(deno)を指定してcurlする
//laravel/app/Libraries/Screenshot.php
public function takeScreenshot ($page_id){
exec("curl deno:8888/api/screenshot/create/".$page_id);
}
denoコンテナ立ち上げ
(PuppeteerとChromeのインストール含)
- container_nameをdenoに設定する
- 頻繁に変更するファイルはvolumesでマウントしておく(imageに含めるとソースを更新する毎にbuildが必要になる為)
#docker-compose.yml
deno:
container_name: deno
build:
context: ./deno
ports:
8888:8888
volumes:
./puppeteer/app:/root/app
./puppeteer/puppeteer/screenshot.js:/root/puppeteer/screenshot.js
./laravel/public/images/screenshots:/var/www/laravel/public/images/screenshots
- deno-puppeteerから取得したDockerfileの84行目以降を以下に変更。
#Dockerfile
# denoにパスを通す
ENV DENO_INSTALL="/root/.deno"
ENV PATH="$DENO_INSTALL/bin:$PATH"
# PuppeteerとChromeをインストール
RUN PUPPETEER_PRODUCT=chrome deno run -A --unstable install.ts
# WEBサーバーを起動
ENTRYPOINT ["deno"]
CMD ["run", "-A", "--no-check", "--unstable", "./app/server.ts"]
- コンテナをバックグラウンドで立ち上げ
docker-compose up -d
実行
- laravelのartisanコマンドを使用して実行
- http://example.com/page/1から指定した座標、サイズのスクリーンショットを取得
// laravel/app/Console/Commands/GetScreenshot.php
class GetScreenshot extends Command{
protected $signature = 'screenshot:take';
// スクリーンショット作成
public function handle(){
$page_id = 1; //ページIDを取得
$screenshot = new Screenshot;
$screenshot->takeScreenshot($page_id);
}
}
php artisan screenshot:take
まとめ
以上がPuppeteerで行う画面スクリーンショットのご紹介となります。
Dockerを利用する事で環境の作成・破棄が容易なのも嬉しいですね。
興味のある方は是非触ってみてください。
\ ログインしなくても検討機能が使える♪ /
新着のエンジニア案件を見てみる