Яндекс.Метрика
Пишите 24/7, мы онлайн
+7 (495) 128-00-14

Каждый SEO-специалист, интернет-маркетолог или директолог знает: тяжелые картинки на сайте — это гарантированный провал в Google PageSpeed Insights, долгая загрузка посадочных страниц и ухудшение поведенческих факторов.

Обычно эту проблему решают двумя путями, и у каждого есть жирные минусы:

  • Онлайн-сервисы (вроде TinyPNG): имеют жесткие лимиты на количество и вес файлов в бесплатной версии, заставляют скачивать архивы, распаковывать их, а также забирают конфиденциальные макеты клиентов на свои сервера.
  • Плагины для CMS (WordPress, Bitrix и др.): часто платные, нагружают базу данных сайта лишними скриптами, замедляют админку и нередко оставляют скрытые ссылки или рекламу от разработчиков в коде.

Сегодня мы решим эту проблему раз и навсегда. Мы соберем собственное локальное веб-приложение на Python, которое будет работать полностью на вашем компьютере: бесплатно, без интернета и ограничений.

Вы просто выбираете пачку картинок в браузере, пишете им нормальные названия на русском языке (внедряя ключевые слова), а программа сама мгновенно сжимает их, переводит в современный формат WebP и генерирует идеальные латинские ЧПУ-урлы для Яндекса и Google.

Что нам понадобится для работы

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

Компонент Что это и где взять Зачем это нужно в проекте
Python Язык программирования. Скачивается с официального сайта python.org Главный вычислительный «движок», который будет обрабатывать графику и текст.
VS Code Удобный и бесплатный редактор кода от Microsoft. Скачивается с code.visualstudio.com Наша рабочая среда, где мы сохраним файл и запустим наше приложение.
Библиотека Pillow Модуль для Python. Устанавливается одной командой в терминале. Отвечает за «умное сжатие»: удаляет скрытый мета-мусор из файлов и жмет вес без потери качества.
Библиотека Flask Модуль для Python. Устанавливается одной командой в терминале. Создает локальный веб-сервер, чтобы у вас в браузере открылся красивый и удобный интерфейс с кнопками.

Шаг 1. Установка базового софта (Python и VS Code)

  1. Зайдите на сайт python.org, скачайте актуальную версию для Windows/Mac и запустите установщик.
  2. ⚠️ КРИТИЧЕСКИ ВАЖНО: В самом первом окне установщика внизу обязательно поставьте галочку напротив пункта «Add python.exe to PATH». Если этого не сделать, компьютер не поймет команды запуска! Нажмите Install Now и дождитесь окончания.
  3. Скачайте и установите VS Code, нажимая кнопку «Далее».

Шаг 2. Подготовка рабочей папки и установка библиотек

  1. Создайте на Рабочем столе обычную папку и назовите её, например, seo_converter.
  2. Откройте программу VS Code.
  3. В левом верхнем меню выберите File → Open Folder (Открыть папку) и укажите на созданную папку seo_converter.
  4. Теперь откроем командную строку. В верхнем меню VS Code выберите Terminal → New Terminal (Новый терминал). Внизу появится панель.
  5. Скопируйте следующую команду, вставьте её в терминал и нажмите Enter:
  6. python -m pip install Flask pillow
  7. Компьютер сам скачает нужные пакеты. Дождитесь появления финальной строки, подтверждающей успешную установку.

