Sayfa Python için Selenium WebDriver ile yüklenene kadar bekleyin


182

Sonsuz bir kaydırma ile uygulanan bir sayfanın tüm verilerini kazımak istiyorum. Aşağıdaki python kodu çalışır.

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

Bu, her aşağı kaydırdığımda, 5 saniye beklemem gerektiği anlamına gelir, bu da genellikle sayfanın yeni oluşturulan içeriği yüklemeyi bitirmesi için yeterlidir. Ancak, bu zaman açısından verimli olmayabilir. Sayfa yeni içerikleri 5 saniye içinde yüklemeyi bitirebilir. Her aşağı kaydırdığımda sayfanın yeni içerikleri yüklemeyi bitip bitmediğini nasıl anlayabilirim? Bunu algılayabilirsem, sayfanın yüklenmesini tamamladıktan sonra daha fazla içerik görmek için tekrar aşağı kaydırabilirim. Bu zamandan tasarruf sağlar.


1
Sayfa hakkında biraz daha bilgi edinmek yardımcı olabilir. Elemanlar sıralı veya öngörülebilir mi? Kimlik veya xpath kullanarak görünürlük kontrol ederek öğelerin yüklenmesini bekleyebilirsiniz
user2272115

: Aşağıdaki sayfa tarama ediyorum pinterest.com/cremedelacrumb/yum
apogne


Bu sorunuza cevap veriyor mu? Selenium'da sayfa yüklenmesini bekleyin
Matej J

Yanıtlar:


235

webdriverAracılığıyla varsayılan olarak yüke bir sayfa için bekleyecektir .get()yöntemle.

@ User227215'in dediği gibi belirli bir eleman arıyor olabileceğiniz gibi, WebDriverWait , sayfanızda bulunan bir öğeyi beklemek için :

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!"

Uyarıları kontrol etmek için kullandım. Konum bulucuyu bulmak için başka herhangi bir tür yöntem kullanabilirsiniz.

DÜZENLEME 1:

webdriverBir sayfanın varsayılan olarak yüklenmesini bekleyeceğini belirtmeliyim . Çerçevelerin içine yükleme veya ajax istekleri için beklemez. Bu, kullandığınızda .get('url')tarayıcınızın sayfa tamamen yüklenene kadar bekleyip koddaki bir sonraki komuta geçeceği anlamına gelir . Ancak bir ajax isteği gönderirken webdriverbeklemez ve sayfanın veya sayfanın bir kısmının yüklenmesi için uygun bir süre beklemek sizin sorumluluğunuzdadır; yani bir modül var expected_conditions.


3
Ben başlamıştı değiştirildi "(IdOfMyElement ")) EC.presence_of_element_located ((By.ID,)" .until (tarayıcı, gecikme) WebDriverWait" manuel bkz "find_element () * sonra argüman WebElement değil, bir dizi olmalı" selenium- python.readthedocs.org/tr/latest/waits.html
fragles

2
@Fragles'ın yorumu ve David Cullen'in yanıtı benim için işe yaradı. Belki bu kabul edilen cevap buna göre güncellenebilir?
Michael Ohlrogge

6
Geçme a'nın yükselmesine browser.find_element_by_id('IdOfMyElement')neden olur NoSuchElementException. Dokümantasyon şöyle bir demet kullanmak diyor: (By.ID, 'IdOfMyElement'). Cevabımı
David Cullen

2
Umarım bu başlangıçta bana açık olmadığı için başka birine yardımcı olur: WebDriverWait aslında daha sonra bir eylem gerçekleştirebileceğiniz (örneğin click()), metni vb. Okuyabileceğiniz bir web nesnesi döndürecektir . Beklemeye neden oldu, daha sonra hala öğeyi bulmalısın. Bir bekleme yaparsanız, daha sonra bir bul öğesi sonra, selenyum eski beklemeye devam ederken öğeyi bulmaya çalıştığından hata verir (umarım bu mantıklıdır). Alt satırda, WebDriverWait'i kullandıktan sonra öğeyi bulmanıza gerek yok - zaten bir nesne.
Ben Wilson

1
@Gopgop Vay canına bu çok çirkin bir yapıcı yorum değil. Bu konuda çirkin olan nedir? Nasıl daha iyi hale getirilebilir?
Modus Tollens

73

Geçmeye çalışırken find_element_by_idyapıcısına presence_of_element_located(gösterildiği gibi kabul edilen cevap ) neden olduğu NoSuchElementExceptionyükseltilmiş olması. Ben sözdizimi kullanmak zorunda kaldı 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"

Bu , belgelerdeki örnekle eşleşir . İşte By belgelerine bir bağlantı .


2
Teşekkür ederim! evet, bu benim için de gerekliydi. Kimlik, kullanılabilecek tek özellik değildir, tam listeyi almak için yardımı kullanın (By). Örneğin kullandımEC.presence_of_element_located((By.XPATH, "//*[@title='Check All Q1']"))
Michael Ohlrogge

Benim için de böyle çalışıyor! Nesnede bulunan farklı konumlandırıcılara genişleyen ek bir cevap yazdım By.
J0ANMM

Farklı sayfaların yüklenebileceği ve her zaman aynı sayfanın olmadığı beklentilerle ilgili bir takip sorusu gönderdim: stackoverflow.com/questions/51641546/…
Liquidgenius

48

Aşağıdaki 3 yöntemi bulun:

readyState

Sayfayı readyState kontrol ediyor (güvenilir değil):

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_forYardımcı işlevi iyidir, ama ne yazık ki click_through_to_new_pagetarayıcı tıklayın işleme başlamadan önce, biz eski sayfasında komut dosyası çalıştırmak için yönetmek yarış durumuna açıktır ve page_has_loadedsadece hemen true döndürür.

id

Yeni sayfa kimliklerini eskisiyle karşılaştırma:

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

Kimlikleri karşılaştırmak, eski referans istisnalarını beklemek kadar etkili olmayabilir.

staleness_of

staleness_ofYöntemi kullanarak :

@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))

