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 или пользовательского действия

Подписаться
Уведомить о
guest
0 комментариев
Старые
Новые Популярные

Настройки cookie

0
Оставьте комментарий! Напишите, что думаете по поводу статьи.x