I've used selenium to initiate a download. After the download is complete, certain actions need to be taken, is there any simple method to find out when a download has complete? (I am using the FireFox driver)
12 Answers
I came across this problem recently. I was downloading multiple files at once and had to build in a way to timeout if the downloads failed.
The code checks the filenames in some download directory every second and exits once they are complete or if it takes longer than 20 seconds to finish. The returned download time was used to check if the downloads were successful or if it timed out.
import time
import os
def download_wait(path_to_downloads):
seconds = 0
dl_wait = True
while dl_wait and seconds < 20:
time.sleep(1)
dl_wait = False
for fname in os.listdir(path_to_downloads):
if fname.endswith('.crdownload'):
dl_wait = True
seconds += 1
return seconds
I believe that this only works with chrome files as they end with the .crdownload extension. There may be a similar way to check in other browsers.
Edit: I recently changed the way that I use this function for times that .crdownload does not appear as the extension. Essentially this just waits for the correct number of files as well.
def download_wait(directory, timeout, nfiles=None):
"""
Wait for downloads to finish with a specified timeout.
Args
----
directory : str
The path to the folder where the files will be downloaded.
timeout : int
How many seconds to wait until timing out.
nfiles : int, defaults to None
If provided, also wait for the expected number of files.
"""
seconds = 0
dl_wait = True
while dl_wait and seconds < timeout:
time.sleep(1)
dl_wait = False
files = os.listdir(directory)
if nfiles and len(files) != nfiles:
dl_wait = True
for fname in files:
if fname.endswith('.crdownload'):
dl_wait = True
seconds += 1
return seconds
- 73
- 1
- 8
- 915
- 9
- 11
-
Way better than the accepted answer (that actually provides no solution at all). – Edmond Dec 04 '21 at 10:30
There is no built-in to selenium way to wait for the download to be completed.
The general idea here would be to wait until a file would appear in your "Downloads" directory.
This might either be achieved by looping over and over again checking for file existence:
Or, by using things like watchdog to monitor a directory:
import os
import time
def latest_download_file():
path = r'Downloads folder file path'
os.chdir(path)
files = sorted(os.listdir(os.getcwd()), key=os.path.getmtime)
newest = files[-1]
return newest
fileends = "crdownload"
while "crdownload" == fileends:
time.sleep(1)
newest_file = latest_download_file()
if "crdownload" in newest_file:
fileends = "crdownload"
else:
fileends = "none"
This is a combination of a few solutions. I didn't like that I had to scan the entire downloads folder for a file ending in "crdownload". This code implements a function that pulls the newest file in downloads folder. Then it simply checks if that file is still being downloaded. Used it for a Selenium tool I am building worked very well.
- 111
- 1
- 3
I know its too late for the answer, though would like to share a hack for future readers.
You can create a thread say thread1 from main thread and initiate your download here. Now, create some another thread, say thread2 and in there ,let it wait till thread1 completes using join() method.Now here,you can continue your flow of execution after download completes.
Still make sure you dont initiate your download using selenium, instead extract the link using selenium and use requests module to download.
Download using requests module
For eg:
def downloadit():
#download code here
def after_dwn():
dwn_thread.join() #waits till thread1 has completed executing
#next chunk of code after download, goes here
dwn_thread = threading.Thread(target=downloadit)
dwn_thread.start()
metadata_thread = threading.Thread(target=after_dwn)
metadata_thread.start()
- 462
- 5
- 19
As answered before, there is no native way to check if download is finished. So here is a helper function that does the job for Firefox and Chrome. One trick is to clear the temp download folder before start a new download. Also, use native pathlib for cross-platform usage.
from pathlib import Path
def is_download_finished(temp_folder):
firefox_temp_file = sorted(Path(temp_folder).glob('*.part'))
chrome_temp_file = sorted(Path(temp_folder).glob('*.crdownload'))
downloaded_files = sorted(Path(temp_folder).glob('*.*'))
if (len(firefox_temp_file) == 0) and \
(len(chrome_temp_file) == 0) and \
(len(downloaded_files) >= 1):
return True
else:
return False
- 321
- 3
- 6
Check for "Unconfirmed" key word in file name in download directory:
# wait for download complete
wait = True
while(wait==True):
for fname in os.listdir('\path\to\download directory'):
if ('Unconfirmed') in fname:
print('downloading files ...')
time.sleep(10)
else:
wait=False
print('finished downloading all files ...')
As soon as the the filed download is completed it exits the while loop.
- 992
- 2
- 15
- 26
- 171
- 5
- 16
x1=0
while x1==0:
count=0
li = os.listdir("directorypath")
for x1 in li:
if x1.endswith(".crdownload"):
count = count+1
if count==0:
x1=1
else:
x1=0
This works if you are trying to check if a set of files(more than one) have finished downloading.
- 21
- 1
this worked for me:
fileends = "crdownload"
while "crdownload" in fileends:
sleep(1)
for fname in os.listdir(some_path):
print(fname)
if "crdownload" in fname:
fileends = "crdownload"
else:
fileends = "None"
- 37
- 6
If using Selenium and Chrome, you can write a custom wait condition such as:
class file_has_been_downloaded(object):
def __init__(self, dir, number):
self.dir = dir
self.number = number
def __call__(self, driver):
print(count_files(dir), '->', self.number)
return count_files(dir) > self.number
The function count_files just verifies that the file has been added to the folder
def count_files(direct):
for root, dirs, files in os.walk(direct):
return len(list(f for f in files if f.startswith('MyPrefix') and (
not f.endswith('.crdownload')) ))
Then to implement this in your code:
files = count_files(dir)
<< copy the file. Possibly use shutil >>
WebDriverWait(driver, 30).until(file_has_been_downloaded(dir, files))
- 11
- 2
With Chrome, files which have not finished downloading have the extension .crdownload. If you set your download directory properly, then you can wait until the file that you want no longer has this extension. In principle, this is not much different to waiting for file to exist (as suggested by alecxe) - but at least you can monitor progress in this way.
- 839
- 8
- 17
I got a better one though:
So redirect the function that starts the download. e.g. download_function= lambda: element.click()
than check number of files in directory and wait for a new file that doesnt have the download extension. After that rename it. (can be change to move the file instead of renaming it in the same directory)
def save_download(self, directory, download_function, new_name, timeout=30):
"""
Download a file and rename it
:param directory: download location that is set
:param download_function: function to start download
:param new_name: the name that the new download gets
:param timeout: number of seconds to wait for download
:return: path to downloaded file
"""
self.logger.info("Downloading " + new_name)
files_start = os.listdir(directory)
download_function()
wait = True
i = 0
while (wait or len(os.listdir(directory)) == len(files_start)) and i < timeout * 2:
sleep(0.5)
wait = False
for file_name in os.listdir(directory):
if file_name.endswith('.crdownload'):
wait = True
if i == timeout * 2:
self.logger.warning("Documents not downloaded")
raise TimeoutError("File not downloaded")
else:
self.logger.info("Downloading done")
new_file = [name for name in os.listdir(directory) if name not in files_start][0]
self.logger.info("New file found renaming " + new_file + " to " + new_name)
while not os.access(directory + r"\\" + new_file, os.W_OK):
sleep(0.5)
self.logger.info("Waiting for write permission")
os.rename(directory + "\\" + new_file, directory + "\\" + new_name)
return directory + "\\" + new_file
-
I like the look of this. A little more complex than some other answers, but looks to be more robust. To adapt this for Firefox, look for files ending with .part instead of .crdownload It might be worth using os.path.join instead of hardcoding "\\" to avoid the Windows vs Unix slash direction issue. – OscarVanL Jun 02 '20 at 00:10
-
There's a bug in your code. You never increment the value of i. So if a download fails it will get stuck in the i < timeout * 2 condition. – OscarVanL Jun 03 '20 at 02:52
-
I also found that every download would timeout if in a previous execution the browser crashed before finishing the download leaving a leftover .crdownload file in the downloads folder. – OscarVanL Jun 03 '20 at 03:11
create a function that uses "requests" to get the file content and call that one, your program will not move forward unless the file is downloaded
import requests
from selenium import webdriver
driver = webdriver.Chrome()
# Open the website
driver.get(website_url_)
x = driver.find_element_by_partial_link_text('download')
y = x.get_attribute("href")
fc = requests.get(y)
fname = x.text
with open(fname, 'wb') as f:
f.write(fc.content)
- 1