【问题标题】:Slow scrolling down the page using Selenium使用 Selenium 缓慢向下滚动页面
【发布时间】:2015-09-05 16:00:17
【问题描述】:

我正在尝试从航班搜索页面中抓取一些数据。

这个页面是这样工作的:

您填写表格,然后单击按钮搜索 - 没关系。当您单击按钮时,您将被重定向到带有结果的页面,这就是问题所在。此页面连续添加结果,例如一分钟,这没什么大不了的 - 问题是要获得所有这些结果。当你在真正的浏览器中时,你必须向下滚动页面,这些结果就会出现。所以我尝试使用 Selenium 向下滚动。它在页面底部向下滚动可能非常快,或者它是跳转而不是滚动页面不会加载任何新结果。

当您缓慢向下滚动时,它会重新加载结果,但如果您快速滚动,它会停止加载。

我不确定我的代码是否有助于理解这一点,所以我附上了它。

SEARCH_STRING = """URL"""

class spider():

    def __init__(self):
        self.driver = webdriver.Firefox()

    @staticmethod
    def prepare_get(dep_airport,arr_airport,dep_date,arr_date):
        string = SEARCH_STRING%(dep_airport,arr_airport,arr_airport,dep_airport,dep_date,arr_date)
        return string


    def find_flights_html(self,dep_airport, arr_airport, dep_date, arr_date):
        if isinstance(dep_airport, list):
            airports_string = str(r'%20').join(dep_airport)
            dep_airport = airports_string

        wait = WebDriverWait(self.driver, 60) # wait for results
        self.driver.get(spider.prepare_get(dep_airport, arr_airport, dep_date, arr_date))
        wait.until(EC.invisibility_of_element_located((By.XPATH, '//img[contains(@src, "loading")]')))
        wait.until(EC.invisibility_of_element_located((By.XPATH, u'//div[. = "Poprosíme o trpezlivosť, hľadáme pre Vás ešte viac letov"]/preceding-sibling::img')))
        self.driver.execute_script("window.scrollTo(0,document.body.scrollHeight);")

        self.driver.find_element_by_xpath('//body').send_keys(Keys.CONTROL+Keys.END)
        return self.driver.page_source

    @staticmethod 
    def get_info_from_borderbox(div):
        arrival = div.find('div',class_='departure').text
        price = div.find('div',class_='pricebox').find('div',class_=re.compile('price'))
        departure = div.find_all('div',class_='departure')[1].contents
        date_departure = departure[1].text 
        airport_departure = departure[5].text
        arrival = div.find_all('div', class_= 'arrival')[0].contents
        date_arrival = arrival[1].text
        airport_arrival = arrival[3].text[1:]
        print 'DEPARTURE: ' 
        print date_departure,airport_departure
        print 'ARRIVAL: '
        print date_arrival,airport_arrival

    @staticmethod
    def get_flights_from_result_page(html):

        def match_tag(tag, classes):
            return (tag.name == 'div'
                    and 'class' in tag.attrs
                    and all([c in tag['class'] for c in classes]))

        soup = mLib.getSoup_html(html)
        divs = soup.find_all(lambda t: match_tag(t, ['borderbox', 'flightbox', 'p2']))

        for div in divs:
            spider.get_info_from_borderbox(div)

        print len(divs)


spider_inst = spider() 

print spider.get_flights_from_result_page(spider_inst.find_flights_html(['BTS','BRU','PAR'], 'MAD', '2015-07-15', '2015-08-15'))

所以在我看来,主要问题是它滚动得太快而无法触发新的结果加载。

你知道如何让它工作吗?

【问题讨论】:

标签: python selenium scroll selenium-webdriver


【解决方案1】:

我需要它来解决同样的问题,我需要抓取一个社交媒体网站

y = 1000
    for timer in range(0,50):
         driver.execute_script("window.scrollTo(0, "+str(y)+")")
         y += 1000  
         time.sleep(1)

每 1000 次睡眠是为了允许加载

