Используйте Python для параллельной загрузки нескольких файлов (или URL-адресов)

Используйте Python для параллельной загрузки файлов или URL-адресов.

Получите больше данных за меньшее время

Фотография от Wesley Tingey на Unsplash

Мы живем в мире больших данных. Часто большие данные организованы как большая коллекция маленьких наборов данных (т.е. один большой набор данных, состоящий из нескольких файлов). Получение этих данных часто вызывает разочарование из-за загрузки (или получения). К счастью, с помощью небольшого кода существуют способы автоматизировать и ускорить скачивание и получение файлов.

Автоматизация скачивания файлов может сэкономить много времени. Существует несколько способов автоматизировать скачивание файлов с помощью Python. Самый простой способ скачивать файлы – использовать простой цикл Python для перебора списка URL-адресов для скачивания. Этот последовательный подход может хорошо работать с несколькими маленькими файлами, но если вы скачиваете много файлов или большие файлы, вам понадобится параллельный подход для максимизации вычислительных ресурсов.

С помощью параллельного механизма скачивания файлов вы можете лучше использовать ресурсы вашего компьютера для одновременной загрузки нескольких файлов, что позволит вам сэкономить время. В этом руководстве показано, как разработать общую функцию для скачивания файлов на Python и применить ее для скачивания нескольких файлов последовательно и параллельно. В коде этого руководства используются только модули, доступные в стандартной библиотеке Python, поэтому не требуется установка дополнительных модулей.

Импорт модулей

Для этого примера нам понадобятся только модули Python requests и multiprocessing для параллельного скачивания файлов. Модули requests и multiprocessing доступны в стандартной библиотеке Python, поэтому вам не нужно выполнять какие-либо установки.

Мы также импортируем модуль time, чтобы отслеживать время, затраченное на скачивание отдельных файлов, и сравнивать производительность между последовательным и параллельным механизмами загрузки. Модуль time также является частью стандартной библиотеки Python.

import requests import time from multiprocessing import cpu_count from multiprocessing.pool import ThreadPool

Определение URL-адресов и имен файлов

Я продемонстрирую параллельное скачивание файлов на Python с использованием файлов gridMET NetCDF, которые содержат данные о ежедневных осадках для Соединенных Штатов.

Здесь я указываю URL-адреса четырех файлов в списке. В других приложениях вы можете программно генерировать список файлов для скачивания.

urls = ['https://www.northwestknowledge.net/metdata/data/pr_1979.nc', 'https://www.northwestknowledge.net/metdata/data/pr_1980.nc', 'https://www.northwestknowledge.net/metdata/data/pr_1981.nc', 'https://www.northwestknowledge.net/metdata/data/pr_1982.nc']

Каждому URL-адресу должно соответствовать его местоположение для скачивания. Здесь я загружаю файлы в каталог «Загрузки» в Windows. Я жестко закодировал имена файлов в список для простоты и ясности. В зависимости от вашего приложения, вам может понадобиться написать код, который будет анализировать входной URL-адрес и загружать его в определенный каталог.

fns = [r'C:\Users\konrad\Downloads\pr_1979.nc', r'C:\Users\konrad\Downloads\pr_1980.nc', r'C:\Users\konrad\Downloads\pr_1981.nc', r'C:\Users\konrad\Downloads\pr_1982.nc']

Для использования механизма многопоточности требуется, чтобы параллельные функции имели только один аргумент (есть некоторые обходные пути, но мы не будем вдаваться в подробности). Чтобы скачать файл, нам понадобится передать два аргумента: URL-адрес и имя файла. Поэтому мы объединим списки urls и fns, чтобы получить список кортежей. Каждый кортеж в списке будет содержать два элемента: URL-адрес и имя файла для загрузки. Таким образом, мы можем передать один аргумент (кортеж), который содержит две части информации.

inputs = zip(urls, fns)

Функция для скачивания URL-адреса

Теперь, когда мы указали URL-адреса для скачивания и соответствующие имена файлов, нам нужна функция для скачивания URL-адресов ( download_url).

Мы передадим один аргумент ( arg) в функцию download_url. Этот аргумент будет итерируемым объектом (список или кортеж), где первый элемент – URL-адрес для скачивания ( url), а второй элемент – имя файла ( fn). Эти элементы присваиваются переменным ( url и fn) для удобочитаемости.

Теперь создайте оператор try, в котором URL извлекается и записывается в файл после его создания. При записи файла возвращаются URL и время загрузки. Если происходит исключение, выводится сообщение.

