PythonのSeleniumでスクレイピングを行った際、通常、待機処理を入れます。
待機処理として、timeモジュールのsleep関数やWebDriverクラスのimplicitly_waitメソッドを使用する方法があり、またベストプラクティスとして、WebDriverWaitクラスを使って特定の条件(要素が表示される、URLが変更されるなど)を満たすまで待機する方法も多くのWebサイトで紹介されています。
待機処理する際にJavaScriptの処理の完了まで待機しないとうまくいかなかったサイトがあったので、その時の対処方法を本記事で説明します。
背景
モジュールのインポートやWebDriverのインスタンス化のコードは省略しますが、私は基本的に以下のようなコードで待機処理を行っています。
driver.get('https://webscraper.blog/')
element = WebDriverWait(driver, 30).until(EC.presence_of_element_located((By.CSS_SELECTOR, 'h1')))簡単に説明するとdriver.getの引数に指定したURLへ移動し、h1タグがDOM上に現れるまで待機し、現れると変数elementにそのWebElementを代入しています。WebDriverWaitの第2引数はtimeoutの値であり、30で指定すると30秒現れないとエラーが発生します。
ほとんどのサイトはこのような待機処理を行うことで取得したいWebElementをターゲットに待機を行うことができ、現れた時点で次の処理を行うことができるので効率の良い待機処理を行うことができます。
しかし、次のような仕様のサイトがあり、何か良い方法がないかと模索しました。そのサイトは検索結果の件数がまず0を表示し、そのあとJavaScriptで正しい件数に書き換えるような仕様となっていました。その場合、とりあえず以下のようなコードで対応しました。
driver.get('https://xxxxxxxxxx/')
element = WebDriverWait(driver, 30).until(EC.presence_of_element_located((By.CSS_SELECTOR, '#count')))
time.sleep(5) # 追加
count = int(element.text)IDがcountのWebElementが現れるまで待機し、そのあと一定時間待機するというコードです。これでとりあえず対応はできましたが、件数が多くなるとJavaScriptの処理時間が長くなり、正しい件数が取得できない可能性があることと逆にJavaScriptの処理時間が短い場合に無駄な待機時間が発生するというデメリットが残っていました。

JavaScriptのdocument.readyStateプロパティで判定する
いろいろ調べてみると以下のコードで行うことがベストかなと思いました。
driver.get('https://webscraper.blog/')
WebDriverWait(driver, 30).until(
lambda d: d.execute_script('return document.readyState') == 'complete'
)
element = driver.find_element(By.CSS_SELECTOR, '#count')
count = int(element.text)JavaScriptのdocument.readyStateプロパティがcompleteとなるまで待機する方法です。document.readyStateプロパティの解説は以下のURLにあります。
https://developer.mozilla.org/ja/docs/Web/API/Document/readyState
untilの引数にlambda d: d.execute_script(‘return document.readyState’) == ‘complete’を渡すことでJavaScriptの処理が完了するまで待機することができました。その後、IDがcountのWebElementを取得し、そのテキストを取得することで検索件数を取得しています。


コメント