init: Parser v1 — Lead Generation Engine

Парсер лидов МБ РФ: Яндекс.Карты + HH.ru + обогащение DaData/ЕГРЮЛ/Rusprofile + Streamlit CRM.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Aks
2026-06-09 12:56:06 +03:00
commit f78f35fb3f
33 changed files with 9198 additions and 0 deletions
+201
View File
@@ -0,0 +1,201 @@
"""Blacklist крупных компаний которым outreach бесполезен.
Эти компании НЕ наши клиенты для предложения сайта/автоматизации:
• У них собственные IT-команды и маркетинг
• Они закрытые холдинги / госструктуры
• Сетки которые мы не сможем "пробить" холодным письмом
При парсинге HH такие компании отсекаем сразу (не вставляем в БД).
Существующих в БД помечаем outreach_status='excluded'.
Стратегия проверки:
1. Точное совпадение очищенного имени со списком EXACT_NAMES
2. Подстрока — содержит ли имя одно из KEYWORD_FRAGMENTS
3. Признак крупного юр.лица (ПАО / Госкорпорация / ФГУП / ФГАОУ)
"""
import re
# Точные имена (lowercase, без юр.формы) — известные крупные сети
EXACT_NAMES = {
# Банки и финансы
"газпромбанк", "альфа-банк", "альфа банк", "втб", "сбер", "сбербанк",
"совкомбанк", "мкб", "райффайзен", "тинькофф", "т-банк", "tinkoff",
"россельхозбанк", "дом.рф", "дом рф", "точка", "тинькофф банк",
"отп банк", "псб", "промсвязьбанк", "банк псб", "норвик банк", "морской банк",
"энергогарант", "согаз-мед", "согаз", "ренессанс страхование",
"финансовый дом солид",
# Телеком
"мтс", "билайн", "мегафон", "tele2", "теле2", "ростелеком",
"ростелеком контакт-центр",
# Ритейл / сети
"вкусвилл", "x5", "икс 5", "перекрёсток", "перекресток", "пятерочка",
"пятёрочка", "магнит", "лента", "ашан", "ашан ритейл россия",
"азбука вкуса", "metro", "лемана про", "lamoda", "ozon", "озон",
"дикси", "красное белое", "красное & белое", "красное и белое",
"бристоль", "fix price", "фикс прайс", "светофор", "верный",
"мираторг", "магнолия", "вкусвилл",
"wildberries", "вайлдберриз", "rwb", "сбермаркет", "яндекс еда",
"яндекс.еда", "яндекс крауд", "яндекс крауд: поддержка",
"яндекс крауд: ai-тренеры", "яндекс команда для бизнеса",
"сбер для экспертов", "сбер. it", "сбер тех", "сберпр", "сберправо",
"ситилинк", "ситилинк: магазины", "merlion", "mts", "т-банк", "тинькофф",
"теремок", "кари", "карі",
# IT-холдинги
"yandex", "mail", "mail.ru", "vk", "rambler", "rambler&co", "kaspersky",
# Маркетплейсы и крупные шопы
"wildberries", "bork", "dns", "dns shop", "сеть магазинов цифровой и бытовой техники dns",
"м.видео", "эльдорадо", "rendez-vous",
# Госструктуры
"правительство москвы", "минстрой", "минздрав", "мфц",
"гбу мфц города москвы мои документы", "грчц",
"росатом", "ростех", "газпром", "роснефть", "лукойл", "транснефть",
"роскосмос", "ржд", "ао росгеология",
# Кадровые / HR крупные (не наши клиенты)
"world class", "encore fitness", "xfit", "ddx fitness", "сити фитнес",
"fitness one",
# Известные сети ресторанов / общепита
"шоколадница", "коффемания", "il патио", "иль патио", "ginza project",
"white rabbit family", "kuxnja", "тануки",
# Сети общепита / фастфуд (расширено 2026-06-05)
"му-му", "му му",
"вкусно и точка", "вкусно — и точка", "вкусно -и точка",
"додо пицца", "додо pizza", "dodo pizza",
"крошка картошка", "крошка-картошка",
"якитория", "стардогс", "стардог", "stardogs",
"грабли", "кофе хауз", "coffee house", "правда кофе",
"даблби", "double b", "хлеб насущный", "буханка",
"чайхона №1", "чайхона номер 1", "две палочки",
"планета суши", "росинтер", "ростикс", "rostic's", "rostics",
"бургер кинг", "сабвей", "subway", "крошка-картошка",
"братья караваевы", "кулинарная лавка братьев караваевых",
"прайм стар", "prime star", "кофемания",
# Девелоперы / стройка
"пик", "лср", "ск самолёт", "эталон", "гк эталон", "гк эталон москва",
"стройтрансгаз", "группа самолёт", "самолёт",
# IT-аутсорс / гиганты услуг
"merlion", "softline", "ланит", "крок", "ит-такт", "консист бизнес групп",
"datapro", "datasoft",
# Strategy / consulting big4
"б1", "b1", "kpmg", "deloitte", "ey", "pwc", "ernst & young",
# Кофейни / международные бренды
"starbucks", "burger king", "kfc", "mcdonalds", "макдоналдс",
# Прочие массовые
"лента", "о'кей", "ашан", "global village", "x5 retail group",
"x5 управляющая компания", "оборонстрой", "стройэлектромонтаж",
}
# Подстроки — если в имени встречаются, скорее всего это крупная компания
# или госструктура (не наша целевая аудитория)
KEYWORD_FRAGMENTS = (
# Юр.формы крупных компаний
" пао ", "пао ", " ао ", # ПАО почти всегда крупные публичные компании
"акционерное общество",
"публичное акционерное",
# Госструктуры
"фгуп", "фгаоу", "фгбу", "фгкоу", "гбуз", "гбу ", "гбоу",
"минздрав", "минобр", "минстрой", "мфц", "правительств",
"госкорпорация", "гос. корп", "ао росгеология", "ао росат",
# Сетевые маркеры
"ритейл россия", "торговая сеть", "розничная сеть",
"холдинг", "корпорация", "концерн", "группа компаний",
# Сети ресторанов / общепита (общие маркеры)
" ресторанная группа ", " ресторанная группа", "ресторанная группа ",
"группа ресторанов", "ресторанный холдинг",
# Крупные банки (универсальные)
" банк ", "банк ", "банка ", "банком ",
# Крупные международные
" moscow ", " corp.", " ltd.", " llc",
# Государственные / некоммерческие
"ао аккую нуклеар", " росатом", " ростех",
# Маркеры HR-аутсорса крупных компаний — это сами кадровые агентства, не клиенты
" кадровое агентство", " кадровый центр", "кадровый центр ",
"рекрутмент",
# Билеты, бронирования, логисты-гиганты
"почта россии", "сдек", "boxberry", "dpd", "деловые линии", "транспортная компания",
)
# Префиксы которые мы убираем для нормализации перед проверкой
HR_HEADERS = (
"пао ", "оао ", "ао ", "ооо ", "ип ", "зао ", "нко ", "гк ",
"сеть ", "сети ", "группа ", "группа компаний ",
"ресторан ", "кафе ", "бар ", "магазин ", "клиника ",
"салон красоты ", "салон ", "студия красоты ", "студия ",
"имидж-лаборатория ", "барбершоп ", "пиццерия ", "столовая ",
"автосервис ", "автосалон ",
)
def _normalize(name: str) -> str:
"""Нормализовать имя для сравнения: lowercase, без юр.формы, без скобочного суффикса."""
if not name:
return ""
s = name.lower().strip()
# Убираем скобочный суффикс «(ИП ФИО)» / «(ООО ХХХ)»
s = re.sub(r"\s*\([^)]*\)\s*", " ", s)
# Убираем кавычки
s = re.sub(r"[«»\"'`'.,]", " ", s)
# Убираем юр.формы и HR-префиксы
for pref in sorted(HR_HEADERS, key=len, reverse=True):
if s.startswith(pref):
s = s[len(pref):].strip()
break
return re.sub(r"\s+", " ", s).strip()
def is_blacklisted(name: str) -> tuple[bool, str | None]:
"""Проверить попадает ли компания в blacklist.
Возвращает (True, reason) если в blacklist, или (False, None).
reason — что именно сматчилось (для лога).
"""
if not name:
return False, None
name_lower = name.lower()
normalized = _normalize(name)
# 1. Точное совпадение нормализованного имени
if normalized in EXACT_NAMES:
return True, f"exact: {normalized!r}"
# 1b. Префикс — «вкусвилл даркстор» начинается с «вкусвилл», «альфа-банк
# центральный офис» — с «альфа-банк». Берём самое длинное совпадение.
for known in sorted(EXACT_NAMES, key=len, reverse=True):
if len(known) >= 5 and normalized.startswith(known + " "):
return True, f"prefix: {known!r}"
# 2. Подстрока (KEYWORD_FRAGMENTS) — должна быть в исходном имени lowercase
for frag in KEYWORD_FRAGMENTS:
if frag in name_lower:
return True, f"keyword: {frag!r}"
# 3. Имя начинается с ПАО / Публичное акционерное — точно крупное публичное АО
if (name_lower.startswith("пао ") or name_lower.startswith("публичное акционерное")
or " пао " in name_lower):
return True, "ПАО"
return False, None
if __name__ == "__main__":
tests = [
("Газпромбанк", True),
("ПАО Совкомбанк. Центральный офис.", True),
("Альфа-Банк. Центральный офис", True),
("АШАН Ритейл Россия, Работа в магазине", True),
("Правительство Москвы", True),
("ВкусВилл. Даркстор", True),
("Сбер для экспертов", True),
("ООО Гранд Пирог", False),
("Кафе Пушкинъ", False),
("ИП Иванов Иван", False),
("Студия маникюра BLOOM", False),
("ФГАОУ ВО РНИМУ", True), # фгаоу
("Гос. корп. ГАУЗ ...", True),
]
print("=== is_blacklisted smoke-test ===")
for name, expected in tests:
got, reason = is_blacklisted(name)
mark = "" if got == expected else ""
print(f" {mark} {name[:50]:50}{got} ({reason or ''}) expected={expected}")