【初心者向け】Dockerでselenium・streamlit環境の構築方法を具体例を使って解説します

dockerを使ったselenium環境とstreamlit環境の構築方法を解説していきます

この記事を読むとわかること・・

  • dockerを使ったselenium環境の構築方法
  • dockerを使ったstreamlit環境の構築方法
  • vncの使い方
selenium:webアプリケーションのブラウザ操作を自動で行うことができるフレームワーク
streamlit:webアプリケーションを簡単に作成することができるフレームワーク

VNC  :パソコンを離れたところから操作するときに使用するソフト

おへんじ
seleniumを使うとwebページからデータ取得などができるよ

dockerのインストール方法が知りたい方はこちら

dockerの基本的な使い方やdocker-compose.ymlの使い方が知りたい方はこちら


環境

解説をする前に私が使用している環境を書いておきます

  • MacBook Pro : (13-inch, 2020, Four Thunderbolt 3 ports)
  • プロセッサ:2 GHz クアッドコアIntel Core i5
  • メモリ:16GB
  • docker version:20.10.17
  • python version:3.9.5
おへんじ
Dockerのバージョンはターミナル上で『docker version』か『docker -v』で確認できるよ

dockerでの環境構築方法についてざっくり解説

今回実装する内容をざっくり説明すると・・・

docker上でseleniumを使ってスクレイピングした内容を

streamlitでweb上に公開するって感じです

今回は実装するために2つのdockerコンテナを作成します

用意するコンテナは以下の2つ

  1. seleniumを使用するためのコンテナ(docker-seleniumを使用)
  2. streamlitを使用するためのコンテナ(自分で作成)

1つ目はdocker-seleniumのdockerイメージを使って構築します

詳細はgithubを確認してください

おへんじ
docker-seleniumはdockerhubにイメージが公開されているよ

docker-seleniumのイメージには

  • どのブラウザを使用するのか(chrome, edge, firefox)
  • どのOSで使用するのか
  • デバック機能を必要とするのか

など用途によって使用するイメージが変わります

私の場合は、

デバック(スクレイピングができているのか確認作業)が

したかったので『standalone-chrome-debug』の

dockerイメージを利用しました

【豆知識】
『standalone-chrome-debug』のイメージを使用するとVNCも使用できます

VNCというのは離れたところからパソコンを

遠隔操作することができるソフトのことですが、

これを使用するとseleniumでブラウザ操作をする様子が

確認できるので、エラーが生じた際にも原因を特定することが簡単になります

VNCを使用すると以下のような感じでスクレイピングをしている様子がわかります

↑VNC起動画面

スクレイピングしているwebページはこちらです(netkeiba.com)

デバックが必要ない方は『standalone-chrome』イメージで十分です

2つ目のコンテナはstreamlitを使えるようにするためのコンテナです

おへんじ
こっちは自作だから後で詳しく説明するね

フォルダ・ファイル構成

今回はdockerを使ってstreamlit環境と

seleniumによるスクレイピングができる環境を構築していきます

細かい説明をする前に今回使用するフォルダ構成とファイルを載せておきます

フォルダ構成

root
 ├── requirements.txt  # インストールするパッケージを記載
 └── docker-compose.yml
 └── sample-streamlit
     └── Dockerfile
     └── streamlit-app
         └── app.py

docker-compose.yml

version: '3'
services:
  selenium:
    container_name: selenium-chrome
    image: selenium/standalone-chrome-debug:3.141.59-20210929
    ports:
      - 4444:4444
      - 5900:5900
    shm_size: "2gb"
  sample-streamlit:
    container_name: sample-streamlit-server
    build:
      context: .
      dockerfile: ./sample-streamlit/Dockerfile
    ports:
      - 8501:8501
    image: sample-streamlit:1.0
    tty: true

2つのコンテナの設定をそれぞれ記載しています

4444 :seleniumのポート番号

5900:VNCのポート番号

8501:streamlitのポート番号

Dockerfile

# ベースイメージ
FROM python:3.8

# 環境変数を設定
ENV PYTHONIOENCODING utf-8

# app/ディレクトリを作成
WORKDIR /app

# パッケージをインストールする準備
RUN apt update
RUN apt install -y
RUN apt install -y python3-pip
RUN pip install --upgrade pip

# requirements.txtをコンテナ側にコピー
COPY requirements.txt .

# requirements.txtに記載されたパッケージをインストールする
RUN python3.8 -m pip install -r requirements.txt

# ローカル側のstreamlit-appディレクトリ配下にあるファイルをコンテナ側のappディレクトリにコピー
COPY ./sample-streamlit/streamlit-app/ /app

8501はstreamlitのポート番号です

docker-compose.ymlに記載しているstreamlit用のポート番号を

コンテナ側にも渡しておく必要があるため記載しています

streamlit公式にdockerを使用したstreamlitの使い方が記載されています

公式に8501のポート番号を使用すると記載されています

requirements.txt

streamlit
webdriver-manager
selenium

