Python
Selenium Python: как скачать файлы в цикле
В Selenium файлы скачивают в цикле, когда на странице есть несколько ссылок или кнопок загрузки: отчеты, PDF-документы, счета, выгрузки из кабинета. Главная сложность не в самом цикле, а в трех вещах: заранее настроить папку загрузки, дождаться появления элементов и проверить, что файл действительно докачался
Если у файла есть прямой адрес, часто проще скачать его через requests. Selenium нужен тогда, когда загрузка появляется после авторизации, клика по кнопке, открытия кабинета или работы JavaScript
По этой теме полезно отдельно посмотреть Составьте еженедельное расписание занятий, чтобы расширить контекст и сравнить подходы
По этой теме полезно отдельно посмотреть Аннотации типов для декораторов в Python, чтобы расширить контекст и сравнить подходы
Настройка папки загрузки
Для Chrome папку загрузки задают через ChromeOptions
from pathlib import Path
from selenium import webdriver
download_dir = Path.cwd() / 'downloads'
download_dir.mkdir(exist_ok=True)
options = webdriver.ChromeOptions()
options.add_experimental_option('prefs', {
'download.default_directory': str(download_dir),
'download.prompt_for_download': False,
'download.directory_upgrade': True,
'safebrowsing.enabled': True,
})
driver = webdriver.Chrome(options=options)
Так браузер будет складывать файлы в папку downloads рядом со скриптом и не будет каждый раз показывать окно выбора папки
На macOS, Windows и Linux путь должен быть абсолютным. Поэтому в примере используется Path.cwd() / 'downloads', а в настройки передается str(download_dir)
Скачать все ссылки в цикле
Минимальный пример:
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
driver.get('https://example.com/files')
wait = WebDriverWait(driver, 20)
links = wait.until(
EC.presence_of_all_elements_located((By.CSS_SELECTOR, 'a.download'))
)
for link in links:
link.click()
Этот код ждет, пока на странице появятся ссылки с классом download, а затем кликает по каждой ссылке
Если страница динамическая, не используйте find_elements() сразу после get(). Элементы могут еще не появиться, и цикл пройдет по пустому списку
Дождаться окончания загрузки
Кликнуть по ссылке недостаточно. Chrome во время скачивания создает временный файл с расширением .crdownload. Пока такой файл есть в папке, загрузка еще не закончилась
import time
from pathlib import Path
def wait_downloads_finished(folder: Path, timeout: int = 60):
start = time.time()
while time.time() - start < timeout:
temp_files = list(folder.glob('*.crdownload'))
if not temp_files:
return
time.sleep(0.5)
raise TimeoutError('Файлы не успели скачаться')
После кликов можно вызвать:
wait_downloads_finished(download_dir)
Это простая проверка для Chrome. Если вы используете другой браузер, временное расширение может отличаться
Полный пример
import time
from pathlib import Path
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
def wait_downloads_finished(folder: Path, timeout: int = 60):
start = time.time()
while time.time() - start < timeout:
if not list(folder.glob('*.crdownload')):
return
time.sleep(0.5)
raise TimeoutError('Файлы не успели скачаться')
download_dir = Path.cwd() / 'downloads'
download_dir.mkdir(exist_ok=True)
options = webdriver.ChromeOptions()
options.add_experimental_option('prefs', {
'download.default_directory': str(download_dir),
'download.prompt_for_download': False,
'download.directory_upgrade': True,
'safebrowsing.enabled': True,
})
driver = webdriver.Chrome(options=options)
wait = WebDriverWait(driver, 20)
try:
driver.get('https://example.com/files')
links = wait.until(
EC.presence_of_all_elements_located((By.CSS_SELECTOR, 'a.download'))
)
for link in links:
link.click()
wait_downloads_finished(download_dir)
finally:
driver.quit()
try/finally нужен, чтобы браузер закрылся даже при ошибке. Без driver.quit() могут остаться висящие процессы Chrome
Скачать только PDF
Если на странице много ссылок, можно отфильтровать только PDF
links = wait.until(
EC.presence_of_all_elements_located((By.CSS_SELECTOR, 'a[href]'))
)
for link in links:
href = link.get_attribute('href') or ''
if not href.lower().endswith('.pdf'):
continue
link.click()
wait_downloads_finished(download_dir)
Если ссылка ведет прямо на файл и не требует клика в браузере, лучше скачать через requests
import requests
response = requests.get(href, timeout=30)
response.raise_for_status()
file_name = href.rsplit('/', 1)[-1]
(download_dir / file_name).write_bytes(response.content)
Так быстрее и надежнее, чем управлять браузером. Selenium оставляйте для случаев, где без браузерного сценария не обойтись
Клик по кнопкам загрузки
Иногда загрузка висит не на ссылке, а на кнопке
buttons = wait.until(
EC.presence_of_all_elements_located((By.CSS_SELECTOR, 'button.download'))
)
for button in buttons:
wait.until(EC.element_to_be_clickable(button))
button.click()
wait_downloads_finished(download_dir)
Если кнопки исчезают или перерисовываются после клика, заранее собранный список элементов может устареть. Тогда лучше каждый раз заново искать следующую кнопку или собирать стабильные ссылки из атрибутов
Частые ошибки
Не настроить папку загрузки и потом искать файлы в системной папке Downloads
Кликать до того, как элементы появились на странице
Не ждать завершения загрузки и работать с недокачанным файлом
Использовать Selenium для прямых ссылок, которые быстрее скачать через requests
Не закрывать браузер через driver.quit()
Нарушать правила сайта или скачивать файлы без разрешения. Автоматизация должна использоваться только там, где у вас есть право на доступ и выгрузку данных
Практические задачи
Настройте папку downloads и проверьте, что файлы попадают именно туда
Скачайте только ссылки, у которых href заканчивается на .pdf
Добавьте вывод имени каждого скачанного файла после завершения загрузки
Переделайте прямые PDF-ссылки на скачивание через requests
Добавьте обработку случая, когда на странице не найдено ни одной ссылки
Чтобы скачать файлы в цикле через Selenium, настройте папку загрузки, дождитесь элементов через WebDriverWait, кликайте по ссылкам или кнопкам и проверяйте завершение скачивания по отсутствию временных файлов
Если файл доступен по прямой ссылке, используйте requests. Selenium нужен для страниц, где загрузка зависит от браузера, авторизации, JavaScript или пользовательского действия