"""Конфигурация парсера лидов. Все настройки в одном месте. Меняем здесь — отражается во всех парсерах. """ # ─────────────────────────────────────────────────────────────────────── # Города и их Yandex.Maps ID # ─────────────────────────────────────────────────────────────────────── # Каждая точка парсинга (город / городской округ / регион): # yandex_id — geo ID в Яндекс.Картах (https://yandex.ru/maps/{yandex_id}/{slug}/...) # yandex_slug — slug в URL (moscow, mytishchi, ...). Декоративный — главное yandex_id # vk_id — city ID в VK API (1=Москва, 2=СПб, ...) # region — агрегатор для группировки в БД (Москва / Московская область / ...) # # Активный регион выбирается через CLI: python main.py --city "Мытищи" # По умолчанию используется ACTIVE_CITY. # # Geo ID можно проверить, открыв https://yandex.ru/maps/{geo_id}/ в браузере. CITIES = { # ── Только проверенные значения (geo_id подтверждены) ─────────── "Москва": {"yandex_id": 213, "yandex_slug": "moscow", "vk_id": 1, "region": "Москва"}, "Москва и МО": {"yandex_id": 1, "yandex_slug": "moscow-and-moscow-oblast", "vk_id": 1, "region": "Москва и МО"}, "Санкт-Петербург": {"yandex_id": 2, "yandex_slug": "saint-petersburg", "vk_id": 2, "region": "Санкт-Петербург"}, # ── Любой другой город / район — через --district ─────────────── # Парсер автоматически использует "Москва и МО" + название как район. # Пример: # python main.py --full --city "Москва и МО" --district "Химки" --category "стоматология" # python main.py --full --city "Москва и МО" --district "Мытищи" --category "автосервис" # python main.py --full --city "Москва" --district "Митино" --category "салон красоты" # # Чтобы добавить точный город — найди его geo_id через браузер: # 1. Открой https://yandex.ru/maps/ # 2. Найди город в поиске → кликни на первый результат # 3. URL станет вида: https://yandex.ru/maps/{geo_id}/{slug}/?... # 4. Скопируй сюда: # # "Мытищи": {"yandex_id": 10743, "yandex_slug": "mytishchi", "vk_id": 1, "region": "Московская область"}, } # Активный регион (можно переопределить через CLI --city) ACTIVE_CITY = "Москва" # Категории — фокус-ЦА 44AS: локальный сервисный бизнес с онлайн-записью и # отзывами. Бьёт прямо в наши продукты: P3 AI Reputation (отзывы Я.Карт/2ГИС), # P4 AI Consultant (входящие + онлайн-запись), P12 AI SMM (Instagram/контент). CATEGORIES = [ # 🎯 Бьюти — главная цель (онлайн-запись = боль, Instagram = канал продаж, отзывы решают) "салон красоты", "барбершоп", "ногтевой сервис", "студия массажа", "косметология", "спа-салон", # 🍽 HoReCa — отзывы = выручка, бронь столиков, визуал блюд "кафе", "ресторан", # 🏥 Клиники / запись — доверие через отзывы + онлайн-запись "стоматология", "фитнес-клуб", ] # ─────────────────────────────────────────────────────────────────────── # HH.ru — signal-запросы (компании ищут "руки" = нет автоматизации) # ─────────────────────────────────────────────────────────────────────── # Принцип: если компания ищет ЭТИ должности — у неё нет CRM / нет # онлайн-записи / ручная коммуникация. Это +3 к hh_signal в скоринге. HH_SIGNAL_QUERIES = [ # Ручная коммуникация → нет CRM / нет автоответов "оператор ПК", "оператор колл-центра", "оператор технической поддержки", "менеджер чата", "менеджер по продажам без CRM", # Личный помощник / административка → "помоги мне разобраться" "помощник руководителя", "ассистент руководителя", "офис-менеджер", # Запись клиентов руками → нет онлайн-booking "администратор записи", "администратор салона красоты", "администратор клиники", "ресепшн", # Бухгалтерия "руками" → не автоматизирован документооборот "бухгалтер 1С", "помощник бухгалтера", ] # Период поиска (дней) — свежие вакансии HH_PERIOD_DAYS = 30 HH_MAX_PAGES_PER_QUERY = 5 # 5 страниц × 100 = до 500 вакансий на запрос # ─────────────────────────────────────────────────────────────────────── # Anti-bot задержки (секунды) # 2026-05-18: снижены ~30% после успешного прогона 2634 лидов без блокировок. # Если домашний IP начнёт ловить captcha — поднять обратно к 2.0/7.0 + 30/60. # ─────────────────────────────────────────────────────────────────────── MIN_DELAY = 1.5 # минимум между запросами MAX_DELAY = 4.0 # максимум между запросами CATEGORY_PAUSE_MIN = 15 # пауза между категориями CATEGORY_PAUSE_MAX = 30 # ─────────────────────────────────────────────────────────────────────── # Лимиты безопасности # ─────────────────────────────────────────────────────────────────────── MAX_BLOCKED_TRIES = 3 # сколько раз получить captcha → останавливаемся MAX_SCROLLS = 15 # максимум скроллов списка Я.Карт MAX_CARDS_PER_CATEGORY = 100 # сколько карточек открывать на 1 категорию # ─────────────────────────────────────────────────────────────────────── # Пути # ─────────────────────────────────────────────────────────────────────── DB_PATH = "leads.db" EXPORT_DIR = "exports" # ─────────────────────────────────────────────────────────────────────── # Скоринг лидов v5 — «решаемая нами боль» (шкала 0-10) # ─────────────────────────────────────────────────────────────────────── # Семантика (решение 2026-06-01): score = есть ли у компании проблемы, # которые закрывают НАШИ продукты, и насколько остро. # score = pain(решаемая боль) × icp_fit(наш ли это размер) # Не дозвонибельность, не «качество лида» — только боль под продукты 44AS. # # Логика в scoring.py: # • каждый детектор боли привязан к продукту (P-код) и теме; # • внутри темы сигналы агрегируются с насыщением (max + k·остальные), # чтобы коррелированные признаки не складывались линейно; # • сумма тем → raw_pain, нормируется к 0-10 через PAIN_NORM; # • ICP-гейт множителем топит крупняк/премиум (см. ICP_* ниже); # • «уже автоматизирован» отдельно НЕ гейтим — у такого лида просто нет # болевых дыр, severity→0 естественно. # # Веса = бизнес-приоритет (severity в «сырых» баллах). Крутим здесь. SCORE_WEIGHTS = { # ── Запись / входящие → P4 AI-Consultant, P1 Text Agent ────────── "no_online_booking": 2.0, # нет онлайн-записи (для услуг — критично) "no_live_chat": 1.0, # нет онлайн-чата → входящие теряются # ── Репутация → P3 AI-Reputation (континуум по рейтингу) ───────── "rating_very_low": 2.0, # avg < 3.5 "rating_low": 1.5, # 3.5 ≤ avg < 4.0 "rating_mid": 1.0, # 4.0 ≤ avg < 4.5 (4.5+ = здоровая репутация, не боль) "few_reviews": 1.0, # < 10 отзывов — репутацией не занимаются "some_reviews": 0.5, # 10..30 отзывов # ── Веб → P10 Smart Web ────────────────────────────────────────── "no_website": 1.5, # нет сайта вовсе "site_dead": 2.5, # сайт не отвечает / 404 (платят, не работает) "site_constructor": 0.5, # tilda/wix И только для малого бизнеса (см. scoring) # ── Маркетинг → P12 AI SMM ─────────────────────────────────────── "no_social": 1.0, # ни vk, ни telegram, ни instagram "no_analytics": 0.5, # не меряют трафик # ── Инфраструктура → P2 AI-Office ──────────────────────────────── "free_email": 0.5, # корп.почта на mail.ru/gmail — нет своей } # Каждый детектор → тема (для тематической агрегации с насыщением). PAIN_THEME = { "no_online_booking": "booking", "no_live_chat": "booking", "rating_very_low": "reputation", "rating_low": "reputation", "rating_mid": "reputation", "few_reviews": "reputation", "some_reviews": "reputation", "no_website": "web", "site_dead": "web", "site_constructor": "web", "no_social": "marketing", "no_analytics": "marketing", "free_email": "infra", } # Детектор → наш продукт (для подсказки CRM «с чем заходить»). PAIN_PRODUCT = { "no_online_booking": "P4", "no_live_chat": "P4", "rating_very_low": "P3", "rating_low": "P3", "rating_mid": "P3", "few_reviews": "P3", "some_reviews": "P3", "no_website": "P10", "site_dead": "P10", "site_constructor": "P10", "no_social": "P12", "no_analytics": "P12", "free_email": "P2", } # Человекочитаемые причины (для reasons в breakdown и CRM). PAIN_REASON = { "no_online_booking": "нет онлайн-записи", "no_live_chat": "нет онлайн-чата", "rating_very_low": "низкий рейтинг (<3.5)", "rating_low": "слабый рейтинг (<4.0)", "rating_mid": "средний рейтинг (<4.5)", "few_reviews": "мало отзывов (<10)", "some_reviews": "немного отзывов (<30)", "no_website": "нет сайта", "site_dead": "сайт не отвечает", "site_constructor": "сайт на конструкторе", "no_social": "нет соцсетей (VK/Telegram)", "no_analytics": "нет веб-аналитики", "free_email": "почта на бесплатном домене", } # ════════════════════════════════════════════════════════════════════ # 📌 КАТЕГОРИЙНАЯ РЕЛЕВАНТНОСТЬ ДЕТЕКТОРОВ — ЧИТАЙ ПРИ ДОБАВЛЕНИИ КАТЕГОРИИ # ════════════════════════════════════════════════════════════════════ # Часть болей применима НЕ ко всем типам бизнеса: # • «нет онлайн-записи» (P4) — только услуги с записью (салон, клиника, # кафе, автосервис). Магазину / опту / бухгалтерии запись не нужна. # • «нет соцсетей» (P12) — только B2C, где соцсети = канал продаж # (бьюти, HoReCa, розница, фитнес). У B2B (бухгалтерия, стройка) — не боль. # Универсальные боли (сайт, репутация, чат, аналитика, почта) применяются ВСЕГДА. # # Модель — БЕЛЫЕ СПИСКИ по ключевым словам (матч по подстроке в lead.category): # детектор срабатывает ТОЛЬКО если категория попала в его множество. # Неизвестная / новая категория по умолчанию НЕ получает booking/social — # консервативно: лучше не начислить, чем ложно завысить (как было с кейсом # «магазин одежды → нет онлайн-записи»). # # 👉 ДОБАВЛЯЕШЬ НОВУЮ КАТЕГОРИЮ (в CATEGORIES выше или новым --category)? # Впиши её ключевое слово сюда: # — есть запись клиентов? → в APPT_CATEGORIES # — продаётся через соцсети? → в SOCIAL_SALES_CATEGORIES # Чистый B2B без записи и соцпродаж — НЕ добавляй никуда (получит только # универсальные боли — это правильно). # ════════════════════════════════════════════════════════════════════ # Услуги с записью клиентов → применяется детектор «нет онлайн-записи» (P4). APPT_CATEGORIES = { "салон", "барбершоп", "ногт", "маникюр", "педикюр", "массаж", "косметолог", "спа", "парикмахер", "эпиляц", "депиляц", "тату", "броу", "ресниц", "стоматолог", "клиник", "медицин", "врач", "ветеринар", "груминг", "фитнес", "йога", "пилатес", "танц", "бассейн", "студи", "кафе", "ресторан", "бар", "кофейн", "пиццери", "суши", "кальян", "банкет", "автосервис", "автомойка", "шиномонтаж", "детейлинг", "сервис", "ремонт", "юридическ", "нотариус", "адвокат", "консультац", } # B2C, где соцсети = канал продаж → применяется детектор «нет соцсетей» (P12). SOCIAL_SALES_CATEGORIES = { "салон", "барбершоп", "ногт", "маникюр", "массаж", "косметолог", "спа", "парикмахер", "тату", "броу", "ресниц", "студи", "кафе", "ресторан", "бар", "кофейн", "пиццери", "суши", "кальян", "магазин", "розниц", "бутик", "шоурум", "одежд", "обув", "цвет", "украшен", "подарк", "парфюм", "фитнес", "йога", "танц", "бассейн", "стоматолог", "клиник", "космет", "фото", "видео", "свадьб", "ивент", "праздник", "декор", "автосервис", "детейлинг", } # Насыщение внутри темы: theme_value = max(severities) + k·sum(остальные). # k < 1 → коррелированные сигналы одной темы не складываются линейно. THEME_SATURATION = 0.4 # Нормировка raw_pain → шкала 0-10. PAIN_NORM = «практический максимум боли» # у сильного малого лида (нет записи+чата + слабый сайт + нет соцсетей ≈ 4-5). # Делим на него, чтобы такой лид попадал в hot, а шкала растягивалась. PAIN_NORM = 5.0 # Hard cap для финального score SCORE_MAX = 10 # ── ICP-гейт: ПРОГРЕССИВНЫЙ штраф за «зрелость» (отзывы × рейтинг) ──── # ЦА = малый/средний бизнес, которому нужна автоматизация. Чем больше отзывов # И выше рейтинг — тем сильнее снижаем балл (процветающим/раскрученным мы менее # нужны и труднее продать). Формула в scoring.icp_fit: # icp = 1 − ICP_PMAX · rf · (ICP_BASE + (1−ICP_BASE)·gf) # rf = min(1, отзывы / ICP_REVIEWS_FULL) — линейно по объёму # gf = clamp((avg − ICP_RATING_MIN)/(5 − ICP_RATING_MIN)) — по рейтингу # Отзывы штрафуют ВСЕГДА (доля ICP_BASE), высокий рейтинг усиливает до полного. # Мало отзывов → множитель ≈1 (новый/борющийся бизнес сохраняет балл). # На Я.Картах рейтинги зажаты 4.5-5.0, поэтому главный рычаг — объём отзывов, # рейтинг лишь усиливает. Заменил прежний ступенчатый гейт (D18). ICP_PMAX = 0.85 # макс. доля снижения (при отзывы≥FULL и avg=5.0) ICP_REVIEWS_FULL = 6000 # отзывов для максимального review-фактора (усилен 2026-06-05: 10000→6000) ICP_BASE = 0.65 # доля штрафа от объёма, не зависящая от рейтинга (усилен: 0.5→0.65) ICP_RATING_MIN = 4.0 # ниже этого рейтинг не усиливает штраф # ── Бэнды (для CRM-сортировки и outreach-очереди) ─────────────────── BAND_HOT = 6 # score >= 6 → 🔥 hot BAND_WARM = 4 # 4..5 → 🟡 warm, ниже → ⚪ cold HOT_LEAD_THRESHOLD = BAND_HOT # совместимость: get_stats / csv_export / CRM # Полнота диагностики ниже порога → лид помечается «нужно обогащение». MIN_COVERAGE = 0.5