WebJesus — парсеры и обработка сбоев
Разбор на реальном коде 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-парсера вместо очереди сообщений — хороший пример переносимости инженерного мышления между разными технологиями.