例ではパッケージのバージョンは指定していませんが

バージョン名の後にバージョンを指定することができます

バージョンを指定しない場合は最新のバージョンがインストールされます

app.py

# coding:utf-8

# 必要なパッケージのインストール
from selenium import webdriver
from selenium.webdriver import ChromeOptions, Chrome
from selenium.webdriver import Chrome
from selenium.webdriver import ChromeOptions
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.by import By
import streamlit as st
import os
import sys
import pickle

# pathの設定(スクレイピングしてきたデータを保存するディレクトリpathを取得)
FILE_PATH_FIT_DATA = '/'.join(os.path.abspath(__file__).split('/')[:-1])
sys.path.append(FILE_PATH_FIT_DATA)


def load_pickle(file_path, file_name):
    """
    pickleファイルをloadする関数
    """
    with open("{}/{}".format(file_path, file_name), 'rb') as f:
        return pickle.load(f)


def save_pickle(file_path, file_name, var_name):
    """
    pickleファイルをsaveする関数
    """
    with open("{}/{}".format(file_path, file_name), 'wb') as f:
        pickle.dump(var_name, f)


def main():

    # "is_horse_name_view"のkeyが存在しない場合に実行する(=1度しか実行されない)
    # st.sessionstateに格納する変数は再読み込みされても保持される
    if "is_horse_name_view" not in st.session_state:
        st.session_state["is_horse_name_view"] = None

    # ボタンを押すとスクレイピングを開始する
    press_button = st.button("スクレイピング開始")

    # ボタンが押されたときに実行される
    if press_button:

        # スクレイピングした馬名を表示する信号をONにする
        st.session_state["is_horse_name_view"] = True

        # スクレイピングをするためのoptionsを設定(今回は特に設定はなし)
        options = ChromeOptions()

        # ドライバーを作成
        driver = webdriver.Remote(command_executor="http://selenium:4444/wd/hub", options=options)

        # サイトに接続
        url = "https://race.netkeiba.com/race/result.html?race_id=202206040401&rf=race_list"
        driver.get(url)

        # サイトの馬名が記載されている element を取得する
        horse_name_class_list = driver.find_elements(By.CLASS_NAME, "Horse_Name")

        # 馬名を抽出してlistに格納する
        horse_name_list = [horse_name.text for horse_name in horse_name_class_list]

        # 取得した馬名を格納したlistをpickleファイル形式で保存
        save_pickle(FILE_PATH_FIT_DATA, "horse_name_list.pickle", horse_name_list)

        # ブラウザを閉じる
        driver.close()

    # st.session_stateを定義しておくことでページが更新されても
    # スクレイピング開始ボタンが押されない限り馬名のradioは表示され続ける
    if st.session_state["is_horse_name_view"]:
        # 上で保存した馬名listを読み込む
        horse_name_list = load_pickle(FILE_PATH_FIT_DATA, "horse_name_list.pickle")
        # 取得した馬名をradioで表示する
        st.radio('出場する馬の一覧', (horse_name_list))


if __name__ == '__main__':
    main()

長くてすみません・・・

やっていることは単純です

app.pyの内容を簡単に説明すると・・・

  1. netkeiba.comの競馬情報サイトのある日のレースに出場する馬名を取得(selenium)
  2. 取得した馬名をpickleファイルに保存
  3. Webページ上に馬名を表示する(streamlit)

といった内容になっています

↑今回スクレイピングするwebページ(netkeiba.com)

今回は実用的なファイルではなく

とりあえずseleniumでスクレイピングして

その結果をstreamlitで作成したwebページに表示する

ってことを実装しただけのファイルになります

ローカルとdockerでseleniumを使用するときの違い

ローカルとdockerでseleniumを使用するときは

以下の違いがあるので注意してください

ローカルでseleniumを使用する場合

driver = webdriver.Chrome(ChromeDriverManager().install(), options=options)

docker上でseleniumを使用する場合

driver = webdriver.Remote(command_executor="http://selenium:4444/wd/hub", options=options)

ファイル構成は以上になります


コンテナ作成手順

今回はdocker-compose.ymlでdockerイメージ・dockerコンテナを

作成していきます

docker-compose.ymlの使い方がわからない方はこちらを参考にしてください

ディレクトリの移動

まず、ターミナルを起動してrootディレクトリ(docker-compose.ymlが直下にあるディレクトリ)まで移動します

$ pwd  # 現在のディレクトリの確認
(実行結果)
/Users/username/root  # rootディレクトリにいることを確認

dockerイメージの作成

$ docker-compose build
(実行結果)
略(いろいろインストールしてるよーって表示される)

最初にbuildするときはかなりの時間がかかるので気長に待ちましょう

私の場合は2分程度かかりました

dockerコンテナの起動

$ docker-compose up -d
(実行結果)
 ⠿ Container sample-streamlit-server  Started
 ⠿ Container selenium-chrome          Started

dockerイメージの確認

