📸

Webサイトのスクリーンショットを自動化する方法

2021/03/28に公開
15

Web サイトのスクリーンショットはブラウザの開発ツールや拡張機能を利用すれば手軽に行えます。しかし、あらかじめスクショしたいサイトやページが決まっていてかつ複数ページある場合では都度ページを開いて作業しているとそれなりに時間がかかります。

この記事ではそんな手動でのスクリーンショット作業を自動化させる方法を紹介します。
知らない単語が途中出てきても何ができるのかイメージだけつかんでもらえれば大丈夫です。

実行環境の用意

今回スクリーンショットの自動化を Python という言語で行います。「Python って何!?」という方は以下リンクの情報を軽く見ておいてもらえると多少理解が進むかと思います。
https://www.python.jp/pages/about.html

手元のローカル PC で今回やるような処理を実行するには Python 自体をインストールする必要があります。この記事ではそのような環境構築の手間を省くため Google が提供する Colaboratory と呼ばれるサービスを利用します。
https://colab.research.google.com/notebooks/intro.ipynb

Colaboratory の利用方法は以下記事の一部を参照してみてください。ファイルの準備まで確認できれば OK です。
https://zenn.dev/kazuki_tam/articles/23410c1a4fe91f661db9#colaboratoryとは

スクリーンショット自動化スクリプトの作成

Colaboratory で新規ファイルの用意ができたら今回やりたいことの整理をしたいと思います。
最低限やりたいことは主に以下の 3 つです。

  • サイト URL を指定するだけでスクリーンショットを自動実行したい
  • 取得した画像を一括ダウンロードしたい
  • ウインドウサイズを指定してスクリーンショットを撮りたい

すぐに利用したい方はコピペで利用できるので「スクリーンショット自動化スクリプトの利用方法」から読み進めてください。

必要ファイルやライブラリの読み込み

chromium-browserエラー対応方法に関するコメントはこちら

コードブロックを追加し、以下処理を記述し、実行します。

%%shell
# Ubuntu no longer distributes chromium-browser outside of snap
#
# Proposed solution: https://askubuntu.com/questions/1204571/how-to-install-chromium-without-snap

# Add debian buster
cat > /etc/apt/sources.list.d/debian.list <<'EOF'
deb [arch=amd64 signed-by=/usr/share/keyrings/debian-buster.gpg] http://deb.debian.org/debian buster main
deb [arch=amd64 signed-by=/usr/share/keyrings/debian-buster-updates.gpg] http://deb.debian.org/debian buster-updates main
deb [arch=amd64 signed-by=/usr/share/keyrings/debian-security-buster.gpg] http://deb.debian.org/debian-security buster/updates main
EOF

# Add keys
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys DCC9EFBF77E11517
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 648ACFD622F3D138
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 112695A0E562B32A

apt-key export 77E11517 | gpg --dearmour -o /usr/share/keyrings/debian-buster.gpg
apt-key export 22F3D138 | gpg --dearmour -o /usr/share/keyrings/debian-buster-updates.gpg
apt-key export E562B32A | gpg --dearmour -o /usr/share/keyrings/debian-security-buster.gpg

# Prefer debian repo for chromium* packages only
# Note the double-blank lines between entries
cat > /etc/apt/preferences.d/chromium.pref << 'EOF'
Package: *
Pin: release a=eoan
Pin-Priority: 500


Package: *
Pin: origin "deb.debian.org"
Pin-Priority: 300


Package: chromium*
Pin: origin "deb.debian.org"
Pin-Priority: 700
EOF

# Install chromium and chromium-driver
apt-get update
apt-get install chromium chromium-driver

# Install selenium
pip install selenium

https://github.com/googlecolab/colabtools/issues/3347

Colaboratory は標準で日本語フォントが入っていないようで、フォントを入れずに動かすと文字化けしてしまいます。(2021/03/28)

# 日本語フォントインストール
!apt-get -y install fonts-ipafont-gothic
!apt-get -y install fonts-ipafont-mincho

