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:
@@ -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}")
|
||||
Reference in New Issue
Block a user