Docker-Selenium を使って印刷プレビューを介しての PDF 保存をやってみた
Docker-Selenium を使って印刷プレビューを介しての PDF 保存をやってみた背景
会社で利用している監視サービスで一々 Web ブラウザを使って監視対象分のサーバーの監視レポートを毎月ダウンロードして顧客に提出ということをしてたので、今まで興味はあったけど手が出せてなかった Python と Selenium に入門したついでにこの課題を解決してみたのですが、思った以上に詰まったので内容をメモします。
Docker-Selenium を使って印刷プレビューを介しての PDF 保存を自動化する前の手作業
- ログイン画面
- id, pass を入力してログインする
- ダッシュボード画面
- 監視対象のサーバーの一覧が表示されている
- サーバー名をクリックして詳細画面に遷移する
- 詳細画面
- 月毎の監視レポートのリンクの一覧が降順表示されている
- 一覧上の最新の月のリンクをクリックして監視レポート画面に遷移する
- 監視レポート画面
- 印刷ボタンがあるので、それをクリックして印刷プレビューを介して PDF を保存
- 2〜4 をサーバーの数だけ繰り返す
ファイル構成
ディレクト構成
Copied!
workspace
└─ dev
└─ shm ← ファイルの保存先
└─ docker-compose.yml ← docker-selenium用のdocker-composeファイル
└─ main.py ← Selenium実行ファイル
Mac に Selenium をインストール
Copied!
pip3 install selenium
Selenium Grid の準備
オーバースペックみたいですが Selenium Grid を使ってみます。
利用するイメージは selenium/standalone-chrome
です。
docker-compose.yml
Copied!
version: '3'
services:
chrome:
image: selenium/standalone-chrome
ports:
- 4444:4444
volumes:
- $PWD/dev/shm:/dev/shm
Docker-Selenium を使って印刷プレビューを介しての PDF 保存処理の作成
main.py
Copied!
import sys
import time
from selenium import webdriver
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
def search_and_download(driver):
driver.get('https://hogehoge/ligin')
# idとパスワードを入力してログイン
id = driver.find_element_by_id("id_username")
id.send_keys("ID")
password = driver.find_element_by_id("id_password")
password.send_keys("パスワード")
login_button = driver.find_element_by_xpath('//*[@type="submit"]')
login_button.click()
# ダッシュボード画面で監視対象のサーバーの詳細画面へのリンクを全て取得し、アドレスだけ別変数に退避
list_items = driver.find_elements_by_xpath('//a[starts-with(@href,"/reports")]')
links = [list_item.get_attribute('href') for list_item in list_items]
for link in links:
try:
# 詳細画面へ遷移し、最新の監視レポートリンクを取得
driver.get(link)
detail = driver.find_element_by_xpath('//a[starts-with(@href,"/reports/report/detail/")]')
# 監視レポート画面へ遷移し、印刷ボタンをクリックすると自動でPDF保存されます
driver.get(detail.get_attribute("href"))
print_button = driver.find_element_by_xpath('//button[starts-with(@onclick,"window")]')
print_button.click()
time.sleep(3)
except:
print('監視レポートなし')
continue
if __name__ == '__main__':
query = ' '.join(sys.argv[1:])
caps = DesiredCapabilities.CHROME.copy()
caps['chromeOptions'] = {
'args': [
'--kiosk-printing',
],
'prefs': {
'savefile': {
'default_directory': '/dev/shm'
},
'printing': {
'print_preview_sticky_settings':{
'appState': {
'recentDestinations': [{
'id': 'Save as PDF',
'origin': 'local',
'account': '',
}],
'selectedDestinationId': 'Save as PDF',
'version': 2
}
}
}
}
}
options = {
'command_executor': 'http://localhost:4444/wd/hub',
'desired_capabilities': caps
}
with webdriver.Remote(**options) as driver:
driver.implicitly_wait(1)
search_and_download(driver)
Docker-Selenium を使って印刷プレビューを介しての PDF 保存処理の内容
Copied!
caps['chromeOptions'] = {
'args': [
'--kiosk-printing',
],
'prefs': {
'savefile': {
'default_directory': '/dev/shm'
},
'printing': {
'print_preview_sticky_settings':{
'appState': {
'recentDestinations': [{
'id': 'Save as PDF',
'origin': 'local',
'account': '',
}],
'selectedDestinationId': 'Save as PDF',
'version': 2
}
}
}
}
}
chromeOptions
で chrome の webdriver の細かな設定を行えます。
-
--kiosk-printing
- このオプションを指定すると、window.print()を実行し印刷しようとすると、印刷プレビューがすぐに閉じられ、Chrome に事前に設定した設定が自動的に使用されます。
- 因みにこのオプションは
--headless
とは併用できないようです。その為、--headless
をつけると PDF がダウンロードされませんでした。
-
prefs.savefile.default_directory
- 今回一番はまったところです。印刷プレビューを介したファイル保存で使用するファイル保存先の設定です。selenium でファイル保存関連を検索すると
download.default_directory
といったdownload
を使用した記事が多いのですが、それでは保存できませんでした。やっとたどり着いたのがこのプロパティで無事保存ができました。
- 今回一番はまったところです。印刷プレビューを介したファイル保存で使用するファイル保存先の設定です。selenium でファイル保存関連を検索すると
-
printing
- このオプションで印刷プレビューの設定ができます。今回は PDF での保存が目的なのでそれを選択するようにしています。
Docker-Selenium を使って印刷プレビューを介しての PDF 保存をやってみた まとめ
目的は達成しましたがまだまだわからないことだらけでした。
本当は main 処理の最後に後始末として driver.quit()
を入れたのですが、
selenium.common.exceptions.WebDriverException: Message: No active session with ID ~
というエラーが出て結局解決できませんでした。
あと作ってる最中ずっと、公式リファレンスはどこかにないの?と思っていました。
特に ChromeOptions
の savefile
は色々ググってやっと見つけたのが個人ブログでした。
その後、ChromeOptions savefile
でぐぐりましたが全く情報がなく、
savefile
といったプロパティの存在はを知るにはどうしたらいいのかと今でも悶々としています。
個人開発したサービス
個人開発したサービス
目次
個人開発したサービス