スクリーンショットの自動化スクリプトをすべて一からプログラムを書いていくとめちゃくちゃ時間がかかってしまうので、ライブラリと呼ばれる複雑な処理を抽象化して使いやすくしたプログラムを利用します。
https://ja.wikipedia.org/wiki/ライブラリ#:~:text=ライブラリ(英%3A library)は,コードの集まりと言える。

ブラウザ操作を自動化させるため Selenium をインストールします。Selenium はブラウザ操作を自動化させるのに必要なフレームワークです。
https://qiita.com/Chanmoro/items/9a3c86bb465c1cce738a
https://www.selenium.dev/

# ライブラリインポート
from google.colab import files
import time
import shutil
import os

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

# Chromeヘッドレスモード起動
options = webdriver.ChromeOptions()
options.headless = True
options.add_argument('--no-sandbox')
options.add_argument('--disable-dev-shm-usage')
driver = webdriver.Chrome('chromedriver',options=options)
driver.implicitly_wait(10)

ブラウザを裏側で起動させてスクリーンショットをとるために Headless Chrome を利用します。
Headless Chrome はブラウザーの UI を利用せずにブラウザ操作することを可能にします。
たとえば「Chrome を立ち上げてサイトにアクセスして・・・」といった作業をコマンドで指定できます。
https://developers.google.cn/web/updates/2017/04/headless-chrome?hl=ja

スクリーンショットを撮りたいサイトリストを以下のように記述します。

# 対象URL
urls = ['https://example.com', 'https://example.com/about/']

次に調整用の変数を定義しておきます。変数を定義しておくことで、ブラウザのウインドウサイズやファイル名を後から調整しやすくします。

ファイル名の接頭辞は fileNamePrefix で調整できます。screen とすると screen-1.png のように連番で保存されます。

ウインドウ幅 375px でスクリーショットを撮りたい場合は windowSizeWidth = 375 と指定します。ウインドウ幅を指定した場合、 screen-1200-1.png のようにサイズがファイル名に付与されます。

ウインドウの高さを指定したい場合も幅と同様です。
windowSizeHeight = 1000

ファイル名が被らないように連番を付与させるため番号の初期値を指定します。
shotNum = 0

# ファイル名接頭辞
fileNamePrefix = "screen"

# ウインドウ幅指定
# 初期値: False, スマホ: 375
windowSizeWidth = False
# windowSizeWidth = 1200

# ウインドウ高さ指定
# 初期値: False
windowSizeHeight = False
# windowSizeHeight = 1000

# 連番初期値
shotNum = 0

ダウンロードフォルダーの作成を以下の処理で行います。

# ダウンロードフォルダ作成
folderCheck = os.path.exists('screen-shot')
if folderCheck:
  shutil.rmtree('screen-shot')
os.mkdir('screen-shot')

urls で定義したサイトにアクセスし、スクリーショットを撮る処理が以下に記述されています。
スクロールされてから要素が表示されるような表現に対応するため、わざとサイト下部までスクロールする処理を入れています。

# 繰り返し処理
for url in urls:
  # パス指定
  folderPath = 'screen-shot/' + fileNamePrefix + '-' + str(windowSizeWidth) + '-' if windowSizeWidth else 'screen-shot/' + fileNamePrefix + '-'

  # サイトURL取得
  driver.get(url)
  WebDriverWait(driver, 15).until(EC.presence_of_all_elements_located)

  # ウインドウ幅・高さ指定
  windowWidth = windowSizeWidth if windowSizeWidth else driver.execute_script('return document.body.scrollWidth;')
  windowHeight = windowSizeHeight if windowSizeHeight else driver.execute_script('return document.body.scrollHeight;')
  driver.set_window_size(windowWidth, windowHeight)

  # スクロール処理
  driver.execute_script('window.scrollTo(0, document.body.scrollHeight);')

  # 処理後一時待機
  time.sleep(3)

  # ファイル連番追加
  shotNum += 1
  shotNumStringified = str(shotNum)

  # スクリーンショット格納
  driver.save_screenshot(folderPath + shotNumStringified + '.png')

  # サーバー負荷軽減処理
  time.sleep(1)

