web-dev-qa-db-ja.com

PythonがSelenium WebDriver for Pythonでロードされるまで待ちます

私は無限スクロールによって実装されたページのすべてのデータを削りたいです。次のpythonコードは動作します。

for i in range(100):
    driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
    time.sleep(5)

これは、一番下までスクロールするたびに5秒間待つ必要があることを意味します。これは、通常、ページが新しく生成されたコンテンツのロードを完了するのに十分です。しかし、これは時間効率が悪いかもしれません。ページが5秒以内に新しいコンテンツの読み込みを完了することがあります。スクロールダウンするたびに、ページが新しいコンテンツの読み込みを完了したかどうかをどのように検出できますか?これが検出できたら、ページの読み込みが完了したことを確認したら、もう一度スクロールして他のコンテンツを表示できます。これはもっと時間効率的です。

116
apogne

webdriver.get()メソッドを介してデフォルトでページがロードされるのを待ちます。

@ user227215が言ったように特定の要素を探しているかもしれないので、あなたはあなたのページにある要素を待つためにWebDriverWaitを使うべきです:

from Selenium import webdriver
from Selenium.webdriver.support.ui import WebDriverWait
from Selenium.webdriver.support import expected_conditions as EC
from Selenium.webdriver.common.by import By
from Selenium.common.exceptions import TimeoutException

browser = webdriver.Firefox()
browser.get("url")
delay = 3 # seconds
try:
    myElem = WebDriverWait(browser, delay).until(EC.presence_of_element_located((By.ID, 'IdOfMyElement')))
    print "Page is ready!"
except TimeoutException:
    print "Loading took too much time!"

私はアラートをチェックするためにそれを使いました。ロケーターを見つけるために他の型メソッドを使用することができます。

編集1:

webdriverはデフォルトでページがロードされるのを待つことになるでしょう。フレーム内の読み込みやajaxリクエストを待ちません。つまり、.get('url')を使用すると、ブラウザはページが完全にロードされるまで待機してから、コード内の次のコマンドに進みます。しかし、あなたがajaxリクエストを投稿しているとき、webdriverは待たず、ページまたはページの一部がロードされるのを適切な時間待つのはあなたの責任です。そのためexpected_conditionsという名前のモジュールがあります。

150

find_element_by_idのコンストラクターにpresence_of_element_locatedを渡そうとすると( 受け入れられた回答 を参照)、NoSuchElementExceptionが発生します。私は fragles ' comment の構文を使わなければなりませんでした:

from Selenium import webdriver
from Selenium.common.exceptions import TimeoutException
from Selenium.webdriver.support.ui import WebDriverWait
from Selenium.webdriver.support import expected_conditions as EC
from Selenium.webdriver.common.by import By

driver = webdriver.Firefox()
driver.get('url')
timeout = 5
try:
    element_present = EC.presence_of_element_located((By.ID, 'element_id'))
    WebDriverWait(driver, timeout).until(element_present)
except TimeoutException:
    print "Timed out waiting for page to load"

これは ドキュメントの例 と一致します。これは Byのドキュメント へのリンクです。

55
David Cullen

以下の3つの方法を見つけます。

readyState

ページreadyStateの確認(信頼できない):

def page_has_loaded(self):
    self.log.info("Checking if {} page is loaded.".format(self.driver.current_url))
    page_state = self.driver.execute_script('return document.readyState;')
    return page_state == 'complete'

wait_forヘルパー関数は優れていますが、残念ながらclick_through_to_new_pageは、ブラウザーがクリックの処理を開始する前に、古いページでスクリプトを実行する競合状態に対してオープンであり、page_has_loadedはすぐにtrueを返します。

id

新しいページIDと古いページIDの比較:

def page_has_loaded_id(self):
    self.log.info("Checking if {} page is loaded.".format(self.driver.current_url))
    try:
        new_page = browser.find_element_by_tag_name('html')
        return new_page.id != old_page.id
    except NoSuchElementException:
        return False

IDの比較は、古い参照の例外を待つほど効果的ではない可能性があります。

staleness_of

staleness_ofメソッドの使用:

