f78f35fb3f
Парсер лидов МБ РФ: Яндекс.Карты + HH.ru + обогащение DaData/ЕГРЮЛ/Rusprofile + Streamlit CRM. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
202 lines
12 KiB
Python
202 lines
12 KiB
Python
"""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}")
|