# ブラウザ稼働終了
driver.quit()

最後に保存したスクリーンショットを zip ファイルにまとめてダウンロードする処理を記述します。

# zipファイル変換ダウンロード
!zip -r download.zip screen-shot
files.download('download.zip')

以上でコードの記述は完了です。

スクリーンショット自動化スクリプトの利用方法

サイト URL を指定するだけでスクリーンショットを自動実行し、一括でファイルをダウンロードできます。次の完成スクリプトをそのままコピペして利用できます。すぐに利用したい方は対象 URL だけ適宜調整してください。

まず新規でブロック追加し、以下コードを記述します。添付ができれば一度実行します。

処理を実行する際は赤枠の ▷ ボタンを押下してください。

上記コードの実行ができれば、さらに新規でブロックを追加して同様に以下コードをコピペします。

最後、上記コードを実行するとスクリーンショットの処理が実行されます。

ダウンロードファイルの管理

スクリーンショットされた画像ファイルは download.zip ファイルに格納されます。
意図的に格納ファイルを削除してリセットしたい場合は左サイドバーの 🗂 アイコンを押下し、download.zip ファイルを削除できます。

まとめ

アニメーションで画面が大きく変わるサイトではうまくスクリーンショットを取れないケースがあるかもしれません。そのような場合は処理のタイミングを遅らせてみるなど調整してみてください。
Web 制作の現場ではスクリーンショットをとる作業がよくあるかと思います。今回紹介した内容が時間の効率化に繋がれば幸いです。

Discussion

ankatsankats

yonemotoさま
記事拝見して、とても関心を持ちました。いきなりですが、薄謝ではありますがご提供させていただいたうえで、特定のサイト(観光予報プラットフォーム、公益社団日本観光振興協会)の向こう3か月の観光需要の予測画面を、約1週間おきにスクショし続けるプログラムを組んでいただくことは可能でしょうか?

Kazuki YonemotoKazuki Yonemoto

コメントありがとうございます!この記事で紹介するColaboratoryは定期実行に対応していないため、記載いただいた要件ですと一からスクラッチで開発するより以下のようなサービスを使われるのが良いかと思います。
https://pashaco.app/

ankatsankats

ご親切にありがとうございました。よく見てみます。

tuna2134tuna2134

えーと
seleniumのv4系を入れたらoptions.add_argument("--headless")が使えなくなっており、options.headless = Trueでできるらしいです。

kkkdkkkd

とても参考になる記事をありがとうございます。
いつもこの記事を参考にスクリーンショットをとっているのですが、colaboのアップデートに伴い以下部分でエラーが発生するようになってしまいました。
driver = webdriver.Chrome('chromedriver',options=options)

解決策のようなものは見つけられたのですが、初心者には難しく、
よろしければご教示いただけないでしょうか?
ttps://github.com/googlecolab/colabtools/issues/3347

Kazuki YonemotoKazuki Yonemoto

確認したところ私の方でも同様のエラーが確認できました。
原因は以下に記載いただいた参照先通りのようでした。
https://github.com/googlecolab/colabtools/issues/3347

以下手順で対応できるかと思います。

  1. ランタイムの接続削除(Colabのメニューから実行)
  2. 事前処理コードの追加と実行
  3. 本体コード(スクリーンショット用)を実行

1 ランタイムの接続削除(Colabのメニューから実行)

2. 事前処理コードの追加と実行

以下コードを本体コードの前にコードブロックを追加し、以下処理のみ先に実行します。

%%shell
# Ubuntu no longer distributes chromium-browser outside of snap
#
# Proposed solution: https://askubuntu.com/questions/1204571/how-to-install-chromium-without-snap

# Add debian buster
cat > /etc/apt/sources.list.d/debian.list <<'EOF'
deb [arch=amd64 signed-by=/usr/share/keyrings/debian-buster.gpg] http://deb.debian.org/debian buster main
deb [arch=amd64 signed-by=/usr/share/keyrings/debian-buster-updates.gpg] http://deb.debian.org/debian buster-updates main
deb [arch=amd64 signed-by=/usr/share/keyrings/debian-security-buster.gpg] http://deb.debian.org/debian-security buster/updates main
EOF

