[英]python selenium: does not wait until page is loaded after a click() command
[英]Wait until page is loaded with Selenium WebDriver for Python
我想抓取無限滾動實現的頁面的所有數據。 以下python代碼有效。
for i in range(100):
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
time.sleep(5)
這意味着每次我向下滾動到底部時,我都需要等待 5 秒,這通常足以讓頁面完成加載新生成的內容。 但是,這可能沒有時間效率。 頁面可能會在 5 秒內完成加載新內容。 每次向下滾動時,如何檢測頁面是否完成加載新內容? 如果我能檢測到這一點,我可以在知道頁面完成加載后再次向下滾動以查看更多內容。 這樣更省時。
默認情況下, 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
的模塊。
試圖通過find_element_by_id
給構造為presence_of_element_located
(如圖中接受的答案)引起NoSuchElementException
被升高。 我不得不在fragles的評論中使用語法:
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"
找到以下3種方法:
檢查頁面 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 的博客。
正如David Cullen的回答中提到的,我一直看到建議使用如下一行:
element_present = EC.presence_of_element_located((By.ID, 'element_id'))
WebDriverWait(driver, timeout).until(element_present)
我很難在某處找到可以與By
一起使用的所有可能的定位器,所以我認為在這里提供列表會很有用。 根據 Ryan Mitchell 的Web Scraping with Python的說法:
ID
示例中使用; 通過 HTML id 屬性查找元素
CLASS_NAME
用於通過元素的 HTML 類屬性查找元素。 為什么這個函數
CLASS_NAME
不是簡單的CLASS
? 使用表單object.CLASS
會給 Selenium 的 Java 庫帶來問題,其中.class
是一個保留方法。 為了使不同語言之間的 Selenium 語法保持一致,改為使用CLASS_NAME
。
CSS_SELECTOR
使用
#idName
、.className
、tagName
約定按類、id 或標簽名稱查找元素。
LINK_TEXT
根據它們包含的文本查找 HTML 標簽。 例如,可以使用
(By.LINK_TEXT, "Next")
選擇(By.LINK_TEXT, "Next")
。
PARTIAL_LINK_TEXT
類似於
LINK_TEXT
,但匹配部分字符串。
NAME
按名稱屬性查找 HTML 標簽。 這對於 HTML 表單很方便。
TAG_NAME
按標簽名稱查找 HTML 標簽。
XPATH
使用 XPath 表達式 ... 來選擇匹配的元素。
來自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"))
在旁注中,您可以檢查是否沒有對 DOM 進行更多修改,而不是向下滾動 100 次(我們是在頁面底部進行 AJAX 延遲加載的情況下)
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
你有沒有試過driver.implicitly_wait
。 它就像驅動程序的設置,因此您只在會話中調用它一次,它基本上告訴驅動程序等待給定的時間,直到可以執行每個命令。
driver = webdriver.Chrome()
driver.implicitly_wait(10)
因此,如果您將等待時間設置為 10 秒,它將盡快執行命令,等待 10 秒后它會放棄。 我在類似的向下滾動場景中使用過它,所以我不明白為什么它在你的情況下不起作用。 希望這是有幫助的。
為了能夠修復這個答案,我必須添加新文本。 確保在implicitly_wait
使用小寫的“w”。
在這里,我使用了一個相當簡單的形式:
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
ajax頁面不斷加載數據的解決方案。 所述的預覽方法不起作用。 我們可以做的是獲取頁面 dom 並對其進行哈希處理,然后在增量時間內將舊的和新的哈希值進行比較。
import time
from selenium import webdriver
def page_has_loaded(driver, sleep_time = 2):
'''
Waits for page to completely load by comparing current page hash values.
'''
def get_page_hash(driver):
'''
Returns html dom hash
'''
# can find element by either 'html' tag or by the html 'root' id
dom = driver.find_element_by_tag_name('html').get_attribute('innerHTML')
# dom = driver.find_element_by_id('root').get_attribute('innerHTML')
dom_hash = hash(dom.encode('utf-8'))
return dom_hash
page_hash = 'empty'
page_hash_new = ''
# comparing old and new page DOM hash together to verify the page is fully loaded
while page_hash != page_hash_new:
page_hash = get_page_hash(driver)
time.sleep(sleep_time)
page_hash_new = get_page_hash(driver)
print('<page_has_loaded> - page not loaded')
print('<page_has_loaded> - page loaded: {}'.format(driver.current_url))
如何將 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"
您可以通過此功能非常簡單地做到這一點:
def page_is_loading(driver):
while True:
x = driver.execute_script("return document.readyState")
if x == "complete":
return True
else:
yield False
當你想在頁面加載完成后做一些事情時,你可以使用:
Driver = webdriver.Firefox(options=Options, executable_path='geckodriver.exe')
Driver.get("https://www.google.com/")
while not page_is_loading(Driver):
continue
Driver.execute_script("alert('page is loaded')")
在代碼中使用它:
from selenium import webdriver
driver = webdriver.Firefox() # or Chrome()
driver.implicitly_wait(10) # seconds
driver.get("http://www.......")
或者,如果您正在尋找特定標簽,則可以使用此代碼:
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 as EC
driver = webdriver.Firefox() #or Chrome()
driver.get("http://www.......")
try:
element = WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.ID, "tag_id"))
)
finally:
driver.quit()
這里的答案非常好。 等待XPATH
快速示例。
# wait for sizes to load - 2s timeout
try:
WebDriverWait(driver, 2).until(expected_conditions.presence_of_element_located(
(By.XPATH, "//div[@id='stockSizes']//a")))
except TimeoutException:
pass
我努力讓這個工作,因為它沒有按預期對我有用。 任何仍在努力使其工作的人都可以檢查這個。
我想等待一個元素出現在網頁上,然后再繼續我的操作。
我們可以使用 WebDriverWait(driver, 10, 1).until(),但問題是直到()期望一個函數,它可以每 1 秒執行一段提供的超時時間(在我們的例子中為 10)。 所以保持它像下面一樣對我有用。
element_found = wait_for_element.until(lambda x: x.find_element_by_class_name("MY_ELEMENT_CLASS_NAME").is_displayed())
這是直到()在幕后做的事情
def until(self, method, message=''):
"""Calls the method provided with the driver as an argument until the \
return value is not False."""
screen = None
stacktrace = None
end_time = time.time() + self._timeout
while True:
try:
value = method(self._driver)
if value:
return value
except self._ignored_exceptions as exc:
screen = getattr(exc, 'screen', None)
stacktrace = getattr(exc, 'stacktrace', None)
time.sleep(self._poll)
if time.time() > end_time:
break
raise TimeoutException(message, screen, stacktrace)
如果您嘗試滾動並查找頁面上的所有項目。 您可以考慮使用以下方法。 這是這里其他人提到的幾種方法的組合。 它為我完成了工作:
while True:
try:
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
driver.implicitly_wait(30)
time.sleep(4)
elem1 = WebDriverWait(driver, 30).until(EC.presence_of_all_elements_located((By.CSS_SELECTOR, "element-name")))
len_elem_1 = len(elem1)
print(f"A list Length {len_elem_1}")
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
driver.implicitly_wait(30)
time.sleep(4)
elem2 = WebDriverWait(driver, 30).until(EC.presence_of_all_elements_located((By.CSS_SELECTOR, "element-name")))
len_elem_2 = len(elem2)
print(f"B list Length {len_elem_2}")
if len_elem_1 == len_elem_2:
print(f"final length = {len_elem_1}")
break
except TimeoutException:
print("Loading took too much time!")
selenium 無法檢測頁面何時完全加載,但 javascript 可以。 我建議你試試這個。
from selenium.webdriver.support.ui import WebDriverWait
WebDriverWait(driver, 100).until(lambda driver: driver.execute_script('return document.readyState') == 'complete')
這將執行 javascript 代碼而不是使用 python,因為 javascript 可以檢測頁面何時完全加載,它將顯示“完成”。 此代碼意味着在 100 秒內,繼續嘗試 document.readyState 直到完整顯示。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.