Шаг 3. Создание файла приложения и код

  1. В левой панели VS Code нажмите на иконку листка с плюсом (New File) и назовите файл строго web_app.py.
  2. Скопируйте весь код, приведенный ниже, и вставьте его в созданный файл:
  3. import os import re import webbrowser from threading import Timer from flask import Flask, request, render_template_string import flask.cli # Импортируем модуль интерфейса Flask для отключения баннера from PIL import Image # Полностью отключаем страшное красное предупреждение и системный баннер Flask flask.cli.show_server_banner = lambda *args: None app = Flask(__name__) # --- НАСТРОЙКИ --- OUTPUT_FOLDER = "ready_images" # Папка для готовых SEO-оптимизированных WebP QUALITY = 80 # Качество сжатия WebP PORT = 5000 # Порт для локального сервера # ------------------ if not os.path.exists(OUTPUT_FOLDER): os.makedirs(OUTPUT_FOLDER) # Функция правильной SEO-транслитерации def seo_translit(text): cyrillic_map = { 'а': 'a', 'б': 'b', 'в': 'v', 'г': 'g', 'д': 'd', 'е': 'e', 'ё': 'yo', 'ж': 'zh', 'з': 'z', 'и': 'i', 'й': 'j', 'к': 'k', 'л': 'l', 'м': 'm', 'н': 'n', 'о': 'o', 'п': 'p', 'р': 'r', 'с': 's', 'т': 't', 'у': 'u', 'ф': 'f', 'х': 'h', 'ц': 'c', 'ч': 'ch', 'ш': 'sh', 'щ': 'shch', 'ъ': '', 'ы': 'y', 'ь': '', 'э': 'e', 'ю': 'yu', 'я': 'ya' } text = text.lower().strip() result = [] for char in text: if char in cyrillic_map: result.append(cyrillic_map[char]) elif char.isalnum() or char == '-': result.append(char) elif char == ' ': result.append('-') res_str = "".join(result) res_str = re.sub(r'-+', '-', res_str) return res_str.strip('-') HTML_INTERFACE = ''' <!DOCTYPE html> <html lang="ru"> <head> <meta charset="UTF-8"> <title>SEO WebP Конвертер PRO</title> <style> body { font-family: 'Segoe UI', system-ui, sans-serif; background-color: #1e1e2e; color: #cdd6f4; display: flex; justify-content: center; align-items: center; min-height: 100vh; margin: 0; padding: 20px; } .container { background-color: #252538; padding: 40px; border-radius: 12px; box-shadow: 0 8px 24px rgba(0,0,0,0.3); text-align: center; width: 700px; } h1 { color: #a6e3a1; margin-bottom: 5px; font-size: 26px; } .sub { color: #a6adc8; font-size: 14px; margin-bottom: 25px; } .file-input-label { display: block; background-color: #89b4fa; color: #11111b; padding: 14px; border-radius: 8px; cursor: pointer; font-weight: bold; margin-bottom: 20px; transition: background 0.2s; } .file-input-label:hover { background-color: #b4befe; } input[type="file"] { display: none; } .file-table { width: 100%; margin-top: 20px; border-collapse: collapse; text-align: left; display: none; } .file-table th, .file-table td { padding: 12px 10px; border-bottom: 1px solid #45475a; vertical-align: middle; } .file-table th { color: #f5c2e7; font-size: 14px; } .thumb { width: 50px; height: 50px; object-fit: cover; border-radius: 6px; border: 1px solid #45475a; background-color: #1e1e2e; } .input-name { width: 92%; padding: 8px; border-radius: 6px; border: 1px solid #45475a; background-color: #1e1e2e; color: #cdd6f4; font-size: 14px; } .input-name:focus { border-color: #a6e3a1; outline: none; } .delete-btn { color: #f38ba8; font-size: 22px; cursor: pointer; font-weight: bold; user-select: none; transition: color 0.2s; display: block; text-align: center; } .delete-btn:hover { color: #d20f39; } button { background-color: #a6e3a1; color: #11111b; border: none; padding: 14px 30px; border-radius: 8px; font-size: 16px; font-weight: bold; cursor: pointer; width: 100%; margin-top: 25px; } button:hover { opacity: 0.9; } .status { margin-top: 20px; padding: 15px; background-color: #313244; border-left: 4px solid #a6e3a1; border-radius: 4px; text-align: left; color: #a6e3a1; font-size: 14px; line-height: 1.6; } .log-item { margin-bottom: 12px; border-bottom: 1px dashed #45475a; padding-bottom: 8px; } .log-item:last-child { margin-bottom: 0; border-bottom: none; padding-bottom: 0; } .log-stats { color: #fab387; font-size: 13px; } </style> </head> <body> <div class="container"> <h1>SEO WebP Конвертер</h1> <div class="sub">Визуальный контроль макетов и ЧПУ-транслитерация</div> <form method="POST" action="/upload" enctype="multipart/form-data"> <label class="file-input-label"> Выбрать исходные изображения (пачкой) <input type="file" id="image_input" name="images" accept=".png, .jpg, .jpeg, .webp" multiple required onchange="handleFiles(this)"> </label> <table class="file-table" id="table"> <thead> <tr> <th style="width: 10%;">Превью</th> <th style="width: 30%;">Исходный файл</th> <th style="width: 53%;">Новое название (на русском)</th> <th style="width: 7%; text-align: center;">Удал.</th> </tr> </thead> <tbody id="table_body"></tbody> </table> <button type="submit" id="submit_btn" style="display: none;">Сжать и перевести в WebP</button> </form> {% if logs %} <div class="status"> <strong style="color: #cdd6f4; display: block; margin-bottom: 15px;">Готово! Все файлы сохранены в папку /''' + OUTPUT_FOLDER + ''':</strong> {% for log in logs %} <div class="log-item">• {{ log | safe }}</div> {% endfor %} </div> {% endif %} </div> <script> let dtContainer = new DataTransfer(); function handleFiles(input) { const files = input.files; dtContainer = new DataTransfer(); for (let i = 0; i < files.length; i++) { dtContainer.items.add(files[i]); } renderTable(); } function renderTable(savedValues = []) { const tbody = document.getElementById('table_body'); const table = document.getElementById('table'); const submitBtn = document.getElementById('submit_btn'); tbody.innerHTML = ''; if (dtContainer.files.length > 0) { table.style.display = 'table'; submitBtn.style.display = 'block'; for (let i = 0; i < dtContainer.files.length; i++) { const file = dtContainer.files[i]; const tr = document.createElement('tr'); const imgUrl = URL.createObjectURL(file); const currentText = savedValues[i] || ''; tr.innerHTML = ` <td><img src="${imgUrl}" class="thumb" alt="preview"></td> <td style="word-break: break-all; font-size: 13px;">${file.name}</td> <td> <input type="text" name="custom_name_${i}" class="input-name" value="${currentText}" placeholder="Введите название на русском" required> </td> <td> <span class="delete-btn" onclick="removeFile(${i})">&times;</span> </td> `; tbody.appendChild(tr); } } else { table.style.display = 'none'; submitBtn.style.display = 'none'; } document.getElementById('image_input').files = dtContainer.files; } function removeFile(index) { const inputs = document.getElementsByClassName('input-name'); const savedValues = []; for (let i = 0; i < inputs.length; i++) { if (i !== index) { savedValues.push(inputs[i].value); } } const newDt = new DataTransfer(); for (let i = 0; i < dtContainer.files.length; i++) { if (i !== index) { newDt.items.add(dtContainer.files[i]); } } dtContainer = newDt; renderTable(savedValues); } </script> </body> </html> ''' @app.route('/') def index(): return render_template_string(HTML_INTERFACE) @app.route('/upload', methods=['POST']) def upload_files(): uploaded_files = request.files.getlist('images') logs = [] for index, file in enumerate(uploaded_files): if file.filename == '': continue russian_name = request.form.get(f'custom_name_{index}') if russian_name: clean_name = seo_translit(russian_name) else: clean_name = seo_translit(os.path.splitext(file.filename)[0]) if not clean_name: clean_name = f"image_{index + 1}" try: file.seek(0, os.SEEK_END) orig_size = file.tell() file.seek(0) orig_size_kb = orig_size / 1024 img = Image.open(file.stream) output_file_name = f"{clean_name}.webp" output_path = os.path.join(OUTPUT_FOLDER, output_file_name) img.save(output_path, format='WEBP', quality=QUALITY, optimize=True) new_size = os.path.getsize(output_path) new_size_kb = new_size / 1024 saved_percent = ((orig_size - new_size) / orig_size) * 100 if orig_size > 0 else 0 log_text = ( f"<strong>{file.filename}</strong> &rarr; <strong>{output_file_name}</strong><br>" f"<span class='log-stats'>Было: {orig_size_kb:.1f} KB | Стало: {new_size_kb:.1f} KB | Сжато на: {saved_percent:.1f}%</span>" ) logs.append(log_text) except Exception as e: logs.append(f"<span style='color: #f38ba8;'>Ошибка при обработке {file.filename}: {e}</span>") return render_template_string(HTML_INTERFACE, logs=logs) def open_browser(): webbrowser.open(f"http://127.0.0.1:{PORT}") if __name__ == '__main__': print(f"\n[Запуск] Локальный сервер Flask стартует...") print(f"[Успешно] Браузер сейчас откроется автоматически на http://127.0.0.1:{PORT}\n") Timer(1.0, open_browser).start() app.run(debug=False, port=PORT)
  4. Сохраните файл, нажав сочетание клавиш Ctrl + S

