Selenium(Python) - 等待下载过程使用Chrome网络驱动程序完成

Bla*_*mba 9 selenium python-3.x selenium-chromedriver

我通过chromewebdriver(windows)使用selenium和python,以便自动执行从不同页面下载大量文件的任务.我的代码有效,但解决方案远非理想:下面的函数点击启动java脚本函数的网站按钮,生成PDF文件然后下载它.

我不得不使用一个静态等待,以等待下载完成(丑陋的),我不能为了验证检查文件系统时,下载完成,因为我使用多线程(下载来自不同页面的文件很多的一次)并且文件的名称也是在网站本身动态生成的.

我的代码:

def file_download(num, drivervar):
Counter += 1
    try:
        drivervar.get(url[num])
        download_button = WebDriverWait(drivervar, 20).until(EC.element_to_be_clickable((By.ID, 'download button ID')))
        download_button.click()
        time.sleep(10) 
    except TimeoutException: # Retry once
        print('Timeout in thread number: ' + str(num) + ', retrying...')
..... 
Run Code Online (Sandbox Code Playgroud)

是否可以在webdriver中确定下载完成?我想避免使用time.sleep(x).

非常感谢.

Flo*_* B. 20

您可以通过chrome://downloads/驱动程序导航获取每次下载的状态.

等待所有下载完成并列出所有路径:

def every_downloads_chrome(driver):
    if not driver.current_url.startswith("chrome://downloads"):
        driver.get("chrome://downloads/")
    return driver.execute_script("""
        var items = downloads.Manager.get().items_;
        if (items.every(e => e.state === "COMPLETE"))
            return items.map(e => e.fileUrl || e.file_url);
        """)


# waits for all the files to be completed and returns the paths
paths = WebDriverWait(driver, 120, 1).until(every_downloads_chrome)
print(paths)
Run Code Online (Sandbox Code Playgroud)


Wal*_*ess 8

我遇到了同样的问题并找到了解决方案。您可以检查 .crdownload 是否在您的下载文件夹中。如果下载文件夹中有 0 个扩展名为 .crdownload 的文件实例,则所有下载都已完成。我认为这仅适用于铬和铬。

def downloads_done():
    while True:
        for filename in os.listdir("/downloads"):
            if ".crdownload" in i:
                time.sleep(0.5)
                downloads_done()
Run Code Online (Sandbox Code Playgroud)

每当您调用 downloads_done() 时,它都会自行循环,直到所有下载完成。如果您正在下载 80 GB 之类的大文件,那么我不建议这样做,因为该函数可以达到最大递归深度。

2020年编辑:

def wait_for_downloads():
    print("Waiting for downloads", end="")
    while any([filename.endswith(".crdownload") for filename in 
               os.listdir("/downloads")]):
        time.sleep(2)
        print(".", end="")
    print("done!")
Run Code Online (Sandbox Code Playgroud)

print() 中的“end”关键字参数通常包含一个换行符,但我们将其替换。虽然 /downloads 文件夹中没有以 .crdownload 结尾的文件名休眠 2 秒并打印一个没有换行符的点到控制台

在发现请求后,我真的不建议再使用 selenium,但如果它是一个非常严密保护的站点,有 cloudflare 和验证码等,那么你可能不得不求助于 selenium。


thd*_*dox 7

使用 Chrome 80,我不得不通过以下代码更改@florent-b 的答案:

def every_downloads_chrome(driver):
    if not driver.current_url.startswith("chrome://downloads"):
        driver.get("chrome://downloads/")
    return driver.execute_script("""
        return document.querySelector('downloads-manager')
        .shadowRoot.querySelector('#downloadsList')
        .items.filter(e => e.state === 'COMPLETE')
        .map(e => e.filePath || e.file_path || e.fileUrl || e.file_url);
        """)
Run Code Online (Sandbox Code Playgroud)

我相信这是复古兼容的,我的意思是这将适用于旧版本的 Chrome。


pro*_*num 5

在无头模式下运行 Chrome 时出现打开问题chrome://downloads/

以下函数使用复合方法,无论模式是否为无头,都可以工作,并选择每种模式中可用的更好方法。

它假设调用者在file_download_path每次调用此函数后清除所有下载的文件。

import os
import logging
from selenium.webdriver.support.ui import WebDriverWait

def wait_for_downloads(driver, file_download_path, headless=False, num_files=1):
    max_delay = 60
    interval_delay = 0.5
    if headless:
        total_delay = 0
        done = False
        while not done and total_delay < max_delay:
            files = os.listdir(file_download_path)
            # Remove system files if present: Mac adds the .DS_Store file
            if '.DS_Store' in files:
                files.remove('.DS_Store')
            if len(files) == num_files and not [f for f in files if f.endswith('.crdownload')]:
                done = True
            else:
                total_delay += interval_delay
                time.sleep(interval_delay)
        if not done:
            logging.error("File(s) couldn't be downloaded")
    else:
        def all_downloads_completed(driver, num_files):
            return driver.execute_script("""
                var items = document.querySelector('downloads-manager').shadowRoot.querySelector('#downloadsList').items;
                var i;
                var done = false;
                var count = 0;
                for (i = 0; i < items.length; i++) {
                    if (items[i].state === 'COMPLETE') {count++;}
                }
                if (count === %d) {done = true;}
                return done;
                """ % (num_files))

        driver.execute_script("window.open();")
        driver.switch_to_window(driver.window_handles[1])
        driver.get('chrome://downloads/')
        # Wait for downloads to complete
        WebDriverWait(driver, max_delay, interval_delay).until(lambda d: all_downloads_completed(d, num_files))
        # Clear all downloads from chrome://downloads/
        driver.execute_script("""
            document.querySelector('downloads-manager').shadowRoot
            .querySelector('#toolbar').shadowRoot
            .querySelector('#moreActionsMenu')
            .querySelector('button.clear-all').click()
            """)
        driver.close()
        driver.switch_to_window(driver.window_handles[0])
Run Code Online (Sandbox Code Playgroud)