Почему парсинг etender.gov.az замедляется после 1400 записей и как это исправить
При сборе данных с портала государственных закупок Азербайджана etender.gov.az вы столкнулись с типичной проблемой: парсер работает быстро до определенного объёма (1400 записей), а затем резко замедляется. Разберём основные причины и предложим конкретные решения для ускорения загрузки данных в MySQL.
Основные причины замедления парсинга
1. Утечка памяти в Selenium WebDriver
В вашем коде используется webdriver.Chrome(options=options), который запускается один раз на весь цикл. При последовательном открытии сотен страниц без перезапуска драйвера происходит накопление кэша и данных DOM. Это приводит к увеличению потребления оперативной памяти и замедлению работы. Решение: перезапускайте драйвер через каждые 50-100 обработанных ID или используйте пул драйверов.
2. Отсутствие пагинации при получении ID
Функция get_all_events_ids(page_size) загружает все ID одним запросом с PageNumber=1. Если page_size равен 2500, то API возвращает огромный JSON, что замедляет обработку. Рекомендация: загружайте ID порциями по 100-200 штук, последовательно увеличивая номер страницы.
3. Нерациональное использование ThreadPoolExecutor
Вы создаёте пул из 16 потоков для параллельного сбора данных, но при этом вызываете time.sleep(1) после каждого завершённого фьючерса. Это искусственно замедляет выполнение. Кроме того, одновременная работа 16 экземпляров Selenium может перегрузить CPU и память. Оптимизация: уменьшите число воркеров до 4-6 и уберите лишние задержки.
4. Проблемы с сетью и таймаутами
API портала может блокировать частые запросы. Вы используете timeout=20 для GET-запросов, но при массовом парсинге сервер может начать отвечать медленнее. Совет: добавьте обработку повторных попыток с экспоненциальной задержкой (retry logic) и увеличьте таймаут до 30-60 секунд.
Как ускорить загрузку данных в MySQL
1. Используйте массовую вставку (bulk insert)
Вместо вставки по одной записи в цикле применяйте session.execute_all() или engine.execute() с одним большим INSERT. Например, для 100 записей можно сформировать единый SQL-запрос. Это снизит количество транзакций и ускорит запись в 5-10 раз.
2. Отключите autoload_with для таблиц
Вызов autoload_with=engine каждый раз загружает метаданные таблицы. Если структура базы не меняется, определите таблицы один раз в начале скрипта и переиспользуйте объекты.
3. Уменьшите количество коммитов
Вы делаете session.commit() после каждого батча из 100 записей. Для 2500 записей это 25 коммитов. Перенесите коммит на конец обработки всех данных или делайте его раз в 500-1000 записей.
Оптимизированный подход к сбору данных
Рекомендуем переработать архитектуру: отделите сбор ID от получения деталей. Сначала загрузите все ID пачками по 100 через API, сохраните их в локальный файл или список. Затем обрабатывайте ID группами по 50, перезапуская Selenium после каждой группы. Для загрузки в БД используйте pandas.to_sql() с параметром method='multi' - это даст максимальную скорость вставки.
Также рассмотрите замену Selenium на прямые HTTP-запросы к API (если данные доступны в JSON) - это снизит нагрузку в 10-20 раз. Проверьте, не дублирует ли API данные, которые вы парсите через браузер.
Заключение
Замедление после 1400 записей вызвано комплексом факторов: утечкой памяти в Selenium, неоптимальной загрузкой ID, избыточными задержками и неэффективной вставкой в БД. Применив описанные выше методы, вы сможете обрабатывать 2500+ тендеров за разумное время без потери скорости.