# Add keys
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys DCC9EFBF77E11517
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 648ACFD622F3D138
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 112695A0E562B32A

apt-key export 77E11517 | gpg --dearmour -o /usr/share/keyrings/debian-buster.gpg
apt-key export 22F3D138 | gpg --dearmour -o /usr/share/keyrings/debian-buster-updates.gpg
apt-key export E562B32A | gpg --dearmour -o /usr/share/keyrings/debian-security-buster.gpg

# Prefer debian repo for chromium* packages only
# Note the double-blank lines between entries
cat > /etc/apt/preferences.d/chromium.pref << 'EOF'
Package: *
Pin: release a=eoan
Pin-Priority: 500


Package: *
Pin: origin "deb.debian.org"
Pin-Priority: 300


Package: chromium*
Pin: origin "deb.debian.org"
Pin-Priority: 700
EOF

# Install chromium and chromium-driver
apt-get update
apt-get install chromium chromium-driver

# Install selenium
pip install selenium

3. 本体コード(スクリーンショット用)を実行

本体コードのselenium、chromium、chromium-driverインストール部分が2の処理と重複するため削除。

# seleniumインストール
# !apt-get update
# !apt install chromium-chromedriver
# !cp /usr/lib/chromium-browser/chromedriver /usr/bin
# !pip install selenium

上記修正ができれば、本体コードを実行します。

kkkdkkkd

迅速なご対応とご返信ありがとうございます。
いただいた内容で試してみたのですが、以下のような別の行でエラーが出てしまい、
ダウンロードにまでいかないみたいなのですが、こちらはまた別の問題になりますでしょうか…?

行: driver.save_screenshot(folderPath + shotNumStringified + '.png')
▼エラー
TimeoutException: Message: timeout: Timed out receiving message from renderer: 10.000
(Session info: headless chrome=90.0.4430.212)

Kazuki YonemotoKazuki Yonemoto

以下のようなエラーが出ているので、対象サイト側の問題が高いかもです。

Timed out receiving message from renderer

kkkdkkkd

サイトをもう一度確認してみます。
ご丁寧にご対応いただきありがとうございました!

under_22under_22

こちらの記事参考にして自動取得試してるのですが、
添付画像のコードで実行してもまだ下記エラーが消えません。

driver = webdriver.Chrome('chromedriver',options=options)

https://zenn.dev/link/comments/6a072634d74945
こちらのスレッドの3.本体コードの削除箇所について、本体コードにて該当箇所が見当たらないためどこを削除すれば良いのでしょうか?

Kazuki YonemotoKazuki Yonemoto

Seleniumのバージョン変更が原因のエラーのようです。
以下のように調整することで対応できました。

https://kimudiary.com/python/googleアカウントで作業の自動化【google-colaboratoryでwebスクレイピ/

from google.colab import files
import requests
import time
import shutil
import os
from selenium import webdriver
from selenium.webdriver.chrome.options import Options

# 以下追記
from selenium.webdriver.chrome.service import Service

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

# 以下に変更
options=webdriver.ChromeOptions()
options.add_argument('--headless')
options.add_argument('--no-sandbox')
options.add_argument('--disable-dev-shm-usage')
service = Service(executable_path=r'/usr/bin/chromedriver')
driver = webdriver.Chrome(service=service,options=options)
under_22under_22

ありがとうございます。
無事エラー解消され、スクショ取得できました。

under_22under_22

また別の問題が発生しており、申し訳ございませんがアドバイスいただけますと幸いです。

【問題】
一部ページがウィンドウ長すぎて、ページ全体のスクショが取れないです。
現状、windowSizeHeight = Falseだと全体が取れずwindowSizeHeight = "数値"にすると他の関係ないページが余白取得してしまいます。

False以外でページ別に全体取得できる値ありますでしょうか?
もしくは他の#スクロール処理 #サーバー負荷軽減処理などで対処できますでしょうか?