Шаг 4. Запуск и использование приложения

  1. Нажмите кнопку Play (маленький треугольник в правом верхнем углу VS Code) или введите команду python web_app.py в терминале и нажмите Enter.
  2. Система выдаст лог запуска, и на вашем компьютере автоматически откроется новая вкладка в браузере по адресу http://127.0.0.1:5000.
⚠️ Важное примечание: не пугайтесь красного текста!
При запуске скрипта в терминале VS Code вы увидите строчку яркого красного цвета: «WARNING: This is a development server...».
Не переживайте — это не ошибка, у вас всё работает правильно. Этим текстом встроенный движок Python просто сообщает, что программа запущена локально на вашем компьютере, а не на сервере в интернете. Инструмент полностью безопасен и готов к работе.

Как работает сам интерфейс:

  • Вы нажимаете на синюю кнопку и выбираете пачку изображений (например, 1.png, 2.jpg, которые вы только что выгрузили из Figma или папки загрузок).
  • Скрипт мгновенно строит таблицу. Слева отображается мини-превью картинки (50px) — вы точно видите, какой макет переименовываете.
  • Напротив каждого файла пишите его нормальное человеческое название на русском языке (например: продвижение сайтов или настройка яндекс директ цена).
  • Если вы случайно добавили лишний файл, нажмите на красный крестик «Удал.». Строка сотрется, а все остальные поля ввода сохранят ваш текст без сброса и сдвигов!
  • Нажмите зеленую кнопку «Сжать и перевести в WebP».

Готово! Страница обновится и выдаст подробный аудит-лог по каждому файлу (сколько КБ весил оригинал, сколько стал весить новый WebP и на сколько процентов он сжался).

Сами готовые изображения автоматически сохраняются в созданную папку ready_images прямо внутри директории скрипта.

Подведем итоги

Мы создали полностью контролируемый, приватный инструмент автоматизации, который экономит часы рутины. Картинки сжимаются в среднем на 60–80%, текст на латинице пишется без ошибок, а на вашем сайте нет ни одного лишнего плагина. Пользуйтесь, ускоряйте свои сайты и повышайте конверсии!

Создание сайтов
Разрабатываем современные сайты под ключ. Учитываем UX/UI, скорость загрузки и SEO-структуру.
Получите готовый инструмент для привлечения клиентов уже через 14 дней.
Оставить заявку

Читайте также