🤖

Canvas要素にて描かれた画像をスクレイピングする方法

2023/06/09に公開

備考

この記事は Qiita にて記載した記事を少し修正したものです.

はじめに

この記事ではcanvas要素にて描かれた画像をスクレイピングする方法について記します.

注意

実際に下記の方法を利用する際、著作権法やスクレイピングするサイトの利用規則を理解した上で利用をお願いいたします.

課題

機械学習やデータ分析のために画像をスクレイピング・クローリングすることがあるかと思います.
通常,サイト上の画像は img 要素に画像 URL を設定することでサイト上に表示しています.

<img src="<画像URL>" />

スクレイピング時に img 要素内の画像 URL を取得し,別途画像をダウンロードすることで img 要素内の画像を取得できます.

main.py
import time

import requests
from selenium import webdriver
from selenium.webdriver.common.by import By
import chromedriver_binary


def main():
    # Selenium Driver起動
    driver = webdriver.Chrome()

    # 画像があるURLへ移動
    driver.get("<画像のあるURL>")

    # 待機処理
    time.sleep(3)

    # 画像URL取得
    img_element = driver.find_element(By.CSS_SELECTOR, "<img要素のCSS Selector>")
    img_url = img_element.get_attribute("src")

    # 画像要素取得
    img_content = requests.get(img_url).content

    # 画像保存
    with open("<任意の画像名.png>", "wb") as w:
        w.write(img_content)

    # Selenium Driver終了
    driver.close()

    return


if __name__ == '__main__':
    main()

しかし,サイトによっては img 要素にて表示せず canvas 要素に描画して表示していることがあります.

index.html
<canvas />
main.js
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
ctx.fillStyle = "green";
ctx.fillRect(10, 10, 100, 100);

その際 img 要素から取得する方法と同じ方法にて画像を取得できません.

課題解決策

canvas 要素から直接画像を抽出して保存することで画像を取得できます.

動作環境(2023/06/09)

  • Python:3.10.3
    • selenium:4.9.1
  • Chrome:114.0.5735.106
    • ChromeDriver:114.0.5735.90

ソースコード

main.py
import os
import time
import base64

from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
import chromedriver_binary


def main():
    # 保存用フォルダの確認&作成
    if os.path.exists('img') is False:
        os.mkdir('img')

    # 検証用URL
    url = "https://yum-git.github.io/Scraping-lec/html/"

    # Selenium Driver起動
    driver = webdriver.Chrome()

    # 指定したURLへ移動&待機
    driver.get(url)
    WebDriverWait(driver, 30).until(
        EC.presence_of_element_located((By.CSS_SELECTOR, "body"))
    )

    # 今回は6枚の画像を用意したので6回処理を行う
    for idx in range(6):
        # 負荷軽減のため動作を待機する
        time.sleep(3)

        # canvas要素を取得する
        canvas_element = driver.find_element(By.CSS_SELECTOR, "#output")

        # javascriptにて画像のDataURLsを取得する
        canvas_dataurls = driver.execute_script(
            "return arguments[0].toDataURL('image/png').substring(21);",
            canvas_element
        )

        # DataURLsをバイナリデータにデコードする
        canvas_content = base64.b64decode(canvas_dataurls)

        # デコードしたデータを保存する
        with open("img/{}.png".format(idx), 'wb') as f:
            f.write(canvas_content)

        # 次の画像に移動する
        canvas_element.click()

    # Selenium Driver終了
    driver.close()

    return


if __name__ == '__main__':
    main()

実行結果

今回テスト用のページを GithubPage にて作成しました.canvas 属性をクリックすると別の画像を描画します.
https://yum-git.github.io/Scraping-lec/html/

main.py を実行すると canvas 属性に描画された画像が保存できていることが確認できます.

まとめ

上記の方法を利用することでcanvas要素内の画像もスクレイピングできます.

株式会社マインディア テックブログ

Discussion