フリーランスエンジニア向けIT系求人・仕事・案件情報サイト
更新日

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を利用する事で環境の作成・破棄が容易なのも嬉しいですね。
興味のある方は是非触ってみてください。

\ ログインしなくても検討機能が使える♪ /
新着のエンジニア案件を見てみる