はじめに
最近、個人的な趣味で地震の震度分布図を作画してツイートするプログラムを作成したのですが、そこで使った画像生成が他のものでも使えるのではないかということで備忘録として書こうかと思います。
原理
PythonのSeleniumと呼ばれるフレームワークは、Webアプリなどのテストをするものなんですが動的サイトのスクレイピングなどにも用いられています。
レンダリングは、Google ChromeのヘッドレスプラウザというCUIベースで動くChromeでレンダリングしています。
その、Seleniumにはスクリーンショットの機能があるためローカルサーバーを立てそこに必要な情報をGETしてあげることで好きな画像をHTML/CSSで作成することが可能です。
必要なもの
-
Python
-
Selenium
一応、SeleniumはRubyやJSにもあるみたいです。
Webページをキャプチャするためのフレームワーク。
pipでのインストール方法は以下
pip install selenium
- Docker
WindowsやMacなどにすでにChromeが入っている場合はなくてもいいのですが、VPS上で実行する際などではChromeをDocker内に入れることで実行します。
- ローカルサーバー
Node.js、DenoなどでもOK。今回はPythonのフレームワークのFlsakを使用します。
作る
今回作ったソースコードは、ここにおいてあります。
Webサーバーを作る
今回はFlaskで作成し、Docker composeでコンテナとしてまとめます。そのため、Pythonのモジュールはpipenvで管理しようかと思います。
以下を入力してPipenvをインストールし、Flaskをインストールします。
pip install pipenv
pipenv --python 3.8
pipenv install flask
更に、たりないファイルなどを追加して以下のようにします。
ディレクトリ構成は以下です。
server
├── Dockerfile
├── Pipfile
├── Pipfile.lock
├── run.py
├── static
│ └── css
│ └── style.css
└── templates
└── index.html
run.py
、index.html
、style.css
は自分の好きなように作ってください。
今回は、URLパラメータでGETした文字列を画面に表示するだけにします。
Dockerfileは以下のように記述します。出力ポートは5000になっていますが任意で大丈夫だと思います。
FROM python:3.8
COPY run.py /run.py
COPY templates /templates
COPY static /static
COPY Pipfile /Pipfile
COPY Pipfile.lock /Pipfile.lock
RUN pip install pipenv
RUN pipenv install --system --deploy
EXPOSE 5000
pipenv shell && python run.py
を試してみて、ブラウザなどで正常に表示できたらOKです。
キャプチャ部分を作る
先程つくったserver
ディレクトリの上部に生成用のプログラムファイルなどを作成します。
ディレクトリはこのようにします。
.
├── docker-compose.yml
├── Dockerfile
├── images
├── main.py
├── Pipfile
├── Pipfile.lock
├── README.md
└── server
pipenvでインストールするモジュールはSeleniumです。
pipenv install selenium
画像生成のDockerfileは以下のようにします。こちらは、Google Chromeも一緒にインストールする必要があります。
FROM python:3.8
COPY main.py /main.py
COPY Pipfile /Pipfile
COPY Pipfile.lock /Pipfile.lock
RUN apt-get update && apt-get install -y unzip
# install google-chrome, vim
RUN wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add && \
echo 'deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main' | tee /etc/apt/sources.list.d/google-chrome.list && \
apt-get update && \
apt-get install -y google-chrome-stable
RUN apt-get install -y vim
# install ChromeDriver
ADD https://chromedriver.storage.googleapis.com/84.0.4147.30/chromedriver_linux64.zip /opt/chrome/
RUN cd /opt/chrome/ && \
unzip chromedriver_linux64.zip
ENV PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt/chrome
RUN pip install pipenv
RUN pipenv install --system --deploy
ADD https://chromedriver.storage.googleapis.com/84.0.4147.30/chromedriver_linux64.zip /opt/chrome/
は現在のChromeのバージョンのリンクが必要です
から、現在のバージョンのリンクを貼り付けます。
さらに、サーバーのコンテナとこのコンテナをつなげるためdocker-compose.yml
を作成します。
version: '3'
services:
server:
container_name: server
build:
context: server
dockerfile: Dockerfile
ports:
- '5000:5000'
tty: true
# restart: always
command: python3 run.py
main:
container_name: main
build: .
tty: true
# restart: always
links:
- server
volumes:
- ./images/:/images/
command: python3 main.py
ここで重要なのは生成した画像をDocker外で使用する場合はその保存するディレクトリをvolumes
に指定してあげる必要があります。
main.py
はSeleniumのキャプチャ方法として調べるとたくさん出てきますが、今回はこのように作成します。
import os
from selenium import webdriver
def main():
image_save_path = os.path.join('images', 'image.png')
text = 'Hello'
captcha(image_save_path, text)
def captcha(image_save_path: str, text: str) -> None:
url = f'http://server:5000/?text={text}'
options = webdriver.ChromeOptions()
options.add_argument('--headless')
options.add_argument('--no-sandbox')
driver = webdriver.Chrome(options=options)
driver.set_window_size(1920, 1080)
driver.execute_script("document.body.style.zoom='100%'")
driver.get(url)
driver.implicitly_wait(3)
driver.save_screenshot(image_save_path)
driver.quit()
if __name__ == "__main__":
main()
動かしてみる
docker-compose up
を入力してみて実際にimages
ディレクトリに画像が生成されていたら成功です。
ソースコード: https://github.com/yuto51942/image-generate
最後に
私が最近趣味で作ったEarthquake-alertにも同じようにして画像生成しているのでぜひ見てみてください。なんならStarも欲しいです。
他の方法でも画像を生成できるかもしれませんが、自分の中ではこの方法が一番自由に色々な画像を生成することができるのではと思います。ぜひ試してみてください。