Seleniumによるブラウザの自動化をすることでスクレイピングが可能となります。今回は、スクレイピングした結果を使うようなwebサイトをFlaskで作成し、Rendorで公開するまでの流れを紹介します。
Rendorとは
Rendorとは、様々なwebアプリをデプロイするためのPaaSであり、Herokuのようなサービスを展開しています。 Rendor上では、Githubと連携することで特定のブランチにプッシュされたときのみアプリをデプロイするなど、自動デプロイが可能となります。
公式サイトが出している料金比較にもあるように、有料化したHerokuと比べても比較的安価に運用ができるため、さくっとサービスを公開したいときに適しているのではないかと思います。
ただし、Rendorには注意点もあり、docker composeをサポートしてはいません。その代わり、render.yaml
という独自のYAMLファイルを書く必要があります。今回は、docker composeを使って開発したFlaskアプリを render.yaml
に置き換えてデプロイするまでを書いていきます。
Flask × Seleniumのアプリのローカル起動まで
今回は以下の記事をベースとした Flask x Seleniumのwebアプリを使います。
ローカルで実行する際には、Python用のコンテナとSelenium用のコンテナをdocker composeで同時にビルドし、起動するような構成となります。
ディレクトリ構造
以下のディレクトリ構造のプロジェクトで進めていきます。
. ├── Dockerfile ├── app │ ├── __init__.py │ ├── app.py │ ├── driver.py │ └── templates │ ├── index.html │ └── result.html ├── docker-compose.yml └── requirements.txt
docker-compose.yml
docker composeは以下のようになっています。
version: '3' services: selenium-worker: image: selenium/standalone-chrome:latest shm_size: 2gb ports: - 4444:4444 - 7900:7900 python: build: . volumes: - ./app:/app tty: true depends_on: - selenium-worker ports: - 5000:5000 environment: - SELENIUM_WORKER_HOST=selenium-worker
Dockerfile
Flask用のDockerfileは以下のようになっており、5000番ポートでリクエストを受ける形となります。
FROM python:latest RUN apt-get update && \ apt-get install -y \ build-essential \ cmake \ git \ sudo \ wget \ vim RUN pip install --upgrade pip COPY ./requirements.txt /requirements.txt RUN pip install -r /requirements.txt WORKDIR /app COPY ./app . ENV FLASK_APP=/app/app.py EXPOSE 5000 CMD ["gunicorn", "--bind", "0.0.0.0:5000", "app:app"]
app/app.py
app.pyはFlaskアプリの実装しており、大まかな実装は以下となります。
from flask import Flask, render_template, request from driver import init_driver, finish_driver app = Flask(__name__) @app.route("/") def index(): return render_template("index.html") @app.route("/scrape") def scrape(): ... driver = init_driver() # スクレイピング処理 finish_driver(driver) return render_template("result.html", ...) if __name__ == '__main__': app.run()
app/driver.py
driver.pyはChromeのリモートweb driverを初期化、終了するのに使われます。これらはapp.pyで呼び出されています。
import os from selenium import webdriver def init_driver(): options = webdriver.ChromeOptions() ... driver = webdriver.Remote( command_executor='http://{}:4444/wd/hub'.format(os.environ['SELENIUM_WORKER_HOST']), options=options ) driver.set_window_size(500, 500) driver.implicitly_wait(10) return driver def finish_driver(driver): driver.quit()
ローカル起動
この構成のもと、docker-compose build
から docker-compose up
をすると、localhost:5000
からアプリにアクセスでき、適当なAPIを叩くことでスクレイピングした結果を返すことができます。ここまでが今回の記事で扱うwebアプリの実装となります。
Renderへのデプロイ
ここからは本題である、Renderへのデプロイに移っていきます。
最初にも書きましたが、Renderでは docker-compose.yml
をサポートしていません。
その代わり、独自の render.yaml
というものがあり、これを使ってデプロイすることになります。
Dockerfile.selenium
と render.yaml
の追加
render.yaml
を使った形に変更するにあたって、先ほどのプロジェクトに新たに Dockerfile.selenium
と render.yaml
を追加します。
render.yaml
は先ほどから出てきているように、docker-compose.yml
の代わりのファイルであり、Dockerfile.selenium
はSelenium standaloneのイメージを利用するためのDockerfileとなります。
docker-compose.yml
と違って、 render.yaml
ではイメージを指定したりすることができません。そのため、サービスごとに Dockerfile
を作って、それぞれに設定を追加していく必要があります。最終的なディレクトリ構造は以下となります。
. ├── Dockerfile ├── Dockerfile.selenium ├── app │ ├── __init__.py │ ├── app.py │ ├── driver.py │ └── templates │ ├── index.html │ └── result.html ├── docker-compose.yml ├── render.yaml └── requirements.txt
Dockerfile.selenium
Dockerfile.selenium
の中身は以下となります。docker-compose.yml
の中身を書き換えただけなので、非常にシンプルになります。
FROM selenium/standalone-chrome:latest EXPOSE 4444 EXPOSE 7900
render.yaml
次に、render.yaml
は以下となります。
services: - type: pserv name: selenium-worker runtime: docker dockerfilePath: ./Dockerfile.selenium rootDir: . envVars: - key: PORT value: 4444 - type: web name: flask runtime: docker dockerfilePath: ./Dockerfile rootDir: . envVars: - key: FLASK_APP value: /app/app.py - key: SELENIUM_WORKER_HOST value: ***
render.yaml
では、各サービスごとにtypeを設定することができ、selenium-workerでは pserv
、flaskでは web
となっています。
公式サイトによると、それぞれ以下のようなサービスとなっています。
- web:いわゆるwebサービス。外部からのアクセスができる。
- pserv (private service):外部からのアクセスができないサービス。Renderにデプロイされたwebサービスからはアクセスが可能だが、ブラウザなどの外部からはアクセスできない。
今回のwebアプリではselenium-workerはflaskからのみアクセスされるため、pserv
としています。
ファイル下部のSELENIUM_WORKER_HOST
という環境変数はこの後設定していくため、一旦適当な文字にしておいてください。
Render上での操作
次に、Renderのダッシュボードでの操作に移ります。
Step 1 Blueprintを選択
Step 2 レポジトリを選択
GitHubと連携すると、デプロイしたいレポジトリが選択できます。Connectを押して連携します。
Step 3 プロジェクト名、ブランチの選択
適宜好きな名前を入力しましょう。ここで、下の方に flask-mn4b
と selenium-worker-mn4b
というサービス名が表示されていることに注目してください。これらが各サービスの名前となっていて、例えばwebサービスはhttps://flask-mn4b.onrender.com
というURLでアクセスすることができるようになります。
Step 4 render.yaml
の再編集
ここまで来れば気づくかもしれませんが、先ほど省略した SELENIUM_WORKER_HOST
という環境変数の値には selenium-worker-mn4b
を入れることになります。
ローカルのときには、selenium-workerへは http://selenium-worker:4444/...
にアクセスすればよかったのですが、Render上ではURLが変わり、http://selenium-worker-mn4b:4444/...
にアクセスするということになります。
結果、render.yaml
は以下のようになります。
各サービスの名前はランダムにつけられるようで、デプロイするまで分かりません。 そのため、一回失敗すること前提でRender上でデプロイし、生成された名前をコピペして環境変数に入れ、再度変更をプッシュすることでしかデプロイできないのかもしれません。
services: - type: pserv name: selenium-worker runtime: docker dockerfilePath: ./Dockerfile.selenium rootDir: . envVars: - key: PORT value: 4444 - type: web name: flask runtime: docker dockerfilePath: ./Dockerfile rootDir: . envVars: - key: FLASK_APP value: /app/app.py - key: SELENIUM_WORKER_HOST value: selenium-worker-mn4b
Step 5 デプロイ結果
修正した render.yaml
をプッシュした結果、デプロイが成功することが確認できました。
まとめ
今回はFlaskとSeleniumを使ったwebアプリをDockerを使ってRenderにデプロイする方法を紹介しました。 Renderは初めて使ってみたのですが、自動デプロイは高速で簡単なので、インフラ周りを触ることなくデプロイができ非常に便利だと思いました。
一方、docker composeには対応しておらず、render.yaml
という独自のyamlファイルを書く必要があるため、不便さもありました。
docker composeを使えるようにしてくれという声は昔からたくさんあるようなのですが、いまだに対応していない様子を見ると、今後もdocker composeがサポートされることは期待できないです。
また、docker-compose.yml
ではselenium-workerにshm_size: 2gb
を設定することでメモリ不足になることを防いでいたのですが、render.yaml
ではshm_size
を設定することはできないため、デプロイはできてもメモリ不足に陥ってしまうことが多いと思います。
今後 shm_size
をRenderがサポートするのを期待したいです。