🌐

Chrome拡張でページのスクリーンショットを撮る

2022/06/17に公開

はじめに

  • 今回はChrome拡張でページのスクリーンショットを撮ることが出来る機能を実装しようと思います。
  • ページの表示範囲だけでなく、スクロール後のページの全てをキャプチャ出来るように実装します。

対象読者

  • Chrome拡張でタブ内のページのスクリーンショットを撮りたい方

Chrome拡張の開発について

https://developer.chrome.com/docs/extensions/mv3/

  • Google公式サイトがあります。(英語)
  • Manifest V3になっているので、V2で対応された方はマイグレーションを参考にしてください。

実装方針

  • debuggerのsendCommandを使用してデバッガのスクリーンショット機能やレイアウト情報取得などを呼び出します。
  • sendCommandを使用する場合、デバッガへのアタッチが必要です。今回はデバッガへのアタッチと処理終了後にデタッチをする処理も実装します。
  • デバッガを使用する場合はタブ情報なども必要なため、chrome.tabs.queryでタブ情報を取得します。
  • Chrome DevTools ProtocolにsendCommandで使用するコマンドの一覧が記載されています。
  • 今回はPage.captureScreenshotPage.getLayoutMetricsを使用します。

今回やらないこと

tabsのcaptureVisibleTabを使用すれば表示範囲のスクリーンキャプチャは可能です。
スクロール事の画像をつなげたり、ページのスクロール処理とstickyやfixedの対策をすることで、今回の実装と同じことが出来ます。
実装量が多かったため、今回はdebuggerを使用した実装にしてあります。

サンプルコード

manifest.json

manifest.json
{
  "manifest_version": 3,
  "name": "ScreenCapture Test",
  "version": "1.0",
  "description": "ScreenCapture Test",
  "permissions": ["activeTab", "debugger"],
  "action": {
    "default_popup": "popup.html"
  }
}

popup.html

popup.html
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
</head>
<body style="margin: 0px; width: 100px;">
  <div style="padding: 10px; background: lemonchiffon;">
    <div>
      <button id="btn">画像保存</button>
    </div>
  </div>
  <script src="/popup.js"></script>
</html>

popup.js

popup.js
document.getElementById('btn').addEventListener('click', async () => {
  // タブ情報取得
  let [tab] = await chrome.tabs.query({ active: true, currentWindow: true });

  // デバッガアタッチ
  chrome.debugger.attach({ tabId: tab.id }, '1.3', async () => {
    console.log('attach - ok');

    // デバッガ起動待機
    await new Promise((resolve) => setTimeout(resolve, 500));

    // レイアウト情報取得
    chrome.debugger.sendCommand({ tabId: tab.id }, 'Page.getLayoutMetrics', {}, (metrics) => {
      // スクリーンショットパラメータ作成
      const params = {
        format: 'png',
        quality: 50,
        clip: {
          x: 0,
          y: 0,
          width:  metrics.cssContentSize.width,
          height: metrics.cssContentSize.height,
          scale: 1
        },
        captureBeyondViewport: true
      }

      // スクリーンショット撮影
      chrome.debugger.sendCommand({ tabId: tab.id }, 'Page.captureScreenshot', params, (result) => {
        // 画像保存
        const downloadEle = document.createElement('a');
        downloadEle.href = 'data:image/png;base64,' + result.data;
        downloadEle.download = 'screenshot.png';
        downloadEle.click()

        // デバッガでタッチ
        chrome.debugger.detach({ tabId: tab.id }, () => {
          console.log('detach ok')
        });
      });
    });
  });
});

動作確認

1. 上記のサンプルコード一式をフォルダに保存

  • 上記のサンプルコード一式を任意のフォルダに保存します。

2. 拡張機能の読み込み

  • Chromeの拡張機能に遷移します。
  • デベロッパーモードをONにします。
  • 「パッケージ化されていない拡張機能を読み込む」ボタンをクリックします。
  • 1で保存したフォルダを指定すれば読み込み完了です。

3. 拡張機能の読み込み状況の確認

拡張機能一覧に追加されていらば読み込み成功です。

4. 拡張機能の起動

  • アドレスバー横の拡張機能一覧の「ScreenCapture Test」をクリックします。

  • ポップアップが表示されれば成功です。
  • 「画像保存」ボタンをクリックすることで、開いているタブのページのスクリーンショットが撮影できます。

おわりに

  • 簡単な実装でChrome拡張でスクリーンショットを撮影できるようになりました。
  • Chrome拡張内でスクリーンショットのアップロードなども可能なので、システムと組み合わせることができます。
  • Manifest V3やDevTools Protocolの日本語情報が少ないので、今回はそこに苦戦しました。
  • 今回のサンプルはcallback地獄になっています。開発で使用する場合は、Promiseでラッピングしてあげてasync/await方式で実装すると良さそうです。

ソースコード一式

https://gist.github.com/yasu-s/1405183ba61876ba83dd5dcc4d0162f0

関連記事

https://zenn.dev/kakkoyakakko/articles/72c114c71455f7

Discussion