はじめに
Seleniumで処理を自動化しました。
しばしば画像の読み込みが完了する前に、処理を始めてしまい例外が発生していました。
そこで画像の読み込みが完了するまで待機する処理を考えてみました。
環境
Python 3.7.3
selenium 3.141.0
Firefox: 67.0.4 (64 ビット)
geckodriver 0.24.0 ( 2019-01-28)
ソース01
JavaScript
画像の読み込みが完了したかどうかを判定する処理は、JavaScriptで実装しました。
以下のようになります。
ここでは、Document配下の全ての画像を対象にしています。
適宜、修正してください。
const isLoadedAllImages = () => {
const images = document.getElementsByTagName("img");
let completed = true;
for (const image of images) {
if (image.complete === false) {
completed = false;
break;
}
}
return completed;
}
return isLoadedAllImages()
Python
上で定義したJavaScriptをSelenium(Python)側から呼び出します。
単純な事ですが、重要なのは、画像が読み込まれるまでSelenium側でsleepをしてやることです。
理由は、
- JavaScriptには、単純なSleepがない
- JavaScript側がビジー状態になっていると画像の読み込みが完了しない
からです。
Sleepをしてくれるライブラリ等あればそれを使えば良いのでしょうが、それの設定をするのも面倒なのでこのような実装にしました。
JavaScriptIsLoadedAllImages = '''
const isLoadedAllImages = () => {
const images = document.getElementsByTagName("img");
let completed = true;
for (const image of images) {
if (image.complete === false) {
completed = false;
break;
}
}
return completed;
}
return isLoadedAllImages()
'''
def isLoadedAllImages(timeOut=300, interval=1):
completed = False
start = time.time()
while time.time() - start < timeOut and completed == False:
completed = driver.execute_script(JavaScriptIsLoadedAllImages)
time.sleep(interval)
return completed
ソース02
JavaScript
同じ関数を繰り返し定義しているためか挙動が不安定な事もあったので修正案です。
こちらはグローバル変数を変更するので注意が必要です。
関数定義
window.isLoadedAllImages = () => {
const images = document.getElementsByTagName("img");
let completed = true;
for (const image of images) {
if (image.complete === false) {
completed = false;
break;
}
}
return completed;
}
関数呼び出し
return isLoadedAllImages();
Python
JavaScriptIsLoadedAllImagesDefine = '''
window.isLoadedAllImages = () => {
const images = document.getElementsByTagName("img");
let completed = true;
for (const image of images) {
if (image.complete === false) {
completed = false;
break;
}
}
return completed;
}
'''
JavaScriptIsLoadedAllImagesCall = "return isLoadedAllImages();"
driver.execute_script(JavaScriptIsLoadedAllImagesDefine)
def isLoadedAllImages(timeOut=300, interval=1):
completed = False
start = time.time()
while time.time() - start < timeOut and completed == False:
completed = driver.execute_script(JavaScriptIsLoadedAllImagesCall)
time.sleep(interval)
return completed
最後に
単純なサイトではこれでうまく行くと思います。
実際には、アクセスしているサイトの挙動によってはうまく行かないことも出てくるかと思います。
余談ですが、Selenium等での自動化にはまると楽しいですね。
これを仕事や収入に結び付ければ良いのでしょうが。