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

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

Автоматизация загрузки файлов может сэкономить много времени. Существует несколько способов автоматизировать загрузку файлов с помощью 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 с использованием файлов NetCDF gridMET, которые содержат ежедневные данные об осадках в США.

Здесь я указываю 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-адрес и имя файла загрузки для 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('Exception in 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('Total time:', 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.059367179870605
url: https://www.northwestknowledge.net/metdata/data/pr_1982.nc time: 12.232381582260132 
Total time: 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 time (s): 14.641696214675903 
url: https://www.northwestknowledge.net/metdata/data/pr_1981.nc time (s): 14.789752960205078 
url: https://www.northwestknowledge.net/metdata/data/pr_1979.nc time (s): 15.052601337432861 
url: https://www.northwestknowledge.net/metdata/data/pr_1982.nc time (s): 23.287317752838135 
Total time: 23.32273244857788

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

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

Заключение

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

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