REPOSITORY                         TAG                 IMAGE ID       CREATED         SIZE
sample-streamlit                   1.0                 e31b70e88d56   3 minutes ago   1.43GB
selenium/standalone-chrome-debug   3.141.59-20210929   aaf43463c572   12 months ago   1.11GB

dockerコンテナの確認

CONTAINER ID   IMAGE                                                COMMAND                  CREATED         STATUS         PORTS                                            NAMES
2319cddf970c   selenium/standalone-chrome-debug:3.141.59-20210929   "/opt/bin/entry_poin…"   3 minutes ago   Up 3 minutes   0.0.0.0:4444->4444/tcp, 0.0.0.0:5900->5900/tcp   selenium-chrome
da6c20a2ba64   sample-streamlit:1.0                                 "python3"                3 minutes ago   Up 3 minutes   0.0.0.0:8501->8501/tcp                           sample-streamlit-server

イメージ・コンテナが作成されているのがわかります

コンテナの中に入る

$ docker-compose exec sample-streamlit bash

(使い方)
docker-compose exec [イメージ名] bash

(実行結果)
root@da6c20a2ba64:/app# ←ターミナルの表記が切り替わるとコンテナの中に入ったことがわかる

コンテナ内のプログラムの実行

root@da6c20a2ba64:/app streamlit run app.py

(使い方)
streamlit run [ファイル名] → ファイル名で指定したファイルがstreamlit上で実行される

(実行結果)
You can now view your Streamlit app in your browser.

  Network URL: http://172.18.0.2:8501
  External URL: http://58.183.119.78:8501

safariかchromeを開いて

http://localhost:8501で検索をすると

app.pyに記載した内容がwebページ上に表示されます

↑app.pyを実行して表示されたwebページ

『スクレイピング開始』ボタンを押すと・・・

スクレイピングが開始されて

競馬サイトのある日のレースに出馬する馬名が表示さます

これが表示されたらseleniumによるスクレイピングができたことがわかります

streamlitの終了

ターミナル上で『Control』+『c』で終了できます

コンテナを抜ける

ターミナル上で『Contorol』+『q』+『p』を同時に押すことで抜けることができます

コンテナを停止

$ docker stop selenium-chrome
(実行結果)
selenium-chrome

$ docker stop sample-streamlit-server
(実行結果)
sample-streamlit-server

コンテナを停止・削除

docker-compose.ymlを使用している場合は

下のコマンドでコンテナの停止・削除が同時にできます

# コンテナ停止・削除
$ docker-compose down

コンテナ・イメージの削除

# コンテナを削除
$ docker rm [コンテナ名]  # コンテナ名を指定してコンテナを削除
(実行結果)
[コンテナ名]

# イメージを削除
# docker rmi [イメージ名]  # イメージ名を指定してイメージを削除
(実行結果)
[イメージ名]

それぞれ不要なコンテナ・イメージを削除してください

これでdockerを使用したselenium環境・streamlit環境を実装することができました


VNCの使い方

最後にvncの使い方を説明しておきます

vncを使用することで、seleniumでスクレイピングしているときの状況を

確認することができます

ローカルでseleniumでスクレイピングする場合

『headless』オプションをつけていなければ

スクレイピング状況が確認できます

今回のようにdockerを使ってstreamlit上でスクレイピングする場合は

vncを使わなければスクレイピング状況が確認できません

それではvncの起動方法を解説します

今回はMacでのVNCの使い方を解説するので

windowsの方は下のページでわかりやすく解説している方が

おられましたので、そちらを参考にしてみてください

Macの場合はVNCは標準でインストールされています

Finderを開く

↑Finder起動画面

まずFinderを開きます

左上の『移動』から『サーバーへ接続』を選択します

サーバー接続

↑サーバー接続画面

接続するサーバーを選択する画面が表示されるので

『vnc://localhost:5900』と入力します

このとき『5900』はdocker-compose.ymlの

ポートフォアディングで設定する数字

同じ値にしておく必要があります

今回の場合はdocker-compose.ymlファイルで設定した

『5900』としています

サーバーを入力後に『接続』を選択します

パスワードの入力

↑VNCパスワード入力画面

次にパスワードを入力する画面が表示されるので

パスワードを入力します

おへんじ
初期設定のパスワードは『secret』だよ

パスワード入力後に『サインイン』を選択します

VNCの起動

↑VNC起動画面

VNCが起動します

↑VNC起動画面

あとは、dockerコンテナを起動してapp.pyを実行し

スクレイピングを実行するとVNC画面にスクレイピングしている

様子が表示されて確認をすることができます

Macには別途VNCをインストールする必要がないため

簡単な手順でスクレイピング状況の確認をすることができます

これでdockerを使用してスクレイピングとstreamlitが

使用できるようになりましたので

皆さんも自分の作りたいwebアプリをサクッと作ってみてください

ohenziblogはプログラミングを独学で始めるための徹底ガイドを目指しています

最新情報をチェックしよう!