web-dev-qa-db-ja.com

ページをリロードせずにSelenium Webdriver DOMデータを更新する方法は?

Python=でSeleniumを使用して、データベースサイトからの検索結果を解析します。検索出力は動的であるため、新しいリクエストを入力すると、ページはリロードされませんが、検索結果は新しいです。

問題は、SeleniumがWebDriver DOMデータを更新しないことです。そのため、次回driver.find_elements_by_class_name('query_header')のようなものを試すと、以前の検索要求とStaleErrorから要素を受け取ります。

WebDriverWait(driver, timeout).until(element_present)を使用しても効果はありません。要素はあります(すべての検索結果ブロックは同じクラス、名前などを持っています)。しかし、それらは古いものです:)

各リクエストの後にdriver.refresh()でページをリロードすることで修正しましたが、少し不自然な+ダブルリクエストに見えます。

Selenium DOMデータを更新する方法はあるので、ページをリロードせずにfind_elementsで新しい要素を取得しますか?

4
sortas

ページのコンテンツを知らなければ、問題の解決策を作成することは困難です。

SeleniumコードがWebdriverから要素を選択すると、セレクターコードの実行時に読み込まれるため、ページ上で要素が選択されます。つまり、ページは必要ではありません新しい要素を取得するためにリロードされます。代わりに、問題はページに要素がまだ存在していないようです。つまり、セレクタが要素の新しいコピーを取得しようとしたときに検索結果が読み込まれなかった可能性があります。


簡単な解決策は、検索を開始してから検索結果を選択するまでの待機時間を増やし、ページが検索結果を読み込む時間を与えることです。

from Selenium import webdriver
import time

# Load page
driver = webdriver.Firefox()
driver.get('https://www.example.com')

# Begin search
driver.find_element_by_tag_name('a').click()

# Wait for search results to load
time.sleep(5)

# Retrieve search results
results = driver.find_elements_by_class_name('result')

これの欠点は、ネットワークQoSと、ページ上での検索クエリの実行にかかる時間に実際に依存することです。


より複雑ですが標準的な解決策は、ページが検索結果をロードするのを待つことです。おそらくAjax検索ロードアイコンをチェックするか、結果が変更されたかどうかを確認します。開始するのに適した場所は、 WebDriverWait's in Selenium を参照することです。

from Selenium import webdriver
from Selenium.webdriver.common.by import By
from Selenium.webdriver.support.ui import WebDriverWait
from Selenium.webdriver.support import expected_conditions

# Load page
driver = webdriver.Firefox()
driver.get('https://www.example.com')

# Begin search
driver.find_element_by_tag_name('a').click()

# Wait for search results to load
WebDriverWait(driver, 30).until(
    expected_conditions.invisibility_of_element_located((By.ID, 'ajax_loader'))
)

# Retrieve search results
results = driver.find_elements_by_class_name('result')

この方法の欠点は、それを機能させる方法を理解するのに長い時間がかかる可能性があり、更新を待機するページごとにカスタマイズする必要があることです。

この方法はうまくいかないようです。そのための提案は(ページが壊れない場合)、DOMの事前検索を操作して、新しい結果が読み込まれるのを待つ前に、セレクターに一致する既存の結果または要素をすべてクリアすることです。これにより、検索結果のセレクターと一致する要素の存在を待つときのSelenium WebDriverWaitの問題が修正されます。

driver.execute_script("el = document.getElementById('#results');el.parentElement.removeChild(el)")

さらに、ページをリロードしないように言ったので、ページがAjaxを使用して検索結果をロードし、JavaScriptでDOMを変更している可能性があります。ネットワークトラフィックを検査し(ほとんどのブラウザーのDevToolsには[ネットワーク]タブが必要です)、Webサイトが検索クエリを送信してデータを解析する方法をリバースエンジニアリングしてみると便利です。

import requests

# Search term (birds)
term = 'ja'

# Send request
request = requests.get('https://jqueryui.com/resources/demos/autocomplete/search.php?term=' + term)

# Print response
print(request.json())

これは特定のサイトのTOSまたはポリシー(実際にはこれらの方法のいずれか)に違反する可能性があるため、注意してください。最初に、DOMにロードされているレベルよりも低いレベルでリクエストを送信して解析する方法を見つけるのが難しい場合がありますページがより伝統的に検索結果をロードした後。プラス面では、これはおそらくAjaxに似た検索が使用されていると想定して、検索結果を取得するための最良の(パフォーマンス、信頼性)方法です。

3
andrewgu

同じスニペットを再利用して、要素をもう一度取得するようにドライバーに要求するだけです。

var X = driver.findElement( By.xpath("myxpath") ); //suppose element A is returned
//...do things
// the dom is reloaded
//copy paste the same command again :
var Y = driver.findElement( By.xpath("myxpath") ); //element B shall be returned after the dom has been updated.

次に、Yはdomリロードによって変更された新しいオブジェクトですが、その説明はまったく同じです!

2
LKTS