MBL
Go / Проекты / WebJesus / WebJesus — парсеры и обработка сбоев
Go сложный

WebJesus — парсеры и обработка сбоев

projectspythonerror-handling

Разбор на реальном коде google_ai_scrapper/parsers/google_ai_parser.py — не пересказ, а построчно проверенная логика.

Архитектурная схема (archify) →

Три разных исхода одного запроса, не два

Метод search() возвращает не бинарное "успех/провал", а три разных состояния, которые дальше обрабатываются по-разному:

  • Пустое тело ответа (raw_body пустой) → "No AI Overview" — это ВАЛИДНЫЙ ответ: у страницы честно нет блока AI Overview, ретраить бессмысленно.
  • Ключа "ai_overview" нет в распарсенном JSON → тоже "No AI Overview" — тот же валидный "пусто", просто обнаруженный на шаг позже.
  • Ошибка json.JSONDecodeError при парсинге тела → пробуется alternate-очистка строки (cleaned_body) и повторный json.loads; если и это не помогло — только тогда "ERROR".

Почему это важно: если бы парсер не различал "легитимно пусто" от "сломалось", легитимные "нет AI Overview" в отчёте выглядели бы неотличимо от реальных сбоев парсинга — пользователь не смог бы понять, стоит ли перезапускать запрос.

Retry с экспоненциальным backoff — только на ERROR

max_retries = self.settings.MAX_RETRIES
for attempt in range(max_retries):
    result = await self._perform_fetch(search_url, search_text, file_text)
    _, _, title, ai_response, full_link = result
    if title == "ERROR" or full_link == "ERROR":
        last_error = ai_response
        if attempt < max_retries - 1:
            wait_time = 2 ** attempt  # 1с, 2с, 4с, 8с...
            # sleep(wait_time), затем следующая попытка
    else:
        return result  # успех или валидный "No AI Overview" — выход сразу

Ключевая деталь: ретрай срабатывает ТОЛЬКО на "ERROR". Результат "No AI Overview" считается успешным исходом и завершает цикл немедленно — повторный запрос не тратится на то, что заведомо не изменится от перезапуска.

Формула ожидания 2 ** attempt даёт классическую экспоненциальную задержку: 1, 2, 4, 8 секунд между попытками — стандартный паттерн против перегрузки внешнего API повторными запросами сразу после сбоя.

Что стоит сказать на собесе про этот код

Разделение "детерминированно пустой результат" vs "транзиентная ошибка, имеет смысл повторить" — тот же принцип, что и в разборе Kafka DLQ в других разделах этой базы знаний (доменная ошибка не ретраится бесконечно, транзиентная — ретраится с backoff). Один и тот же паттерн мышления, только в контексте HTTP-парсера вместо очереди сообщений — хороший пример переносимости инженерного мышления между разными технологиями.

Самопроверка 0 / 2
Понимаю разницу между "No AI Overview" (валидный пустой ответ) и "ERROR" (реальный сбой) в парсере
Знаю формулу backoff парсера — 2^attempt секунд, ретраи только на ошибку
Как усвоено?