Функция download_url является основой нашего кода. Она фактически выполняет загрузку и создание файла. Теперь мы можем использовать эту функцию для загрузки файлов последовательно (с использованием цикла) и параллельно. Рассмотрим эти примеры.

def download_url(args):   t0 = time.time()   url, fn = args[0], args[1]   try:     r = requests.get(url)     with open(fn, 'wb') as f:       f.write(r.content)       return(url, time.time() - t0)   except Exception as e:     print('Исключение в download_url():', e)

Загрузка нескольких файлов с помощью цикла Python

Чтобы загрузить список URL-адресов в связанные файлы, переберите итерируемый объект (inputs), который мы создали, передавая каждый элемент в функцию download_url. После завершения каждой загрузки мы будем выводить загруженный URL и время загрузки.

Общее время загрузки всех URL-адресов будет выводиться после завершения всех загрузок.

t0 = time.time() for i in inputs:   result = download_url(i)   print('url:', result[0], 'time:', result[1])   print('Общее время:', time.time() - t0)

Вывод:

url: https://www.northwestknowledge.net/metdata/data/pr_1979.nc time: 16.381176710128784 url: https://www.northwestknowledge.net/metdata/data/pr_1980.nc time: 11.475878953933716 url: https://www.northwestknowledge.net/metdata/data/pr_1981.nc time: 13.059367179870605url: https://www.northwestknowledge.net/metdata/data/pr_1982.nc time: 12.232381582260132 Общее время: 53.15849542617798

На загрузку каждого отдельного файла ушло от 11 до 16 секунд. Общее время загрузки было немного меньше минуты. Ваши времена загрузки будут варьироваться в зависимости от вашего конкретного сетевого подключения.

Давайте сравним этот последовательный (циклический) подход с параллельным подходом ниже.

Загрузка нескольких файлов параллельно с помощью Python

Для начала создайте функцию (download_parallel), которая будет обрабатывать параллельную загрузку. Функция (download_parallel) будет принимать один аргумент – итерируемый объект, содержащий URL-адреса и связанные имена файлов (переменная inputs, которую мы создали ранее).

Затем получите количество доступных процессоров для обработки. Это определит количество потоков, которые будут запущены параллельно.

Теперь используйте multiprocessing ThreadPool, чтобы сопоставить inputs с функцией download_url. Здесь мы используем метод imap_unordered из ThreadPool и передаем ему функцию download_url и аргументы ввода для download_url (переменная inputs). Метод imap_unordered будет выполнять download_url одновременно для указанного количества потоков (т.е. параллельная загрузка).

Таким образом, если у нас четыре файла и четыре потока, все файлы могут быть загружены одновременно, а не ждать завершения одной загрузки перед началом другой. Это может сэкономить значительное количество времени обработки.

В заключительной части функции download_parallel будут выведены загруженные URL-адреса и время, необходимое для загрузки каждого URL-адреса.

def download_parallel(args):   cpus = cpu_count()   results = ThreadPool(cpus - 1).imap_unordered(download_url, args)   for result in results:     print('url:', result[0], 'time (s):', result[1])

После определения inputs и download_parallel файлы могут быть загружены параллельно с помощью одной строки кода.

download_parallel(inputs)

Вывод:

url: https://www.northwestknowledge.net/metdata/data/pr_1980.nc время (с): 14.641696214675903 url: https://www.northwestknowledge.net/metdata/data/pr_1981.nc время (с): 14.789752960205078 url: https://www.northwestknowledge.net/metdata/data/pr_1979.nc время (с): 15.052601337432861 url: https://www.northwestknowledge.net/metdata/data/pr_1982.nc время (с): 23.287317752838135 Общее время: 23.32273244857788

Обратите внимание, что с подходом к загрузке каждого отдельного файла ушло больше времени. Это может быть результатом изменения скорости сети или накладных расходов, необходимых для отображения загрузок на соответствующие потоки. Несмотря на то, что загрузка отдельных файлов заняла больше времени, параллельный метод привел к сокращению общего времени загрузки на 50%.

Вы можете увидеть, как параллельная обработка может значительно сократить время обработки для нескольких файлов. С увеличением количества файлов вы сэкономите гораздо больше времени, используя параллельный подход к загрузке.

Вывод

Автоматизация загрузки файлов в ваших процедурах разработки и анализа может сэкономить вам много времени. Как показано в этом учебнике, реализация параллельной загрузки может значительно сократить время получения файлов, если вам требуется много файлов или большие файлы.

Опубликовано на https://opensourceoptions.com.