@contextlib.contextmanager
def wait_for_page_load(self, timeout=10):
    self.log.debug("Waiting for page to load at {}.".format(self.driver.current_url))
    old_page = self.find_element_by_tag_name('html')
    yield
    WebDriverWait(self, timeout).until(staleness_of(old_page))

詳細については、 Harryのブログ を確認してください。

32
kenorb

から Selenium/webdriver/support/wait.py

driver = ...
from Selenium.webdriver.support.wait import WebDriverWait
element = WebDriverWait(driver, 10).until(
    lambda x: x.find_element_by_id("someId"))
16
Carl

David Cullenからの 回答に記載されているように、 では、次のような行を使用することをお勧めします。

element_present = EC.presence_of_element_located((By.ID, 'element_id'))
    WebDriverWait(driver, timeout).until(element_present)

By構文で使用できるすべての可能なロケータをどこにでも見つけることは困難だったので、ここでリストを提供すると便利だと思いました。 Ryan MitchellによるPythonによるWebスクレイピングによると、

ID

例で使用されています。 HTMLのid属性で要素を見つける

CLASS_NAME

HTMLクラス属性によって要素を見つけるために使用されます。この関数CLASS_NAMEが単にCLASSではないのはなぜですか? object.CLASSという形式を使用すると、SeleniumのJavaライブラリーで問題が発生します。ここで、.classは予約済みのメソッドです。 Seleniumの構文を異なる言語間で統一するために、代わりにCLASS_NAMEを使用しました。

CSS_SELECTOR

#idName.classNametagNameの規則を使用して、クラス、ID、またはタグ名で要素を検索します。

LINK_TEXT

含まれているテキストでHTMLタグを検索します。たとえば、「次へ」というリンクは(By.LINK_TEXT, "Next")を使用して選択できます。

PARTIAL_LINK_TEXT

LINK_TEXTと似ていますが、部分文字列で一致します。

NAME

Name属性でHTMLタグを見つけます。これはHTMLフォームには便利です。

TAG_NAME

HTMLタグをタグ名でまとめます。

XPATH

XPath式...を使用して、一致する要素を選択します。

14
J0ANMM

ちなみに、100回スクロールダウンする代わりに、DOMへの変更がこれ以上ないかどうかを確認できます(ページの下部がAJAX lazy-loadedである場合)

def scrollDown(driver, value):
    driver.execute_script("window.scrollBy(0,"+str(value)+")")

# Scroll down the page
def scrollDownAllTheWay(driver):
    old_page = driver.page_source
    while True:
        logging.debug("Scrolling loop")
        for i in range(2):
            scrollDown(driver, 500)
            time.sleep(2)
        new_page = driver.page_source
        if new_page != old_page:
            old_page = new_page
        else:
            break
    return True
7
raffamaiden

WebDriverWaitをWhileループに入れて例外をキャッチするのはどうですか。

from Selenium import webdriver
from Selenium.webdriver.support.ui import WebDriverWait
from Selenium.webdriver.support import expected_conditions as EC
from Selenium.common.exceptions import TimeoutException

browser = webdriver.Firefox()
browser.get("url")
delay = 3 # seconds
while True:
    try:
        WebDriverWait(browser, delay).until(EC.presence_of_element_located(browser.find_element_by_id('IdOfMyElement')))
        print "Page is ready!"
        break # it will break from the loop once the specific element will be present. 
    except TimeoutException:
        print "Loading took too much time!-Try again"
3
Rao

ここで私はやや単純な形を使ってそれをしました:

from Selenium import webdriver
browser = webdriver.Firefox()
browser.get("url")
searchTxt=''
while not searchTxt:
    try:    
      searchTxt=browser.find_element_by_name('NAME OF ELEMENT')
      searchTxt.send_keys("USERNAME")
    except:continue
3

driver.implicitly_waitを試してみましたか。これはドライバの設定に似ているので、セッションで一度だけ呼び出すだけで、基本的には各コマンドを実行できるようになるまで一定時間待つようドライバに指示します。

driver = webdriver.Chrome()
driver.implicitly_Wait(10)

そのため、10秒の待ち時間を設定した場合、コマンドはできるだけ早く実行され、中断するまで10秒待ちます。私はこれを似たようなスクロールダウンのシナリオで使ったので、あなたのケースではうまくいかないのかわかりません。これが役に立つことを願っています。

2
seeiespi