seleniumを使ったスクレイピングは非常に便利ですが、毎日定期実行するとなると必要になるのは実行環境。
手元のパソコンで動かしても良いんですが、やはりパソコンは電源を切ることもあるしあまりローカルの環境汚染もしたくない。
そこで思い立ったのがSynologyのNAS上でプログラムを毎日自動実行することでした。
色々試行錯誤したんですが、DSM(SynologyのNAS上で動くLinuxベースのOS)ではyumやdnf、apt等のインストールコマンドが使用できず主にChromeのインストールに難航。
続けて思案していたらパッケージの中にDockerを発見し、「あれ?コンテナ建てればよくね?」と試してみたら無事成功しました。
この記事ではSynologyのNAS上でDockerを起動させ、Pythonコンテナを立ち上げselenium実行に必要な各種ソフトウェアのインストールを行い定期実行する方法を解説していきます。
- SynologyのNAS上でDocker環境の構築ができる
- SynologyのNAS上でPython+Seleniumを使ったスクレイピング方法がわかる
- SynologyのNAS上でタイムスケジューラを使って定期実行する方法がわかる
SynologyのNAS上でDockerを動かすための前提環境
まず必要な前提環境です、残念ながら全てのSynology製のNASで実行できる訳ではありません。
SynologyのNASには入門機とも言えるJシリーズ、オールラウンドのValueシリーズ、家庭用の最上位モデルであるPlusシリーズがありますがDockerのインストールが行えるのはPlusシリーズのみです。
非公式にDockerをインストールする方法もあるようですが、責任を負えないため基本的にPlusシリーズを前提にお願い致します。
ちなみに僕はDS216j、DS218jと使用して現在はDS720+を愛用しています。
JシリーズやValueシリーズに比べ価格は上がりますが、普通に使っていても処理速度が段違いなので奮発してでもPlusシリーズにすることをおすすめします!
価格を抑えたいのであれば同じくPlusシリーズのDS220+もおすすめです。
ハードディスクはお馴染み、WesternDigitalの赤一択です。
SynologyのNAS上にDockerをインストールしSSH接続できるようにする
まずはSynologyへのDockerインストールが必要になります。
これはとても簡単です、パッケージセンターでDockerと検索してインストールしておいてください。
これだけでDockerコマンドが使えるので特に設定等は必要ありません。
SSH接続の設定
Synologyのパッケージセンターを通してインストールしたDockerはGUIでも操作できますが、ここではコマンド操作が前提なのでSSH接続できるようにしておきます。
コントロールパネルの「端末とSNMP」タブ内でSSHを有効化しポートを適当に設定しておきます。
これで以下のコマンドでSSH接続できるはずです。
$ ssh <username>@<IP Address> -p <Port No.>
例)ユーザー名「tarou」、IPアドレス「192.168.1.10」、ポート番号「59876」の場合
$ ssh tarou@192.168.1.10 -p 59876
パスワードを求められるので入力してEnterすればシェルが表示されます。
Dockerをインストール済みであれば以下のコマンドでバージョンが返ってきます、返ってこない場合はインストールをしておきましょう。
$ docker -v
Docker version 20.10.3, build b455053
Dockerfileとテストファイルの準備
環境が整ったので、Dockerfile等の必要なファイルの準備を行います。
必要なファイル群のディレクトリ構成は以下の通りです。
python-selenium-test(ルートディレクトリ)/
├ Dockerfile
├ src/
│ ├ img/
│ ├ main.py
│ ├ requirements.txt
│ └ date.txt
└ start.sh
実際のプログラム実行に必要なソース類(Pythonファイル等)はsrcディレクトリにおいておきます。
ファイル/ディレクトリ名 | 用途 |
---|---|
Dockerfile | dockerのレイヤ処理を記述したファイル |
srcディレクトリ | プログラム実行に必要なファイル・ディレクトリを入れておくディレクトリ |
imgディレクトリ | seleniumで撮影したスクリーンショットを格納するディレクトリ |
main.py | 実行されるPythonプログラム |
requirements.txt | pipインストール時に使うパッケージリスト |
date.txt | Pythonプログラム実行日時を出力するテキストファイル |
start.sh | Synologyタイムスケジューラで使うシェルスクリプト |
Dockerfile
使用するDockerfileは以下のように定義しました。
FROM python:3.10
WORKDIR /home/
COPY /src/ /home/
RUN useradd -r containeruser \
&& wget https://dl.google.com/linux/linux_signing_key.pub \
&& apt-key add linux_signing_key.pub \
&& 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 -f install -y google-chrome-stable fonts-ipafont fonts-ipaexfont python3-selenium \
&& pip3 install -r requirements.txt
CMD ["python3", "main.py"]
PythonにseleniumやChromedriverが同梱されているイメージも公開されているんですが、極力公式を使いたい(公開停止等のリスク回避)というのもあって公式のPythonイメージを使用しています。
RUNでフォントやselenium、Google Chromeのインストールを行い、COPYでDocker側に渡したrequirements.txtを基にpipでPythonパッケージのインストールを行っています。
ちなみにGoogle Chromeのインストールはまるっと以下のサイトを参考にしました、この場を借りてお礼申し上げます。
Dockerに自信が無いなら書籍と動画で学習しよう
Dockerについて詳しくない、Dockerfileって何?という方は以下の書籍が体系的に勉強できてとても良かったです!
プログラミングを行う上でGitと並んで外せない知識がDockerなのでぜひこの機会に触れて頂けると良いかと思います。
スキマ時間を有効活用するなら動画→書籍の順で学習しよう
動画学習サイトUdemyでDockerを学習するなら「ゼロからはじめる Dockerによるアプリケーション実行環境構築 」がおすすめです。
2万人以上の受講生がおり高評価を得ているベストセラー動画です、僕はこの「ゼロからはじめる Dockerによるアプリケーション実行環境構築 」で雰囲気を掴んでハンズオンで学習し、細かい部分を書籍で補完するという方法でDockerの学習を行いました。
- 動画をとりあえず全部見て頭の中に「なにか聞いたことあるな」という記憶をつける
- 書籍で記憶を基に詳細を学習する
この順番なら書籍を見るたびに「あっ!これ動画で見た!」という進研ゼミさながらの効率的な学習ができるので非常におすすめです!
main.py
Dockerコンテナ内で実行するPythonプログラムです。
今回はDockerの定期実行がきちんと行えているか確認するため、実行時刻をテキストファイルに書き出すというプログラムを作成しました。
seleniumの検証のため同時に現在時刻を表示してくれるTIME.ISさんにアクセスしスクリーンショットを撮影して、撮影時点の時刻のファイル名を付与して保存します。
また実際の実行時にも分かりやすいようにターミナル上にも現在時刻を出力しています。
import datetime
from selenium import webdriver
from selenium.webdriver.common.by import By
# アクセス先のURL
URL='https://time.is/ja/Tokyo'
# webdriverの設定
options = webdriver.ChromeOptions()
options.add_argument("--headless")
options.add_argument("--no-sandbox")
options.add_argument("--disable-dev-shm-usage")
options.add_argument("--disable-gpu")
driver = webdriver.Chrome(options=options)
# 現在時刻を取得して日本時間に変換
now = datetime.datetime.now() + datetime.timedelta(hours=9)
# try~finallyを用いて必ずchromeが終了するようにする
try:
# アクセス
driver.get(URL)
# TIME.ISのソース内に存在するtime_sectionがあれば処理を実行する
if driver.find_elements(By.ID, 'time_section'):
# 現在時刻を日本語表記でコンソールに出力
print(now.strftime('%Y年%m月%d日 %H:%M:%S'))
# date.txtを開いて現在時刻をstringで出力
with open('date.txt', mode='w', encoding='utf-8') as f:
f.write(str(now))
# スクリーンショットを撮影してimgディレクトリ下に現在日時.pngのファイル名で保存
driver.save_screenshot('img/' + str(now.strftime('%Y-%m-%d-%H-%M-%S')) + '.png')
finally:
# webdriverを終了
driver.quit()
TIME.ISさんのソースを見ると、time_section
というIDがあったのでこれがあれば正常にアクセスできているというif分岐を入れています。
実際に使用する場合は事前に対象のWebサイトのソースを見て対象のidやclassが存在するかどうかでチェックをすると良いと思います。
options.add_argument("--disable-dev-shm-usage")
options.add_argument("--disable-gpu")
webdriverの2つのOptionsですが、前者は仮想ディスクを/dev/shm
ではなく/tmp
に作成させるものです。
後者はGPUを無効化するOptionで、Dockerコンテナで動かす場合はこの2つを記述しないとseleniumがハングする事が多いので忘れずに指定しておきましょう。
requirements.txt
今回使用するパッケージはseleniumだけです。
selenium
SynologyのNAS上でDockerイメージのビルドと実行
準備が整ったのでビルドと実行テストを行います。
$ cd <Dockerfileがあるディレクトリ>
$ sudo docker image build -t python-selenium-test:latest .
これでビルドされます、パッケージのインストールや更新でかなり時間がかかるので気長に待ちましょう。
Successfully built 80f1a49608b8
Successfully tagged python-selenium-test:latest
こんな表示が出たらOK、続いて実行してみましょう。
$ sudo docker run --rm -it -v ${PWD}/src:/home python-selenium-test
2022年01月14日 00:15:52
現在時刻が表示されれば成功です!
date.txtの中身も見てみましょう。
2022-01-14 00:15:52.720053
実行時刻が出力されていれば大成功!
imgディレクトリの中にあるPNGファイル、ファイル名はdate.txtの日時になっており実際の画像にも撮影時点の日時が記録されています(datetime.nowで取得した時間と、seleniumでアクセスした時間に数秒の差はあります)。
Synology DSMのタスクスケジューラでPython×seleniumを定期実行する
最後にSynologyのDSM標準機能であるタスクスケジューラに先程のdocker runコマンドを定期実行するよう設定していきます。
シェルスクリプト作成
コマンド内で現在の階層をPWDコマンドで出力しているのでディレクトリ移動やsudoで動かすためのユーザーパスワードが必要なため超簡単なシェルスクリプトを書いて、シェルスクリプトからdocker runを動かすようにしたいと思います。
#!/bin/sh
# sudo権限でdockerを動かすためにSynologyユーザーのパスワードを記述
PASS=password
# Dockerfileがあるディレクトリを指定
cd /volume1/Work/Develop/docker/python-selenium/test
# sudo権限でdocker runコマンドを実行
echo $PASS | sudo -S docker run --rm -v ${PWD}/src:/home python-selenium-test
これをstart.sh等分かりやすい名前をつけてDockerfileがあるディレクトリに置いておきます。
注意点として、先程はdocker runコマンドに-it
オプションを指定していましたがシェルスクリプト上では外しています。
タスクスケジューラで自動実行する場合勝手に始まって勝手に終わるので、-it
オプションは必要ありません(というかつけたままだとエラーになります)。
main.pyを修正
コンソールを目にする機会はないので不要なprint文を削除しておきます。
最終的なPythonプログラムが以下の通りです。
import datetime
from selenium import webdriver
from selenium.webdriver.common.by import By
# アクセス先のURL
URL='https://time.is/ja/Tokyo'
# webdriverの設定
options = webdriver.ChromeOptions()
options.add_argument("--headless")
options.add_argument("--no-sandbox")
options.add_argument("--disable-dev-shm-usage")
options.add_argument("--disable-gpu")
driver = webdriver.Chrome(options=options)
# 現在時刻を取得して日本時間に変換
now = datetime.datetime.now() + datetime.timedelta(hours=9)
# try~finallyを用いて必ずchromeが終了するようにする
try:
# アクセス
driver.get(URL)
# TIME.ISのソース内に存在するtime_sectionがあれば処理を実行する
if driver.find_elements(By.ID, 'time_section'):
# date.txtを開いて現在時刻をstringで出力
with open('date.txt', mode='w', encoding='utf-8') as f:
f.write(str(now))
# スクリーンショットを撮影してimgディレクトリ下に現在日時.pngのファイル名で保存
driver.save_screenshot('img/' + str(now.strftime('%Y-%m-%d-%H-%M-%S')) + '.png')
finally:
# webdriverを終了
driver.quit()
タイムスケジューラに登録する
いよいよ最後の作業、Synologyのタイムスケジューラに作成したシェルスクリプトを読み込ませて行きます。
タスクスケジューラは「コントロールパネル」のサービス内にある「タスクスケジューラ」タブ/アイコンから設定可能です。
タスクスケジューラ上部の作成ボタンから「予約タスク」「ユーザー指定のスクリプト」という順でクリックして新しいタスクスケジュールを作成します。
まずはタスク名を入力します、今回は「Python-Selenium-Test」としておきました。
英数字とスペースのみ、最初と最後にスペースを使うことはできません。
実行ユーザーはシェルスクリプトにパスワードを書いたユーザー名を指定してください、パスワードが合わないとsudo権限が得られず永遠に失敗し続けてしまいます。
続いてスケジュールタブ、どの程度の頻度で実行して欲しいのかを指定します。
今回はテストなので5分おきにしてみました、言うまでもありませんがアクセス先がご自身のサイトではない場合過度なアクセスは控えましょう。
最後にタスク設定、ここでは実際に動かすコマンドを入力します。
今回はシェルスクリプトを動かすので以下のように入力します。
bash <シェルスクリプトのPATH>
僕の場合シェルスクリプトを/volume1/Work/Develop/docker/python-selenium/test/start.sh
に設置したので上記のようになっています。
実行結果をEメールで受け取りたい場合は「Eメールで詳細な実行情報を送信」にチェックを入れメールアドレスを入力しておきましょう。
「スクリプトが異常終了した場合にのみ詳細な実行情報を送信」にチェックを入れると正常に実行した場合は通知が来ないので、エラーだけが欲しい場合はここにもチェックを入れておきます。
余談ですが僕は普段使っている定期実行系のプログラムは全てSlackに結果を通知するようにしているので、そういう場合はチェックしない方が良いですね。
最後にOKをクリックして保存しておきましょう!
ログを保存したい場合
タスクスケジューラのログを保存したい場合、タスクを選択して設定ボタンをクリックします。
「出力結果を保存」にチェックを入れ保存先のディレクトリを指定すると、操作ボタンの「結果を表示」からログを確認することができます。
標準出力の内容も表示されるので、実行したPythonプログラムのエラーも表示されるのでNAS自体に空き容量がある場合は保存しておいても良いですね。
実行結果確認
それでは最後に実行結果を確認しておきます。
date.txtが定期的に更新されていることを確認します。
ばっちり5分おきにスクリーンショットが保存されていますね!
SynologyのNAS上でDockerを動かしてPython×seleniumのスクレイピングを定期実行する まとめ
Synology DSMはLinuxベースですが、全てのLinuxコマンドが使える訳ではありませんしyumやdnf、aptコマンドで手軽にパッケージがインストールできる訳でもありません。
しかしDockerが動くので、コンテナさえ建ててしまえばハードに依存しないものならなんでもできます。
NASという基本的に24時間365日稼働するシステムは定期実行と非常に相性が良いので利用しない手はないですよね!
ファイル保存や整理、バックアップのためだけにSynologyのNASを利用しているのはとてももったいないのでぜひこの機会に定期実行の母艦としても利用して頂けると同じSynology愛好家として嬉しいなと思います!
コメント