Измени кнопку старта сделай красивой
```python
import requests
import json
import pandas as pd
import tkinter as tk
from tkinter import filedialog, messagebox, DoubleVar, IntVar, ttk
import random
import aiohttp
import asyncio
import threading
import time

# Список User-Agent для имитации разных браузеров
user_agents = [
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.2 Safari/605.1.15",
    "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36",
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Firefox/68.0",
    "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36"
]


# Асинхронная функция для получения деталей с увеличенным тайм-аутом и рандомизацией задержки между запросами
async def fetch_part_detail(session, detail_num, location_id, max_delivery_days, make=None, retries=5):
    url = f"https://emex.ru/api/search/search?detailNum={detail_num}&locationId={location_id}&showAll=true"
    if make:
        url += f"&make={make}"
    headers = {"User-Agent": random.choice(user_agents)}

    for attempt in range(retries):
        try:
            # Задержка перед каждым запросом для снижения вероятности блокировки
            await asyncio.sleep(random.uniform(3, 5))  # Задержка 1-3 секунды
            async with session.get(url, headers=headers, timeout=30) as response:
                if response.status == 200:
                    return await response.json()
                else:
                    print(f"Ошибка при запросе данных: {response.status}")
        except asyncio.TimeoutError:
            print(f"Тайм-аут на попытке {attempt + 1}. Повторная попытка...")
        except aiohttp.ClientError as e:
            print(f"Ошибка запроса: {e}. Попытка {attempt + 1}.")
    return None  # Возвращаем None, если все попытки не удались


# Парсинг данных и фильтрация по наличию, рейтингу и сроку доставки
def parse_response(json_data, exclude_preorder, min_rating, max_delivery_days):
    parts_info = []
    search_result = json_data.get('searchResult', {})

    originals = search_result.get('originals', [])
    if originals:
        for original in originals:
            offers = original.get('offers', [])

            # Фильтруем по рейтингу и сроку доставки
            offers_filtered = [offer for offer in offers if
                               offer.get('rating', 0) >= min_rating and offer.get('delivery', {}).get('value', float(
                                   'inf')) <= max_delivery_days]

            # Сортируем по цене
            offers_sorted = sorted(offers_filtered, key=lambda x: x['displayPrice']['value'])

            for offer in offers_sorted[:3]:  # Ограничиваем 3 результатами
                quantity = offer.get('quantity', 'Не указано')

                # Проверяем количество: если 1000, то считаем как "Под заказ"
                if quantity == 1000 or offer.get('notAvailable', False):
                    quantity = "Под заказ"

                # Фильтр по наличию (exclude_preorder исключает под заказ)
                if exclude_preorder and quantity == "Под заказ":
                    continue

                part_info = {
                    "Номер детали": original.get('detailNum', 'Не указано'),
                    "Название": original.get('name', 'Не указано'),
                    "Бренд": original.get('make', 'Не указано'),
                    "Цена": offer.get('displayPrice', {}).get('value', 'Не указано'),
                    "Рейтинг": offer.get('rating', 'Нет данных'),
                    "Количество": quantity,
                    "Срок доставки": f"{offer.get('delivery', {}).get('value', 'Не указано')} {offer.get('delivery', {}).get('units', '')}"
                }
                parts_info.append(part_info)
    return parts_info


# Асинхронная функция для получения и сохранения данных
async def search_and_save_to_excel(location_id, detail_numbers, search_type, make, exclude_preorder, min_rating,
                                   max_delivery_days, output_file, progress_bar, root):
    all_parts_info = []
    async with aiohttp.ClientSession() as session:
        total_tasks = len(detail_numbers)  # Общее количество задач для прогресса
        progress_step = 100 / total_tasks  # Рассчитываем шаг прогресса

        previous_detail_num = None  # Переменная для хранения предыдущего артикула

        for i, detail_num in enumerate(detail_numbers):
            result = await fetch_part_detail(session, detail_num, location_id, max_delivery_days,
                                             make if search_type == 'oem' else None)
            if result:
                parts_info = parse_response(result, exclude_preorder, min_rating, max_delivery_days)

                # Добавляем пустую строку, если артикул поменялся
                for part in parts_info:
                    current_detail_num = part['Номер детали']
                    if previous_detail_num and current_detail_num != previous_detail_num:
                        # Добавляем пустую строку для нового артикула
                        all_parts_info.append(
                            {"Номер детали": "", "Название": "", "Бренд": "", "Цена": "", "Рейтинг": "",
                             "Количество": "", "Срок доставки": ""})

                    all_parts_info.append(part)
                    previous_detail_num = current_detail_num

            else:
                messagebox.showerror("Ошибка", f"Не удалось получить данные для детали {detail_num}.")

            # Обновляем прогресс-бар после обработки каждой детали
            progress_bar['value'] += progress_step
            root.update_idletasks()

        # Сохранение данных в Excel
        df = pd.DataFrame(all_parts_info)
        df.to_excel(output_file, index=False)
        print(f"Данные сохранены в файл {output_file}")


# Функция для запуска асинхронного кода в отдельном потоке
def run_async_thread(coro):
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    loop.run_until_complete(coro)


# Интерфейс
class Application(tk.Tk):
    def __init__(self):
        super().__init__()
        self.title("Emex Парсер")
        self.configure(bg='#ffffff')
        self.geometry("550x730")  # Немного увеличим размер окна

        # Ввод данных
        self.create_widgets()

    def create_widgets(self):
        # Стили
        style = ttk.Style()
        style.configure('TLabel', font=('Helvetica', 12), background='#ffffff')
        style.configure('TButton', font=('Helvetica', 12), background='#ff6600', foreground='#FF6600')
        style.map('TButton', background=[('active', '#e65c00')])

        # Заголовок приложения
        tk.Label(self, text="Парсер Emex", font=("Helvetica", 16, "bold"), bg='#ffffff', fg='#FF6600').pack(pady=10)

        # Ввод ID магазина
        ttk.Label(self, text="ID магазина:").pack(pady=5)
        self.entry_location_id = ttk.Entry(self, font=("Helvetica", 12), width=30)
        self.entry_location_id.pack(pady=5)

        # Ввод деталей
        ttk.Label(self, text="Детали (по одной на строку):").pack(pady=5)
        self.entry_details = tk.Text(self, height=8, width=40, font=("Helvetica", 12), relief="solid")
        self.entry_details.pack(pady=5)

        # Ввод бренда
        ttk.Label(self, text="Бренд (для поиска по OEM):").pack(pady=5)
        self.entry_make = ttk.Entry(self, font=("Helvetica", 12), width=30)
        self.entry_make.pack(pady=5)

        # Радиокнопки для типа поиска и чекбокс "Только в наличии"
        frame_radio = tk.Frame(self, bg='#ffffff')  # Фрейм для радиокнопок и чекбокса
        frame_radio.pack(pady=5)
        self.search_option = tk.StringVar(value='article')
        tk.Radiobutton(frame_radio, text="По артикулу", variable=self.search_option, value='article', bg='#ffffff',
                       font=("Helvetica", 12)).grid(row=0, column=0, padx=10)
        tk.Radiobutton(frame_radio, text="По OEM", variable=self.search_option, value='oem', bg='#ffffff',
                       font=("Helvetica", 12)).grid(row=0, column=1, padx=10)
        self.exclude_preorder = tk.BooleanVar(value=True)
        ttk.Checkbutton(frame_radio, text="Только в наличии", variable=self.exclude_preorder).grid(row=0, column=2,
                                                                                                   padx=10)

        # Ползунок для выбора минимального рейтинга
        ttk.Label(self, text="Минимальный рейтинг:").pack(pady=5)
        self.min_rating = DoubleVar()
        self.min_rating.set(1.0)
        self.rating_scale = tk.Scale(self, from_=1, to=5, resolution=0.1, orient=tk.HORIZONTAL,
                                     variable=self.min_rating, length=400)
        self.rating_scale.pack(pady=5)

        # Ползунок для выбора максимального срока доставки
        ttk.Label(self, text="Максимальный срок доставки (дни):").pack(pady=5)
        self.max_delivery_days = IntVar()
        self.max_delivery_days.set(30)  # По умолчанию 30 дней
        self.delivery_scale = tk.Scale(self, from_=1, to=60, orient=tk.HORIZONTAL, variable=self.max_delivery_days,
                                       length=400)
        self.delivery_scale.pack(pady=5)

        # Прогресс-бар
        self.progress_bar = ttk.Progressbar(self, orient='horizontal', length=400, mode='determinate')
        self.progress_bar.pack(pady=10)

        # Кнопка для запуска парсинга
        self.btn_parse = ttk.Button(self, text="Начать парсинг", command=self.start_parsing)
        self.btn_parse.pack(pady=20)

    def start_parsing(self):
        location_id = self.entry_location_id.get()
        detail_numbers = self.entry_details.get("1.0", tk.END).strip().split("\n")
        search_type = self.search_option.get()
        make = self.entry_make.get().strip()
        exclude_preorder = self.exclude_preorder.get()
        min_rating = self.min_rating.get()
        max_delivery_days = self.max_delivery_days.get()

        if not location_id or not detail_numbers:
            messagebox.showerror("Ошибка", "Заполните все поля!")
            return

        output_file = filedialog.asksaveasfilename(defaultextension=".xlsx", filetypes=[("Excel files", "*.xlsx")])
        if output_file:
            self.progress_bar['value'] = 0  # Сбрасываем прогресс-бар

            # Запуск асинхронного процесса в отдельном потоке
            coro = search_and_save_to_excel(location_id, detail_numbers, search_type, make, exclude_preorder,
                                            min_rating, max_delivery_days, output_file, self.progress_bar, self)
            threading.Thread(target=run_async_thread, args=(coro,)).start()


if __name__ == "__main__":
    app = Application()
    app.mainloop()

```