Daha fazla bilgi için Harry'nin bloguna bakın .


Neden self.driver.execute_script('return document.readyState;')güvenilir olmadığını söylüyorsun ? Statik bir dosyanın yeni bir sekmede (.get () yerine başka bir sekmede javascript yoluyla açılır) yüklenmesini bekleyen kullanım durumum için mükemmel çalışıyor gibi görünüyor.
Arthur Hebert

1
@ArthurHebert Yarış durumu nedeniyle güvenilir olmayabilir, ilgili alıntıları ekledim.
kenorb

23

David Cullen'in cevabında belirtildiği gibi, her zaman aşağıdaki gibi bir çizgi kullanmak için öneriler gördüm:

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

Kullanılabilecek tüm olası yer belirleyicileri bir yerde bulmak benim için zor oldu By, bu yüzden burada listeyi sağlamanın yararlı olacağını düşündüm. Ryan Mitchell tarafından Python ile Web Scraping göre :

ID

Örnekte kullanılmıştır; HTML kimlik özelliklerine göre öğeler bulur

CLASS_NAME

Öğeleri HTML sınıfı özelliklerine göre bulmak için kullanılır. Neden bu fonksiyon CLASS_NAMEbasit değil CLASS? Formun kullanılması, object.CLASS Selenium'un .classayrılmış bir yöntem olan Java kitaplığı için sorun yaratacaktır . Selenyum sözdizimini farklı diller arasında tutarlı tutmak için CLASS_NAMEbunun yerine kullanılmıştır.

CSS_SELECTOR

Kullanarak kendi sınıf, kimliği veya etiket adıyla unsurları bulur #idName, .className, tagNamekongre.

LINK_TEXT

HTML etiketlerini içerdikleri metne göre bulur. Örneğin, "İleri" yazan bir bağlantı kullanılarak seçilebilir (By.LINK_TEXT, "Next").

PARTIAL_LINK_TEXT

LINK_TEXTKısmi bir dizeye benzer , ancak eşleşir.

NAME

HTML etiketlerini ad özelliklerine göre bulur. Bu, HTML formları için kullanışlıdır.

TAG_NAME

HTML etiketlerini etiket adlarına göre bulur.

XPATH

Eşleşen öğeleri seçmek için bir XPath ifadesi kullanır.


5
Kimin belgeleri , konumlandırıcı olarak kullanılabilen özellikleri listeler.
David Cullen

1
Aradığım şey buydu! Teşekkürler! Eh, google beni bu soruya gönderdiğinden bulmak daha kolay olmalı, ancak resmi belgelere değil.
J0ANMM

Kitaptan alıntı için teşekkürler. Dokümantasyondan çok daha net.
ZygD


11

Yan notta, 100 kez aşağı kaydırmak yerine DOM'da başka değişiklik olup olmadığını kontrol edebilirsiniz (sayfanın alt kısmının AJAX tembel olarak yüklenmiş olması durumunda)

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

Bu kullanışlı. Ancak 500 neyi temsil ediyor? Sayfanın sonuna ulaşmak için yeterince büyük mü?
Moondra

Sayfanın kaydırması gereken miktardır ... olabildiğince yükseğe ayarlamanız gerekir. Bu sayının benim için yeterli olduğunu öğrendim, çünkü AJAX öğeleri tembel yüklenene kadar sayfayı aşağıya kaydırır, sayfayı tekrar yükleme ihtiyacını
giderir

Bu, gitlab'daki bir sorunla ilgili tüm yorumların tam olarak yüklenmesini sağlamaya çalışırken yardımcı olur.
bgStack15

8

Eğer denedin driver.implicitly_wait. Sürücü için bir ayar gibidir, bu yüzden sadece oturumda bir kez çağırırsınız ve temel olarak sürücüye her komutun yürütülmesine kadar verilen süreyi beklemesini söyler.

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

Bu nedenle, 10 saniyelik bir bekleme süresi ayarlarsanız, komut en kısa sürede yürütülür ve vazgeçmeden 10 saniye beklenir. Bunu benzer kaydırma senaryolarında kullandım, bu yüzden neden sizin durumunuzda işe yaramayacağını anlamıyorum. Umarım bu yardımcı olur.

Bu yanıtı giderebilmek için yeni metin eklemem gerekiyor. Küçük bir 'w' harfi kullandığınızdan emin olun implicitly_wait.


Örtük olarak beklemek ve webdriverwait arasındaki fark nedir?
song0089

4

WebDriverWait'i While döngüsüne yerleştirmeye ve istisnaları yakalamaya ne dersiniz?

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"

Döngüye ihtiyacın yok mu?
Corey Goldberg

4

Burada oldukça basit bir form kullanarak yaptım:

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

0

Bunu bu işlevle çok basit yapabilirsiniz:

def page_is_loading(driver):
    while True:
        x = driver.execute_script("return document.readyState")
        if x == "complete":
            return True
        else:
            yield False

sayfa yükleme işlemi tamamlandıktan sonra bir şeyler yapmak istediğinizde şunları kullanabilirsiniz:

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')")

0

Sürekli veri yükleyen ajax sayfaları için çözüm. Belirtilen önizleme yöntemleri işe yaramıyor. Bunun yerine yapabileceğimiz sayfa dom ve hash değerini alıp eski ve yeni hash değerlerini bir delta boyunca karşılaştırmaktır.

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))
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.