сжатие картинок с помощью Python на FTP

Пакетное сжатие картинок с помощью Python на FTP

Google не дает расслабиться бедным сеошникам. То скорость загрузки сайта не такая, то еще что-то. Одной из значимых вещей кроме всего прочего является вес картинок. А значит сегодня мы будем изобретать сжатие картинок с помощью Python на FTP. Присоединяйтесь, будет весело!

К сожалению, консольных утилит, которые сжимают изображение по заданным параметрам осталось не так много. Изучение данного вопроса позволило найти несколько рабочих программ, но самой интересной мне показалась ImageMagic от студии ImageMagick Studio LLC. Программа кроме Windows-интерфейса имеет очень обширный функционал, доступный в режиме командной строки. А значит — задача сводится к:

  • Соединению с FTP-сервером
  • Определение папки для работы (была мысль сделать программу, которая бы работала полностью автоматически — но вдруг пользователь предпочтет оптимизировать картинки только в определенном каталоге? Вообщем, пока есть, что есть — в случае необходимости код можно доработать до режима полной автоматики
  • Определяем расширение файла. Если это графический файл (jpg|png) — копируем его в папку с кодом программы, где так же находится программа для обработки изображения
  • Обрабатываем картинку по следующей схеме: оригинальную картинку сжимаем в новый файл с именем исходного файла|переименовываем в формате: orig_имяфайла
  • Отправляем на FTP-сервер обработанную картинку (которая имеет имя оригинального файла), и оригинальную картинку (на всякий случай пусть будет)
  • Удаляем из папки на жестком диске все картинки
  • Повторяем цикл

В виде кода весь описанный алгоритм выглядит так (не хочу расписывать его построчно — есть комментарии к бОльшей части кода):

  1. #Programm for resize pictures here: http://lavrynenko.com/wp-content/uploads/2019/01/magick.exe
  2. import ftplib #Подключаем библиотеку для работы с FTP
  3. import os #Подключаем библиотеку для работы с локальной файловой системой
  4. import sys #Подключаем библиотеку, которая обеспечивает доступ к некоторым функциям (чуть ниже)
  5. import subprocess #Подключаем библиотеку, которая позволяет запускать внешние процессы из программы
  6.  
  7. def autorization(): #Объявляем функцию, которая отвечает за FTP-авторизацию
  8.     host = str(input('Host?: ')) #FTP 
  9.     ftp_user = str(input('User?: ')) #Login
  10.     ftp_password = str(input('Password?: ')) #Password
  11.  
  12.     #После чего каждую переменную подключим к авторизации:
  13.     print('Попытка соединения с FTP-сервером', host)
  14.     print('Login:', ftp_user)
  15.     print('Password:', ftp_password)
  16.     ftp = ftplib.FTP(host, ftp_user, ftp_password)
  17.     welcome_text = ftp.getwelcome()
  18.     print(welcome_text)  # Вывели на экран Welcome-сообщение сервера
  19.     directory_list = ftp.nlst()  # загоняем в переменную list список содержимого директории
  20.     print('Структура информации на сервере:\n')
  21.     for directory in directory_list:
  22.         print(directory)
  23.     print('\nСоздаем список каталогов\n')
  24.     directory_listing = []
  25.     catalog_or(ftp, directory_listing)
  26.  
  27. def catalog_or(ftp, directory_listing):
  28.     directory_list = ftp.nlst()  # загоняем в переменную list список содержимого директории
  29.     print('Структура информации на сервере:\n')
  30.     for directory in directory_list:
  31.         print(directory)
  32.     print('\nСоздаем список папок\n')
  33.     directory_listing = [] #Создаем список папок
  34.     for director in directory_list: #Начинаем цикл проверки - папка или нет
  35.         try: #Ветка - если правда
  36.             ftp.cwd(director) #Если получается входить в папку
  37.             directory_listing.append(director) #Добавляем имя папки в список папок
  38.             ftp.sendcmd('cdup') #Выходим на уровень выше
  39.         except: #Ветка - если проблема
  40.             ftplib.error_perm: 550 #Получаем ошибку, возвращаемся назад
  41.  
  42.     directory_listing = '\\'.join(directory_listing) #Чистим список
  43.     print('На сервере имеются следующие папки:\n', directory_listing, '\n') #Выводим информацию - какие папки имеются на сервере
  44.     menu(ftp, directory_listing) #Уходим в функцию МЕНЮ
  45.  
  46. def menu(ftp, directory_listing): #Объявляем функцию, которая отвечает за меню работы с сервером
  47.     menu_directory = int(input(' 1 - Выбрать каталог для работы\n 2 - Выйти вверх\n 3 - Работать в текущем каталоге\n >>> '))
  48.     if menu_directory == 1: #Если выбран пункт 1
  49.         down(ftp, directory_listing) #Запускаем функцию Down
  50.     elif menu_directory == 2: #Если выбран пункт 2
  51.         up(ftp, directory_listing)  #Запускаем функцию Up
  52.     elif menu_directory == 3: #Если выбран пункт 3
  53.         work(ftp, directory_listing) #Запускаем функцию Work
  54.  
  55. def up(ftp, directory_listening): #Функция Up
  56.     print('Выходим вверх') #Выводим сообщение
  57.     ftp.sendcmd('cdup') #Выполняем выход из папки на уровень вверх
  58.     catalog_or(ftp, directory_listening) #Запускаем функцию проверки - каталог или нет
  59.  
  60. def down(ftp, directory_listening): #Функция Down
  61.     print('Войти в каталог \n')
  62.     work_directory = str(input('Введите название папки\n >>> ')) #Получаем название папки, куда нужно войти
  63.     print('Входим в папку:', work_directory) #Выводим сообщение
  64.     ftp.cwd(work_directory) #Входим в нужную папку
  65.     print(ftp.nlst()) #Выводим на экран содержимое папки
  66.     catalog_or(ftp, directory_listening) #Запускаем проверку - папки или нет
  67.  
  68. def work(ftp, directory_listening): #Функция Work
  69.     print('Работаем!') #Выводим сообщение :) 
  70.  
  71.     # Определяем место, где находится сам код программы
  72.     print('sys.argv[0] =', sys.argv[0])
  73.     pathname = os.path.dirname(sys.argv[0])
  74.     print('Программа находится тут:', pathname)
  75.     print('Полный путь к программе:', os.path.abspath(pathname))
  76.  
  77.     #Получаем список всего содержимого в папке на сервере
  78.     directory_list = ftp.nlst()  # загоняем в переменную list полный список содержимого директории
  79.     print('Структура информации на сервере:\n', directory_list)
  80.  
  81.     #Формируем список только файлов
  82.     file_list = [] #список только файлов
  83.     directory_listing = [] #список только папок
  84.     for director in directory_list:
  85.         try: #Ветка - если правда
  86.             ftp.cwd(director) #Входим в папку
  87.             directory_listing.append(director) #Если это получается - добавляем название папки в список папок
  88.             ftp.sendcmd('cdup') #Выходим на уровень вверх
  89.         except: #Ветка - если фигня
  90.             ftplib.error_perm: 550 #Получаем ошибку :( ничего не делаем
  91.     print('Папки:', directory_listing) #Выводим сообщение - какие папки имеются
  92.     file_list = list(set(directory_list) - set(directory_listing)) #Сравниваем два списка - папок и всего имеющегося. Что различается - считаем файлами
  93.     print('Только файлы:', file_list, '\n') #Выводим информацию об этом
  94.  
  95.     #Определяем расширение файла
  96.     for file in file_list: #Для каждого файла в списке файлов
  97.         file_extention = file.split('.')[-1] #Получаем все, что после точки
  98.  
  99.         if file_extention == 'jpg': #Ветка для расширения .jpg
  100.             print(file, ' = jpg') #Выводим информацию, что файл имеет это расширение
  101.             print('Загружаем файл', file, 'c FTP') #Выводим информацию
  102.             with open(file, 'wb') as local_file: #Открываем локальный файл
  103.                 ftp.retrbinary('retr ' + file, local_file.write) #Пишем в локальный файл информацию из файла на сервере
  104.                 print(file, 'загружен') #Выводим информацию
  105.  
  106.             orig_file = 'orig_' + file #Получаем переменную, которая будет содержать новое имя файла
  107.             os.rename(file, orig_file) #Переименовываем исходный файл
  108.             subprocess.run('{} {} {} {} {}'.format('magick.exe', orig_file, '-quality', '50', file)) #Обрабатываем исходную картинку
  109.             print(file, 'обработан') #Выводим сообщение
  110.  
  111.             #А теперь загоняем на FTP обработанный файл + оригинальный
  112.             with open(file, 'rb') as file_to_upload: #Открываем локальный обработанный файл
  113.                 ftp.storbinary('stor ' + file, file_to_upload) #Загружаем на сервер локальный файл
  114.                 print(file, 'успешно загружен на FTP') #Выводим сообщение
  115.  
  116.             with open(orig_file, 'rb') as file_to_upload: #Открываем локальный исходный файл
  117.                 ftp.storbinary('stor ' + orig_file, file_to_upload) #Выгружаем на сервер исходный файл
  118.                 print(orig_file, 'успешно загружен на FTP') #Выводим сообщение
  119.  
  120.             file_size = os.path.getsize(file) #Получаем объем обработанного файла
  121.             orig_file_size = os.path.getsize(orig_file) #Получаем объем исходного файла
  122.             print('\n Файл', file, 'занимал', orig_file_size, 'kb, а теперь занимает', file_size, 'kb.') #Показываем, на сколько именно килобайт похудел файл
  123.             print(' Экономим:', orig_file_size - file_size, 'kb\n') #Радуем информацией, сколько места сэкономили
  124.  
  125.             #Убираем файлы на компьютере
  126.             os.remove(orig_file) #Удаляем исходный файл
  127.             print(orig_file, 'удален') #Выводим сообщение
  128.             os.remove(file) #Удаляем файл
  129.             print(file, 'удален\n') #Выводим сообщение
  130.  
  131.         elif file_extention == 'png': #Ветка - если файл с расширением png
  132.             print(file, ' = png') #Выводим сообщение, что файл имеет это расширение
  133.             print('Загружаем файл', file, 'c FTP') #Выводим сообщение, что загружаем файл с сервера
  134.             with open(file, 'wb') as local_file: #Открываем локальный файл для записи
  135.                 ftp.retrbinary('retr ' + file, local_file.write) #Читаем файл с сервера, пишем в локальный файл
  136.                 print(file, 'загружен') #Выводим сообщение
  137.  
  138.             orig_file = 'orig_' + file #Получаем переменную, которая состоит из имени исходного файла + приставка
  139.             os.rename(file, orig_file) #Переименовываем исходный файл
  140.             subprocess.run('{} {} {} {} {}'.format('magick.exe', orig_file, '-quality', '91', file)) #Обрабатываем переименованный файл так, что бы получился обработанный файл с именем исходного
  141.             print(file, 'обработан') #Выводим сообщение
  142.  
  143.             #А теперь загоняем на FTP обработанный файл + оригинальный
  144.             with open(file, 'rb') as file_to_upload: #Открываем локальный файл для чтения
  145.                 ftp.storbinary('stor ' + file, file_to_upload) #Перебрасываем файл на сервер
  146.                 print(file, 'успешно загружен на FTP') #Выводим сообщение
  147.  
  148.             with open(orig_file, 'rb') as file_to_upload: #Открываем локальный файл для чтения
  149.                 ftp.storbinary('stor ' + orig_file, file_to_upload) #Перебрасываем файл на сервер
  150.                 print(orig_file, 'успешно загружен на FTP') #Выводим сообщение
  151.  
  152.             file_size = os.path.getsize(file) #Определяем размер исходного файла
  153.             orig_file_size = os.path.getsize(orig_file) #Определяем размер полученного файла
  154.             print('\n Файл', file, 'занимал', orig_file_size, 'kb, а теперь занимает', file_size, 'kb.') #Радуем пользователя информацией о том, что файл сжался
  155.             print(' Экономим:', orig_file_size - file_size, 'kb\n') #Показываем - на сколько именно он сжался :) 
  156.  
  157.             #Чистим за собой мусор
  158.             os.remove(orig_file) #Удаляем исходный файл
  159.             print(orig_file, 'удален')
  160.             os.remove(file) #Удаляем полученный файл
  161.             print(file, 'удален\n')
  162.         elif file_extention != 'png' or 'jpg':
  163.             print(file, ' = другое расширение\n')
  164.  
  165. def index():
  166.     autorization()
  167.  
  168. index()

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

Из запланированных доработок хочется:

  • Оптимизировать код — многие части повторяются
  • Сделать полностью автоматическую версию с возможностью выбора режима работы
  • Навести порядок со статистикой — что бы в конце работы выдавался результат «Итого»
  • Окультурить визуальную выдачу 🙂

Сам код в полном виде доступен тут

Как всегда — в случае вопросов задавайте их на почту, или в Telegram 🙂

Спасибо за внимание! И не забывайте тестировать 🙂