Python Selenium Chrome スクリーンショット

# 取得イメージ
**①表示されている箇所のみスクリーンショットを取るパターン**
(スクロールで隠れている箇所は隠れたまま)
![スクロール.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/557347/c3306f3f-730b-12f7-2bb6-8d95b17d8126.png)

**②スクロールで隠れた箇所も含めてスクリーンショットを取るパターン**
(すごい縦長のページの場合、すごい縦長の画像になります)
どちらも対応できる処理を作ります。
![test.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/557347/947a4dcb-2f6b-0609-c6aa-12b4e2a3c175.png)


# 環境
windows10
Python 3.11.4
Selenium 4.10.0
Chrome 114.0.5735.134

※Chrome DevTools Protocolの機能を利用するためChrome専用です。


# スクリーンショット取得処理
以下のようにするとスクリーンショットを取得できます。
```python: スクリーンショット取得処理
import base64
from selenium import webdriver

def save_screenshot(driver, file_path, is_full_size=False):
    # スクリーンショット設定
    screenshot_config = {
        # Trueの場合スクロールで隠れている箇所も含める、Falseの場合表示されている箇所のみ
        "captureBeyondViewport": is_full_size,
    }

    # スクリーンショット取得
    base64_image = driver.execute_cdp_cmd("Page.captureScreenshot", screenshot_config)

    # ファイル書き出し
    with open(file_path, "wb") as fh:
        fh.write(base64.urlsafe_b64decode(base64_image["data"]))


# driver生成
driver = webdriver.Chrome()
# ウィンドウ最大化
driver.maximize_window()
# サイト表示
driver.get("https://pypi.org")
# 見えている箇所のみのスクリーンショット取得 取得イメージ①
save_screenshot(driver, "not_full_size.png")
# 全画面スクリーンショット取得　取得イメージ②
save_screenshot(driver, "full_size.png", is_full_size=True)

```

## 解説
### スクリーンショットパラメーター
Chrome DevTools Protocol(CDP) の機能でスクリーンショットを取っており
その際に、「全画面」にするか「見えている範囲のみ」にするかを指定しています。
パラメーターはCDPのドキュメントを参照

https://chromedevtools.github.io/devtools-protocol/tot/Page/#method-captureScreenshot

重要なのは```captureBeyondViewport```と```fromSurface```の2つのパラメーターです。
どちらも```True```になっていないと**表示されている箇所のみ**のスクリーンショットになります。

```fromSurface```はデフォルト値が```True```になっているので指定せず
```captureBeyondViewport```を変更し
**True**: 全画面
**False**: 見えている範囲のみ
を切り替えています。

```python: 全画面、見えている範囲のみの切り替え
screenshot_config = {
    # Trueの場合スクロールで隠れている箇所も含める、Falseの場合表示されている箇所のみ
    "captureBeyondViewport": is_full_size,
}
```

### スクリーンショット取得関数
「保存するファイルのパス」と「全画面で取得するか」を引数に渡して呼び出せるよう関数化しています。
```python
save_screenshot(driver, file_path, is_full_size=False)
```
```is_full_size```(全画面で取得するか)は初期値をFalse(見えている範囲のみ)にしているので
全画面で取得したい場合は```True```を指定して呼び出します。
```python:全画面でスクリーンショット
save_screenshot(driver, "full_size.png", is_full_size=True)
```
```python:見えている範囲のみスクリーンショット
# is_full_sizeを指定しないとデフォルトでFalseになる
save_screenshot(driver, "not_full_size.png")

# または明示的に指定することも可
save_screenshot(driver, "not_full_size.png", is_full_size=False)
```

```file_path``` は絶対パス、相対パスどちらでも指定できます。
例えば以下のようにすると、screenshotというフォルダの中に保存できます。
(フォルダが存在しないとエラーになるので、事前に作成)
```python:フォルダ内に保存するパス指定例
save_screenshot(driver, "screenshot/test.png")
```

以上で「見えている範囲のスクリーンショットをとる」「全画面のスクリーンショットをとる」ことができました。
以降は、スクリーンショットの取得位置・範囲を指定する方法です。

# (位置を指定する)
パラメーター```clip```で位置を明示的に指定することができます。
```python
# 画面サイズの取得
page_rect = driver.execute_cdp_cmd("Page.getLayoutMetrics", {})

# スクリーンショット設定、全画面取得されるよう設定
screenshot_config = {
    "captureBeyondViewport": True,
    "clip": {
        "width": page_rect["contentSize"]["width"],
        "height": page_rect["contentSize"]["height"],
        "x": 0,
        "y": 0,
        "scale": 1,
    },
}

# スクリーンショット取得
base64_image = driver.execute_cdp_cmd("Page.captureScreenshot", screenshot_config)
```

```width```: 画面幅
```height```: 画面高さ
```x```: 横位置
```y```: 縦位置
```scale```: ページの縮尺
を指定することができます。

以下のように設定すると、右に200pxずらし、縮尺30%でスクリーンショットできます。
```python
screenshot_config = {
    "captureBeyondViewport": True,
    "clip": {
        "width": page_rect["contentSize"]["width"],
        "height": page_rect["contentSize"]["height"],
        "x": 200,
        "y": 0,
        "scale": 0.3,
    },
}
```
![test.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/557347/b8d37785-836a-0717-4af1-8f0724eaa4e7.png)

# 参考
https://www.selenium.dev/selenium/docs/api/py/webdriver_chromium/selenium.webdriver.chromium.webdriver.html#selenium.webdriver.chromium.webdriver.ChromiumDriver.execute_cdp_cmd