【讨论】:

    【解决方案2】:

    经过一番实验,终于找到了一个不错的解决方案:

        def __scroll_down_page(self, speed=8):
        current_scroll_position, new_height= 0, 1
        while current_scroll_position <= new_height:
            current_scroll_position += speed
            self.__driver.execute_script("window.scrollTo(0, {});".format(current_scroll_position))
            new_height = self.__driver.execute_script("return document.body.scrollHeight")
    

    【讨论】:

    • 这个解决方案对我有用。我正在抓取一个网站。当我在开发工具中检查元素但在页面源中丢失时,某些元素存在。我尝试使用 selenium 并使用上述方法慢慢向下滚动,现在我可以在页面源代码中看到它们。谢谢@Dorcioman
    • 我猜这是一个函数,你到底怎么用?
    • 实例化self.__driver对象后调用函数
    【解决方案3】:

    您可以使用 Selenium 进行平滑滚动,如下所示:

    total_height = int(driver.execute_script("return document.body.scrollHeight"))
    
    for i in range(1, total_height, 5):
        driver.execute_script("window.scrollTo(0, {});".format(i))
    

    【讨论】:

    • 嗨,我是 python 新手,我想知道第三个参数如何改变滚动速度?
    • @SasanAngel,第三个参数告诉我要改变多少。在其他语言中,它就像 i=i+5。所以这里不是直接从0滚动到n。它将每 5 滚动一次以到达 n。
    • 这是一个很好的解决方案。谢谢,阿斯蒂克。请注意,正如它所写的那样,这对于在您向下滚动时加载但不会删除任何先前加载的滚动出视图的页面(如某些网站)非常有效。如果你是这种情况,你需要在滚动时进行抓取,所以在 for 相同的 for 循环中。只是给其他人的小便条。
    【解决方案4】:

    这是一种对我有用的不同方法,涉及滚动到最后一个搜索结果的视图并在再次滚动之前等待加载其他元素:

    # -*- coding: utf-8 -*-
    from selenium import webdriver
    from selenium.webdriver.common.by import By
    from selenium.webdriver.support.ui import WebDriverWait
    from selenium.common.exceptions import StaleElementReferenceException
    from selenium.webdriver.support import expected_conditions as EC
    
    
    class wait_for_more_than_n_elements(object):
        def __init__(self, locator, count):
            self.locator = locator
            self.count = count
    
        def __call__(self, driver):
            try:
                count = len(EC._find_elements(driver, self.locator))
                return count >= self.count
            except StaleElementReferenceException:
                return False
    
    
    driver = webdriver.Firefox()
    
    dep_airport = ['BTS', 'BRU', 'PAR']
    arr_airport = 'MAD'
    dep_date = '2015-07-15'
    arr_date = '2015-08-15'
    
    airports_string = str(r'%20').join(dep_airport)
    dep_airport = airports_string
    
    url = "https://www.pelikan.sk/sk/flights/list?dfc=C%s&dtc=C%s&rfc=C%s&rtc=C%s&dd=%s&rd=%s&px=1000&ns=0&prc=&rng=1&rbd=0&ct=0" % (dep_airport, arr_airport, arr_airport, dep_airport, dep_date, arr_date)
    driver.maximize_window()
    driver.get(url)
    
    wait = WebDriverWait(driver, 60)
    wait.until(EC.invisibility_of_element_located((By.XPATH, '//img[contains(@src, "loading")]')))
    wait.until(EC.invisibility_of_element_located((By.XPATH,
                                                   u'//div[. = "Poprosíme o trpezlivosť, hľadáme pre Vás ešte viac letov"]/preceding-sibling::img')))
    
    while True:  # TODO: make the endless loop end
        results = driver.find_elements_by_css_selector("div.flightbox")
        print "Results count: %d" % len(results)
    
        # scroll to the last element
        driver.execute_script("arguments[0].scrollIntoView();", results[-1])
    
        # wait for more results to load
        wait.until(wait_for_more_than_n_elements((By.CSS_SELECTOR, 'div.flightbox'), len(results)))
    

    注意事项:

    • 您需要确定何时停止循环 - 例如,在特定的 len(results) 值处
    • wait_for_more_than_n_elements 是 custom Expected Condition,它有助于识别下一部分何时加载,我们可以再次滚动

    【讨论】:

    • 恐怕不行。它在循环中返回 10,当我尝试输入以下内容时:for result in results: print result.text 我发现它返回相同的值。
    • @Milan 好吧,我看到结果计数随着循环的每次迭代而增加,这意味着正在加载更多结果。结束循环后提取结果。
    • 为了检查它是否找到新结果,我将结果添加到集合中,并在集合的每个循环打印长度中添加。它保持在 15。在这里您可以找到代码和打印的结果:pastebin.com/fkUrCvAm
    • @Milan 很有趣,谢谢。如果改用 Chrome,您会得到相同的结果吗?另外,您使用的是哪个 selenium 和 firefox 版本?谢谢。
    • 它会引发错误,我已经安装了这个 chromedriver chromedriver.storage.googleapis.com/index.html?path=2.15。我也试过 PhantomJS。在这里您可以找到 3 个驱动程序的输出:pastebin.com/9gRYbEh4
    【解决方案5】:
    from selenium import webdriver
    import time
    
    driver = webdriver.Chrome()
    driver.get("https://en.wikipedia.org")
    height = browser.execute_script("return document.body.scrollHeight")
    for scrol in range(100,height,100):
        browser.execute_script(f"window.scrollTo(0,{scrol})")
        time.sleep(0.1)
    

    它对我有用。如果您想将页面滚动到最后以显示所有页面元素,这对您来说可能很有价值。 如果你想提高滚动速度,只需将滚动速度更改为 100 到 200。

    【讨论】:

      【解决方案6】:

      在 Python Selenium 中,获取元素的 Y 位置,然后慢慢向下滚动。

      y = driver.execute_script("return document.querySelector('YOUR-CSS-SELECTOR').getBoundingClientRect()['y']")
      for x in range(0, int(y), 100):
          driver.execute_script("window.scrollTo(0, "+str(x)+");")
      

      【讨论】:

        【解决方案7】:
        @Test
        public void clickMeButton() {
           WebElement clickMeButton = driver.findElement(By.name("et_builder_submit_button"));
        
           // softly scroll to the element group
           JavascriptExecutor js = (JavascriptExecutor) driver;
        
           for (int i = 0; i < 800; i += 7) {
              js.executeScript("window.scrollTo(0, " + i + ")");
           }
        
           System.out.println("selected button");
           clickMeButton.click();
           System.out.println("clicked");
        }
        

        【讨论】:

          【解决方案8】:

          假设页面是延迟加载的,使用window.scrollTo() 迭代地跳转到点可能会遗漏超出范围的元素。此外,假设页面的高度与加载的内容相关是动态的,尚未加载的项目可能会被忽略。所以我选择了window.scrollBy():

          height = driver.execute_script("return document.body.scrollHeight")
          for i in range(height):
             driver.execute_script('window.scrollBy(0,20)') # scroll by 20 on each iteration
             height = driver.execute_script("return document.body.scrollHeight") # reset height to the new height after scroll-triggered elements have been loaded. 
          

          在我的例子中,滚动到第 30 位后加载了一批新元素,为页面提供了我需要考虑的新高度。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-08-30
            • 1970-01-01
            相关资源
            最近更新 更多