diff --git a/encar_scraper/encar.py b/encar_scraper/encar.py index 60a15f2..f58daf2 100644 --- a/encar_scraper/encar.py +++ b/encar_scraper/encar.py @@ -15,6 +15,19 @@ import urllib3 from .core.config import Settings from .storage.db import PersistenceService from .storage.schemas import CarRecord, ImageRecord +from .translations import ( + BODY_TYPE_MAP, + BRAND_ENGLISH_ALIASES, + BRAND_TRANSLATIONS, + CITY_TO_COUNTRY, + COLOR_TRANSLATIONS, + FUEL_TYPE_MAP, + GEARBOX_MAP_KO, + MODEL_BODY_TYPE_MAP, + MODEL_TRANSLATIONS, + SELL_TYPE_MAP, + expand_allowed_brands, +) logger = logging.getLogger("encar_scraper") @@ -38,387 +51,6 @@ CAR_TYPE_DOMESTIC = "Y" # только отечественные CAR_TYPE_IMPORT = "N" # только импорт CAR_TYPE_ELECTRIC = "G" # электро/гибрид -# Корейские → латинские для fuel -FUEL_TYPE_MAP = { - "가솔린": "petrol", - "gasoline": "petrol", - "디젤": "diesel", - "diesel": "diesel", - "전기": "electric", - "electric": "electric", - "하이브리드": "hybrid", - "hybrid": "hybrid", - "lpg": "lpg", - "LPG": "lpg", - "LPG(일반인 구입)": "lpg", - "LPG(장애인 구입)": "lpg", - "가솔린+전기": "hybrid", - "디젤+전기": "hybrid", - "CNG": "cng", -} - -GEARBOX_MAP_KO = { - "오토": "AT", - "자동": "AT", - "수동": "MT", - "cvt": "CVT", - "CVT": "CVT", -} - -SELL_TYPE_MAP = { - "일반": "STOCK", - "경매": "AUCTION", - "입찰": "TENDER", -} - -CITY_TO_COUNTRY = { - "서울": "KR", "경기": "KR", "인천": "KR", "부산": "KR", "대구": "KR", - "광주": "KR", "대전": "KR", "울산": "KR", "세종": "KR", "강원": "KR", - "충북": "KR", "충남": "KR", "전북": "KR", "전남": "KR", "경북": "KR", - "경남": "KR", "제주": "KR", -} - -# Переводы брендов с корейского/английского на русский -BRAND_TRANSLATIONS = { - "현대": "Хёндэ", - "기아": "Киа", - "쉐보레(GM대우)": "Шевроле", - "벤츠": "Мерседес-Бенц", - "BMW": "BMW", - "아우디": "Ауди", - "폭스바겐": "Фольксваген", - "토요타": "Тойота", - "도요타": "Тойота", - "닛산": "Ниссан", - "혼다": "Хонда", - "포르쉐": "Порше", - "랜드로버": "Ленд Ровер", - "재규어": "Ягуар", - "볼보": "Вольво", - "푸조": "Пежо", - "시트로엥": "Ситроен", - "르노": "Рено", - "미니": "Мини", - "링컨": "Линкольн", - "캐딜락": "Кадиллак", - "크라이슬러": "Крайслер", - "지프": "Джип", - "포드": "Форд", - "테슬라": "Тесла", - "폴크스바겐": "Фольксваген", - "스코다": "Шкода", - "세아트": "SEAT", - "오펠": "Опель", - "피아트": "Фиат", - "알파로메오": "Альфа Ромео", - "마세라티": "Мазерати", - "람보르기니": "Ламборгини", - "페라리": "Феррари", - "벤틀리": "Бентли", - "롤스로이스": "Роллс-Ройс", - "애스턴마틴": "Астон Мартин", - "맥라렌": "Макларен", - "부가티": "Бугатти", - "코닉세그": "Кёнигсегг", - "파가니": "Пагани", - "헤네시": "Hennessey", - "쉘비": "Shelby", - "다임러": "Daimler", - "마이바흐": "Майбах", - "스마트": "Смарт", - "DS": "DS", - "렉서스": "Лексус", - "인피니티": "Инфинити", - "아큐라": "Акура", - "스바루": "Субару", - "미쓰비시": "Мицубиси", - "마쓰다": "Мазда", - "이스즈": "Исузу", - "다이하츠": "Daihatsu", - "스즈키": "Сузуки", - "KG모빌리티(쌍용)": "KG Mobility", - "르노코리아(삼성)": "Рено Корея", - "한국GM": "GM Korea", - "쌍용": "СсангЙонг", - "대우": "Дэу", - "제네시스": "Генезис", - "르노삼성": "Рено Самсунг", - "닷지": "Dodge", - "험머": "Hummer", - "폴스타": "Polestar", - "사브": "Saab", - "로터스": "Lotus", - "리비안": "Rivian", - "루시드": "Lucid", - "리막": "Rimac", - "알파 로메오": "Альфа Ромео", - "마쯔다": "Мазда", - "어큐라": "Акура", - "다이하쯔": "Daihatsu", -} - -# Маппинг английских названий брендов → все варианты (корейский + русский перевод). -# Используется для расширения фильтра allowed_brands из runtime_config. -BRAND_ENGLISH_ALIASES: dict[str, list[str]] = { - "hyundai": ["현대", "Хёндэ"], - "kia": ["기아", "Киа"], - "genesis": ["제네시스", "Генезис"], - "chevrolet": ["쉐보레(GM대우)", "쉐보레", "Шевроле"], - "mercedes": ["벤츠", "Мерседес-Бенц"], - "mercedes-benz": ["벤츠", "Мерседес-Бенц"], - "bmw": ["BMW"], - "audi": ["아우디", "Ауди"], - "volkswagen": ["폭스바겐", "폴크스바겐", "Фольксваген"], - "toyota": ["토요타", "도요타", "Тойота"], - "nissan": ["닛산", "Ниссан"], - "honda": ["혼다", "Хонда"], - "porsche": ["포르쉐", "Порше"], - "land rover": ["랜드로버", "Ленд Ровер"], - "jaguar": ["재규어", "Ягуар"], - "volvo": ["볼보", "Вольво"], - "peugeot": ["푸조", "Пежо"], - "citroen": ["시트로엥", "Ситроен", "시트로엥/DS"], - "renault": ["르노", "Рено"], - "mini": ["미니", "Мини"], - "lincoln": ["링컨", "Линкольн"], - "cadillac": ["캐딜락", "Кадиллак"], - "chrysler": ["크라이슬러", "Крайслер"], - "jeep": ["지프", "Джип"], - "ford": ["포드", "Форд"], - "tesla": ["테슬라", "Тесла"], - "lexus": ["렉서스", "Лексус"], - "infiniti": ["인피니티", "Инфинити"], - "acura": ["아큐라", "Акура"], - "subaru": ["스바루", "Субару"], - "mitsubishi": ["미쓰비시", "Мицубиси"], - "mazda": ["마쓰다", "Мазда"], - "suzuki": ["스즈키", "Сузуки"], - "ferrari": ["페라리", "Феррари"], - "lamborghini": ["람보르기니", "Ламборгини"], - "maserati": ["마세라티", "Мазерати"], - "bentley": ["벤틀리", "Бентли"], - "rolls-royce": ["롤스로이스", "Роллс-Ройс"], - "aston martin": ["애스턴마틴", "Астон Мартин"], - "mclaren": ["맥라렌", "Макларен"], - "ssangyong": ["쌍용", "СсангЙонг", "KG모빌리티(쌍용)", "KG Mobility"], - "daewoo": ["대우", "Дэу"], - "dodge": ["닷지"], - "hummer": ["험머"], - "gmc": ["GMC"], - "polestar": ["폴스타"], - "ds": ["DS", "시트로엥/DS"], - "renault korea": ["르노코리아(삼성)", "Рено Корея", "르노삼성", "Рено Самсунг"], - "saab": ["사브"], - "lotus": ["로터스"], - "alfa romeo": ["알파로메오", "Альфа Ромео", "알파 로메오"], - "fiat": ["피아트", "Фиат"], - "smart": ["스마트", "Смарт"], - "skoda": ["스코다", "Шкода"], - "daihatsu": ["다이하츠", "다이하쯔", "Daihatsu"], - "koenigsegg": ["코닉세그", "Кёнигсегг"], - "pagani": ["파가니", "Пагани"], - "bugatti": ["부가티", "Бугатти"], - "maybach": ["마이바흐", "Майбах"], - "rivian": ["리비안"], - "lucid": ["루시드"], - "rimac": ["리막"], -} - - -def expand_allowed_brands(brands: set[str] | None) -> set[str] | None: - """Расширяет набор английских брендов всеми известными alias'ами (корейский + русский).""" - if not brands: - return brands - expanded = set(brands) - for brand in brands: - aliases = BRAND_ENGLISH_ALIASES.get(brand.lower(), []) - for alias in aliases: - expanded.add(alias.lower()) - return expanded - -MODEL_TRANSLATIONS = { - "가솔린": "бензин", - "디젤": "дизель", - "하이브리드": "гибрид", - "전기": "электро", - "터보": "турбо", - "쿠페": "купе", - "세단": "седан", - "왜건": "универсал", - "해치백": "хэтчбек", - "컨버터블": "кабриолет", - "오토": "автомат", - "수동": "механика", - "프레스티지": "престиж", - "시그니처": "сигнатур", - "럭셔리": "люкс", - "스포츠": "спорт", - "롱레인지": "дальний запас хода", - "스탠다드": "стандарт", - "플러스": "плюс", - "리미티드": "лимитед", - "스페셜": "спешл", - "모던": "модерн", - "익스클루시브": "эксклюзив", - "캘리그래피": "каллиграфи", - "노블레스": "ноблесс", - "트림": "трим", - "후륜": "задний привод", - "전륜": "передний привод", - "사륜": "полный привод", -} - -# Переводы цветов с корейского на английский -COLOR_TRANSLATIONS = { - "흰색": "white", - "검정색": "black", - "회색": "gray", - "은색": "silver", - "진회색": "dark gray", - "밝은 회색": "light gray", - "청색": "blue", - "파란색": "blue", - "진청색": "dark blue", - "밝은 청색": "light blue", - "녹색": "green", - "초록색": "green", - "진녹색": "dark green", - "밝은 녹색": "light green", - "적색": "red", - "빨간색": "red", - "진적색": "dark red", - "밝은 적색": "light red", - "주황색": "orange", - "노란색": "yellow", - "갈색": "brown", - "베이지색": "beige", - "아이보리": "ivory", - "크림색": "cream", - "보라색": "purple", - "분홍색": "pink", - "금색": "gold", - "은색": "silver", - "청금색": "blue-gold", - "청은색": "blue-silver", - "흑청색": "black-blue", - "흑적색": "black-red", - "흑녹색": "black-green", - "흑회색": "black-gray", - "흰청색": "white-blue", - "흰적색": "white-red", - "흰녹색": "white-green", - "흰회색": "white-gray", - "청적색": "blue-red", - "청녹색": "blue-green", - "청회색": "blue-gray", - "적녹색": "red-green", - "적회색": "red-gray", - "녹회색": "green-gray", - "기타": "other", - "기타색상": "other", - "특별색상": "special", - "무채색": "neutral", - "메탈릭": "metallic", - "펄": "pearl", - "무광": "matte", - "광택": "glossy", -} - -# Типы кузова -BODY_TYPE_MAP = { - "세단": "SEDAN", - "SUV": "SUV", - "SUV ": "SUV", - "해치백": "HATCHBACK", - "왜건": "WAGON", - "컨버터블": "CONVERTIBLE", - "쿠페": "COUPE", - "픽업": "PICKUP", - "밴": "VAN", - "트럭": "TRUCK", - "버스": "BUS", - "기타": "OTHER", - "승용차": "SEDAN", - "승합차": "VAN", - "화물차": "TRUCK", - "특장차": "SPECIAL", - "경차": "COMPACT", - "중형차": "MID_SIZE", - "대형차": "FULL_SIZE", - "스포츠카": "SPORTS", - "클래식카": "CLASSIC", - "전기차": "ELECTRIC", - "하이브리드": "HYBRID", -} - -# Определение body_type по модели -MODEL_BODY_TYPE_MAP: dict[str, str] = { - # SUV / кроссоверы - "투싼": "SUV", "싼타페": "SUV", "팰리세이드": "SUV", "코나": "SUV", "베뉴": "SUV", - "크레타": "SUV", "넥쏘": "SUV", "아이오닉5": "SUV", "아이오닉6": "SEDAN", - "셀토스": "SUV", "스포티지": "SUV", "쏘렌토": "SUV", "모하비": "SUV", - "니로": "SUV", "EV6": "SUV", "EV9": "SUV", - "티볼리": "SUV", "코란도": "SUV", "렉스턴": "SUV", "토레스": "SUV", "액티언": "SUV", - "GV70": "SUV", "GV80": "SUV", "GV60": "SUV", - "X1": "SUV", "X2": "SUV", "X3": "SUV", "X4": "SUV", "X5": "SUV", "X6": "SUV", "X7": "SUV", "iX": "SUV", "iX3": "SUV", - "Q2": "SUV", "Q3": "SUV", "Q4": "SUV", "Q5": "SUV", "Q7": "SUV", "Q8": "SUV", - "GLA": "SUV", "GLB": "SUV", "GLC": "SUV", "GLE": "SUV", "GLS": "SUV", - "카이엔": "SUV", "마칸": "SUV", - "레인지로버": "SUV", "디스커버리": "SUV", "디펜더": "SUV", - "래브4": "SUV", "하이랜더": "SUV", "FJ 크루져": "SUV", - "CR-V": "SUV", "HR-V": "SUV", - "티구안": "SUV", "투아렉": "SUV", "아틀라스": "SUV", "ID.4": "SUV", - "트레일블레이저": "SUV", "이쿼녹스": "SUV", "트래버스": "SUV", "트랙스": "SUV", "캡티바": "SUV", "올란도": "SUV", - "XC40": "SUV", "XC60": "SUV", "XC90": "SUV", "V40": "HATCHBACK", "V60": "STATION_WAGON", "V90": "STATION_WAGON", - "F-PACE": "SUV", "E-PACE": "SUV", "I-PACE": "SUV", - "모델 Y": "SUV", "Model Y": "SUV", "모델 X": "SUV", "Model X": "SUV", - "RX": "SUV", "NX": "SUV", "UX": "SUV", "LX": "SUV", - "QX50": "SUV", "QX60": "SUV", "QX80": "SUV", - "QM3": "SUV", "QM6": "SUV", - "레니게이드": "SUV", "컴패스": "SUV", "체로키": "SUV", "랭글러": "SUV", "글래디에이터": "PICKUP", - "에비에이터": "SUV", "노틸러스": "SUV", "코세어": "SUV", - "마칸": "SUV", "카이엔": "SUV", - "르반떼": "SUV", "그레칼레": "SUV", - "Z4": "CONVERTIBLE", - # 세단 - "쏘나타": "SEDAN", "아반떼": "SEDAN", "그랜저": "SEDAN", "K5": "SEDAN", - "K8": "SEDAN", "K9": "SEDAN", "K3": "SEDAN", "K7": "SEDAN", - "G70": "SEDAN", "G80": "SEDAN", "G90": "SEDAN", - "1시리즈": "HATCHBACK", - "2시리즈 그란쿠페": "SEDAN", "2시리즈 액티브투어러": "MINIVAN", - "3시리즈": "SEDAN", "3시리즈 GT": "SEDAN", - "5시리즈": "SEDAN", "7시리즈": "SEDAN", "i4": "SEDAN", "i7": "SEDAN", - "A1": "HATCHBACK", "A3": "SEDAN", "A4": "SEDAN", "A6": "SEDAN", "A7": "SEDAN", "A8": "SEDAN", - "A-클래스": "SEDAN", "CLA": "SEDAN", - "C-클래스": "SEDAN", "E-클래스": "SEDAN", "S-클래스": "SEDAN", - "캠리": "SEDAN", "아발론": "SEDAN", "프리우스": "SEDAN", - "어코드": "SEDAN", "시빅": "SEDAN", - "파사트": "SEDAN", "제타": "SEDAN", "아테온": "SEDAN", - "말리부": "SEDAN", "크루즈": "SEDAN", "임팔라": "SEDAN", - "S60": "SEDAN", "S90": "SEDAN", - "IS": "SEDAN", "ES": "SEDAN", "GS": "SEDAN", "LS": "SEDAN", - "Q50": "SEDAN", "Q70": "SEDAN", - "모델 3": "SEDAN", "Model 3": "SEDAN", "모델 S": "SEDAN", "Model S": "SEDAN", - "SM6": "SEDAN", "SM5": "SEDAN", - # 해치백 - "스파크": "HATCHBACK", "모닝": "HATCHBACK", "레이": "HATCHBACK", - "i30": "HATCHBACK", "벨로스터": "HATCHBACK", - "골프": "HATCHBACK", "폴로": "HATCHBACK", "ID.3": "HATCHBACK", - "캐스퍼": "HATCHBACK", - # 미니밴/밴 - "카니발": "MINIVAN", "스타리아": "MINIVAN", "스타렉스": "MINIVAN", - "V-클래스": "MINIVAN", "비토": "VAN", - "시에나": "MINIVAN", "알파드": "MINIVAN", - # 트럭/픽업 - "포터": "PICKUP", "봉고": "PICKUP", "마이티": "TRUCK", - "타코마": "PICKUP", "터프": "PICKUP", - "콜로라도": "PICKUP", "실버라도": "PICKUP", - # 쿠페 - "4시리즈": "COUPE", "8시리즈": "COUPE", "2시리즈": "COUPE", - "A5": "COUPE", "TT": "COUPE", - "RC": "COUPE", "LC": "COUPE", -} - # Regex для парсинга объёма двигателя из Badge ENGINE_VOLUME_RE = re.compile(r"(\d+\.\d+)\s*(?:T|터보)?") # Regex для европейских кодов: "530i", "C200", "A220", "GLC300", "740d", "M340i", "S63", "120i", "E220d" и т.д. @@ -848,9 +480,9 @@ class EncarMapper: class EncarScraper: DEFAULT_PAGE_SIZE = 1000 # API поддерживает до 1000 - def __init__(self, settings: Settings | None = None) -> None: + def __init__(self, settings: Settings | None = None, persistence: PersistenceService | None = None) -> None: self.settings = settings or Settings() - self.persistence = PersistenceService(self.settings) + self.persistence = persistence or PersistenceService(self.settings) self.car_mapper = EncarMapper() self._batch_pool: urllib3.HTTPSConnectionPool | None = None diff --git a/encar_scraper/translations.py b/encar_scraper/translations.py new file mode 100644 index 0000000..657bd48 --- /dev/null +++ b/encar_scraper/translations.py @@ -0,0 +1,680 @@ +# Словари переводов корейского текста русский / английский. +# Используется в encar.py для маппинга данных Encar API. + + +# Топливо + +FUEL_TYPE_MAP = { + "가솔린": "petrol", + "gasoline": "petrol", + "디젤": "diesel", + "diesel": "diesel", + "전기": "electric", + "electric": "electric", + "하이브리드": "hybrid", + "hybrid": "hybrid", + "lpg": "lpg", + "LPG": "lpg", + "LPG(일반인 구입)": "lpg", + "LPG(장애인 구입)": "lpg", + "가솔린+전기": "hybrid", + "디젤+전기": "hybrid", + "CNG": "cng", +} + + +# КПП + +GEARBOX_MAP_KO = { + "오토": "AT", + "자동": "AT", + "수동": "MT", + "cvt": "CVT", + "CVT": "CVT", +} + + +# Тип продажи + +SELL_TYPE_MAP = { + "일반": "STOCK", + "경매": "AUCTION", + "입찰": "TENDER", +} + + +# Город → страна + +CITY_TO_COUNTRY = { + "서울": "KR", "경기": "KR", "인천": "KR", "부산": "KR", "대구": "KR", + "광주": "KR", "대전": "KR", "울산": "KR", "세종": "KR", "강원": "KR", + "충북": "KR", "충남": "KR", "전북": "KR", "전남": "KR", "경북": "KR", + "경남": "KR", "제주": "KR", +} + + +# Бренды: корейский → русский + +BRAND_TRANSLATIONS = { + "현대": "Хёндэ", + "기아": "Киа", + "쉐보레(GM대우)": "Шевроле", + "벤츠": "Мерседес-Бенц", + "BMW": "BMW", + "아우디": "Ауди", + "폭스바겐": "Фольксваген", + "토요타": "Тойота", + "도요타": "Тойота", + "닛산": "Ниссан", + "혼다": "Хонда", + "포르쉐": "Порше", + "랜드로버": "Ленд Ровер", + "재규어": "Ягуар", + "볼보": "Вольво", + "푸조": "Пежо", + "시트로엥": "Ситроен", + "르노": "Рено", + "미니": "Мини", + "링컨": "Линкольн", + "캐딜락": "Кадиллак", + "크라이슬러": "Крайслер", + "지프": "Джип", + "포드": "Форд", + "테슬라": "Тесла", + "폴크스바겐": "Фольксваген", + "스코다": "Шкода", + "세아트": "SEAT", + "오펠": "Опель", + "피아트": "Фиат", + "알파로메오": "Альфа Ромео", + "마세라티": "Мазерати", + "람보르기니": "Ламборгини", + "페라리": "Феррари", + "벤틀리": "Бентли", + "롤스로이스": "Роллс-Ройс", + "애스턴마틴": "Астон Мартин", + "맥라렌": "Макларен", + "부가티": "Бугатти", + "코닉세그": "Кёнигсегг", + "파가니": "Пагани", + "헤네시": "Hennessey", + "쉘비": "Shelby", + "다임러": "Daimler", + "마이바흐": "Майбах", + "스마트": "Смарт", + "DS": "DS", + "렉서스": "Лексус", + "인피니티": "Инфинити", + "아큐라": "Акура", + "스바루": "Субару", + "미쓰비시": "Мицубиси", + "마쓰다": "Мазда", + "이스즈": "Исузу", + "다이하츠": "Daihatsu", + "스즈키": "Сузуки", + "KG모빌리티(쌍용)": "KG Mobility", + "르노코리아(삼성)": "Рено Корея", + "한국GM": "GM Korea", + "쌍용": "СсангЙонг", + "대우": "Дэу", + "제네시스": "Генезис", + "르노삼성": "Рено Самсунг", + "로터스": "Лотус", + "리비안": "Ривиан", + "루시드": "Люсид", + "리막": "Римак", + "알파 로메오": "Альфа Ромео", + "마쯔다": "Мазда", + "어큐라": "Акура", + "다이하쯔": "Дайхатсу", + # --- Новые бренды (с сайта Encar) --- + "쉐보레": "Шевроле", + "시트로엥/DS": "Ситроен/DS", + "미쯔비시": "Мицубиси", + "미쯔오까": "Мицуока", + "동풍소콘": "Донгфенг Сокон", + "북기은상": "BAIC", + "신위안": "Skywell", + "이네오스": "Инеос", + "지리": "Джили", + "머큐리": "Меркурий", + "사이언": "Scion", + "뷰익": "Бьюик", + "새턴": "Saturn", + "올즈모빌": "Oldsmobile", + "폰티악": "Понтиак", + "포톤": "Foton", + "MG로버": "MG Rover", + "닷지": "Додж", + "험머": "Хаммер", + "폴스타": "Полстар", + "사브": "Сааб", + "기타 제조사": "Другие", + "기타 수입차": "Другие импортные", +} + + +# Бренды: английский → алиасы (корейский + русский) + +BRAND_ENGLISH_ALIASES: dict[str, list[str]] = { + "hyundai": ["현대", "Хёндэ"], + "kia": ["기아", "Киа"], + "genesis": ["제네시스", "Генезис"], + "chevrolet": ["쉐보레(GM대우)", "쉐보레", "Шевроле"], + "mercedes": ["벤츠", "Мерседес-Бенц"], + "mercedes-benz": ["벤츠", "Мерседес-Бенц"], + "bmw": ["BMW"], + "audi": ["아우디", "Ауди"], + "volkswagen": ["폭스바겐", "폴크스바겐", "Фольксваген"], + "toyota": ["토요타", "도요타", "Тойота"], + "nissan": ["닛산", "Ниссан"], + "honda": ["혼다", "Хонда"], + "porsche": ["포르쉐", "Порше"], + "land rover": ["랜드로버", "Ленд Ровер"], + "jaguar": ["재규어", "Ягуар"], + "volvo": ["볼보", "Вольво"], + "peugeot": ["푸조", "Пежо"], + "citroen": ["시트로엥", "Ситроен", "시트로엥/DS", "Ситроен/DS"], + "renault": ["르노", "Рено"], + "mini": ["미니", "Мини"], + "lincoln": ["링컨", "Линкольн"], + "cadillac": ["캐딜락", "Кадиллак"], + "chrysler": ["크라이슬러", "Крайслер"], + "jeep": ["지프", "Джип"], + "ford": ["포드", "Форд"], + "tesla": ["테슬라", "Тесла"], + "lexus": ["렉서스", "Лексус"], + "infiniti": ["인피니티", "Инфинити"], + "acura": ["아큐라", "어큐라", "Акура"], + "subaru": ["스바루", "Субару"], + "mitsubishi": ["미쓰비시", "미쯔비시", "Мицубиси"], + "mazda": ["마쓰다", "마쯔다", "Мазда"], + "suzuki": ["스즈키", "Сузуки"], + "ferrari": ["페라리", "Феррари"], + "lamborghini": ["람보르기니", "Ламборгини"], + "maserati": ["마세라티", "Мазерати"], + "bentley": ["벤틀리", "Бентли"], + "rolls-royce": ["롤스로이스", "Роллс-Ройс"], + "aston martin": ["애스턴마틴", "Астон Мартин"], + "mclaren": ["맥라렌", "Макларен"], + "ssangyong": ["쌍용", "СсангЙонг", "KG모빌리티(쌍용)", "KG Mobility"], + "kg mobility": ["KG모빌리티(쌍용)", "KG Mobility", "쌍용", "СсангЙонг"], + "daewoo": ["대우", "Дэу"], + "dodge": ["닷지", "Додж"], + "hummer": ["험머", "Хаммер"], + "gmc": ["GMC"], + "polestar": ["폴스타", "Полстар"], + "ds": ["DS", "시트로엥/DS", "Ситроен/DS"], + "renault korea": ["르노코리아(삼성)", "Рено Корея", "르노삼성", "Рено Самсунг"], + "saab": ["사브", "Сааб"], + "lotus": ["로터스", "Лотус"], + "alfa romeo": ["알파로메오", "Альфа Ромео", "알파 로메오"], + "fiat": ["피아트", "Фиат"], + "smart": ["스마트", "Смарт"], + "skoda": ["스코다", "Шкода"], + "daihatsu": ["다이하츠", "다이하쯔", "Дайхатсу"], + "koenigsegg": ["코닉세그", "Кёнигсегг"], + "pagani": ["파가니", "Пагани"], + "bugatti": ["부가티", "Бугатти"], + "maybach": ["마이바흐", "Майбах"], + "rivian": ["리비안", "Ривиан"], + "lucid": ["루시드", "Люсид"], + "rimac": ["리막", "Римак"], + "byd": ["BYD"], + "geely": ["지리", "Джили"], + "dongfeng": ["동풍소콘", "Донгфенг Сокон"], + "baic": ["북기은상", "BAIC"], + "skywell": ["신위안", "Skywell"], + "ineos": ["이네오스", "Инеос"], + "mercury": ["머큐리", "Меркурий"], + "scion": ["사이언", "Scion"], + "buick": ["뷰익", "Бьюик"], + "saturn": ["새턴", "Saturn"], + "oldsmobile": ["올즈모빌", "Oldsmobile"], + "pontiac": ["폰티악", "Понтиак"], + "foton": ["포톤", "Foton"], + "mg rover": ["MG로버", "MG Rover"], + "mitsuoka": ["미쯔오까", "Мицуока"], + "other": ["기타 제조사", "기타 수입차", "Другие", "Другие импортные"], +} + + +def expand_allowed_brands(brands: set[str] | None) -> set[str] | None: + """Расширяет набор английских брендов всеми известными alias'ами (корейский + русский).""" + if not brands: + return brands + expanded = set(brands) + for brand in brands: + aliases = BRAND_ENGLISH_ALIASES.get(brand.lower(), []) + for alias in aliases: + expanded.add(alias.lower()) + return expanded + + + +# Модели / комплектации: корейский → русский + +MODEL_TRANSLATIONS = { + # --- Топливо / тип двигателя --- + "가솔린": "бензин", + "디젤": "дизель", + "하이브리드": "гибрид", + "전기": "электро", + "터보": "турбо", + "일렉트릭": "электрик", + "일렉트리파이드": "электрифайд", + "블루텍": "BlueTEC", + "블루모션": "BlueMotion", + # --- Кузов / конструкция --- + "쿠페": "купе", + "세단": "седан", + "왜건": "универсал", + "웨건": "универсал", + "해치백": "хэтчбек", + "컨버터블": "кабриолет", + "카브리올레": "кабриолет", + "크로스오버": "кроссовер", + "리무진": "лимузин", + "하이리무진": "хай-лимузин", + "하이리무진(특장업체)": "хай-лимузин(спецтехника)", + "스포트백": "спортбэк", + "스파이더": "спайдер", + "밴": "вэн", + "픽업": "пикап", + "캠핑카": "кемпер", + # --- КПП / привод --- + "오토": "автомат", + "수동": "механика", + "후륜": "задний привод", + "전륜": "передний привод", + "사륜": "полный привод", + "4모션": "4Motion", + # --- Трим / комплектация --- + "프레스티지": "престиж", + "시그니처": "сигнатур", + "럭셔리": "люкс", + "스포츠": "спорт", + "롱레인지": "дальний запас хода", + "스탠다드": "стандарт", + "플러스": "плюс", + "리미티드": "лимитед", + "스페셜": "спешл", + "모던": "модерн", + "익스클루시브": "эксклюзив", + "캘리그래피": "каллиграфи", + "노블레스": "ноблесс", + "트림": "трим", + "프리미엄": "премиум", + "프리미어": "премьер", + "플래티넘": "платинум", + "플래티넘Ⅱ": "платинум II", + "인스퍼레이션": "инспирейшн", + "트렌디": "тренди", + "다이나믹": "динамик", + "프라임": "прайм", + "초이스": "чойс", + "르블랑": "Ле Блан", + "프레지던트": "президент", + "인스크립션": "инскрипшн", + "볼드": "болд", + "밸류": "вэлью", + "퓨어": "пьюр", + "엑셀런스": "экселленс", + "얼티메이트": "алтимейт", + "에센셜": "эссеншл", + "슈프림": "суприм", + "에어": "эйр", + "어반": "урбан", + "탑": "топ", + "코어": "кор", + "엘리트": "элит", + "셀렉션": "селекшн", + "브라이트": "брайт", + "클래식": "классик", + "고급형": "высшая комплектация", + "최고급형": "максимальная комплектация", + "기본형": "базовая комплектация", + "디럭스": "делюкс", + "디럭스팩": "делюкс-пакет", + "마이스터": "мастер", + "마스터": "мастер", + "마스터즈": "мастерс", + "어메이징": "амейзинг", + "파워": "пауэр", + "리저브": "резерв", + "퍼펙트": "перфект", + "리얼": "реал", + "울트라": "ультра", + "세이프티": "сейфти", + "퍼포먼스": "перформанс", + "인텔리전트": "интеллиджент", + "컬렉션": "коллекшн", + "브릴리언트": "бриллиант", + "어드밴티지": "адвантейдж", + "파이니스트": "файнест", + "런치팩": "ланч-пак", + "페이버드": "фейворд", + "엘레강스": "элеганс", + "하이": "хай", + "블랙라벨": "Блэк Лейбл", + "플럭스": "флюкс", + "테크": "тек", + "프로": "про", + "베스트": "бест", + "라이즈": "райз", + "액티브": "актив", + "롱": "лонг", + "패키지": "пакет", + "에디션": "эдишн", + "디자인": "дизайн", + "라운지": "лаунж", + "아트": "арт", + "팝": "поп", + "조이": "джой", + "레드라인": "редлайн", + "스타일": "стайл", + "블랙": "блэк", + "라이트": "лайт", + "온라인": "онлайн", + "론지튜드": "лонгитюд", + "오버랜드": "оверленд", + "컴페티션": "компетишн", + "올스페이스": "оллспейс", + "스타": "стар", + # --- Поколение / номер --- + "1세대": "1 поколение", + "2세대": "2 поколение", + "3세대": "3 поколение", + "4세대": "4 поколение", + "5세대": "5 поколение", + "6세대": "6 поколение", + "7세대": "7 поколение", + "8세대": "8 поколение", + "10세대": "10 поколение", + "더": "", + "뉴": "нью", + "넥스트": "некст", + "올": "олл", + "그란": "гран", + "그랜드": "гранд", + # --- Посадочных мест --- + "2인승": "2-местный", + "3인승": "3-местный", + "5인승": "5-местный", + "6인승": "6-местный", + "7인승": "7-местный", + "9인승": "9-местный", + "11인승": "11-местный", + "12인승": "12-местный", + # --- Двери --- + "4도어": "4-дверный", + "5도어": "5-дверный", + # --- Модели Hyundai --- + "팰리세이드": "Палисейд", + "싼타페": "Санта Фе", + "투싼": "Туксон", + "아반떼": "Аванте", + "쏘나타": "Соната", + "그랜저": "Грэнджер", + "아이오닉5": "Ioniq 5", + "아이오닉6": "Ioniq 6", + "아이오닉": "Ioniq", + "넥쏘": "Нексо", + "벨로스터": "Велостер", + "코나": "Кона", + "캐스퍼": "Каспер", + "베뉴": "Венью", + "스타렉스": "Старекс", + "스타리아": "Стариа", + "에쿠스(신형)": "Экус", + "엑센트(신형)": "Акцент", + "아슬란": "Аслан", + "베라크루즈": "Веракруз", + # --- Модели Kia --- + "카니발": "Карнивал", + "쏘렌토": "Соренто", + "스포티지": "Спортейдж", + "모닝": "Морнинг", + "뉴모닝": "нью Морнинг", + "레이": "Рэй", + "셀토스": "Селтос", + "니로": "Ниро", + "스팅어": "Стингер", + "쏘울": "Соул", + "모하비": "Мохаве", + "프라이드": "Прайд", + "스토닉": "Стоник", + # --- Модели Chevrolet/GM --- + "스파크": "Спарк", + "말리부": "Малибу", + "트랙스": "Тракс", + "크루즈": "Круз", + "올란도": "Орландо", + "트레일블레이저": "Трейлблейзер", + "콜로라도": "Колорадо", + "트래버스": "Траверс", + "캡티바": "Каптива", + "이쿼녹스": "Эквинокс", + "아베오": "Авео", + "볼트": "Вольт", + "임팔라": "Импала", + "맥스크루즈": "МаксКруз", + # --- Модели Genesis --- + "제네시스": "Генезис", + # --- Модели иностранных авто (транслитерация) --- + "익스플로러": "Эксплорер", + "머스탱": "Мустанг", + "카이엔": "Каен", + "파나메라": "Панамера", + "마칸": "Макан", + "타이칸": "Тайкан", + "박스터": "Бокстер", + "카레라": "Каррера", + "티구안": "Тигуан", + "골프": "Гольф", + "제타": "Джетта", + "투아렉": "Туарег", + "파사트": "Пассат", + "아테온": "Артеон", + "콰트로": "кваттро", + "아방가르드": "Авангард", + "쿠퍼": "Купер", + "컨트리맨": "Кантримен", + "클럽맨": "Клабмен", + "디스커버리": "Дискавери", + "레인지로버": "Рейндж Ровер", + "이보크": "Эвок", + "벨라": "Велар", + "디펜더": "Дефендер", + "랭글러": "Рэнглер", + "체로키": "Чероки", + "체로키(KL)": "Чероки (KL)", + "체로키(WL)": "Чероки (WL)", + "레니게이드": "Ренегат", + "컴패스": "Компас", + "루비콘": "Рубикон", + "에비에이터": "Авиатор", + "에스컬레이드": "Эскалейд", + "컨티넨탈": "Континенталь", + "르반떼": "Леванте", + "기블리": "Гибли", + "알티마": "Альтима", + "오딧세이": "Одиссей", + "어코드": "Аккорд", + "캠리": "Камри", + "시에나": "Сиенна", + "프리우스": "Приус", + "크로스컨트리": "Кросс Кантри", + "타스만": "Тасман", + # --- Служебные / категории --- + "(세부등급": "(подуровень", + "없음)": "отсутствует)", + "(렌터카)": "(прокат)", + "(렌터카용)": "(для проката)", + "LPG(렌터카용)": "LPG(для проката)", + "LPI(렌터카용)": "LPI(для проката)", + "(택시형)": "(такси)", + "택시형": "такси", + "어린이보호차": "детский транспорт", + "카고": "карго", + "모델": "модель", + "스마트": "Смарт", + "기아": "Киа", + "마이바흐": "Майбах", + "레인지": "Рейндж", +} + + +# Цвета: корейский → английский + +COLOR_TRANSLATIONS = { + "흰색": "white", + "검정색": "black", + "회색": "gray", + "은색": "silver", + "진회색": "dark gray", + "밝은 회색": "light gray", + "청색": "blue", + "파란색": "blue", + "진청색": "dark blue", + "밝은 청색": "light blue", + "녹색": "green", + "초록색": "green", + "진녹색": "dark green", + "밝은 녹색": "light green", + "적색": "red", + "빨간색": "red", + "진적색": "dark red", + "밝은 적색": "light red", + "주황색": "orange", + "노란색": "yellow", + "갈색": "brown", + "베이지색": "beige", + "아이보리": "ivory", + "크림색": "cream", + "보라색": "purple", + "분홍색": "pink", + "금색": "gold", + "청금색": "blue-gold", + "청은색": "blue-silver", + "흑청색": "black-blue", + "흑적색": "black-red", + "흑녹색": "black-green", + "흑회색": "black-gray", + "흰청색": "white-blue", + "흰적색": "white-red", + "흰녹색": "white-green", + "흰회색": "white-gray", + "청적색": "blue-red", + "청녹색": "blue-green", + "청회색": "blue-gray", + "적녹색": "red-green", + "적회색": "red-gray", + "녹회색": "green-gray", + "기타": "other", + "기타색상": "other", + "특별색상": "special", + "무채색": "neutral", + "메탈릭": "metallic", + "펄": "pearl", + "무광": "matte", + "광택": "glossy", +} + + +# Тип кузова: корейский → enum + +BODY_TYPE_MAP = { + "세단": "SEDAN", + "SUV": "SUV", + "SUV ": "SUV", + "해치백": "HATCHBACK", + "왜건": "WAGON", + "컨버터블": "CONVERTIBLE", + "쿠페": "COUPE", + "픽업": "PICKUP", + "밴": "VAN", + "트럭": "TRUCK", + "버스": "BUS", + "기타": "OTHER", + "승용차": "SEDAN", + "승합차": "VAN", + "화물차": "TRUCK", + "특장차": "SPECIAL", + "경차": "COMPACT", + "중형차": "MID_SIZE", + "대형차": "FULL_SIZE", + "스포츠카": "SPORTS", + "클래식카": "CLASSIC", + "전기차": "ELECTRIC", + "하이브리드": "HYBRID", +} + + +# Определение body_type по модели (fallback) + +MODEL_BODY_TYPE_MAP: dict[str, str] = { + # SUV / кроссоверы + "투싼": "SUV", "싼타페": "SUV", "팰리세이드": "SUV", "코나": "SUV", "베뉴": "SUV", + "크레타": "SUV", "넥쏘": "SUV", "아이오닉5": "SUV", "아이오닉6": "SEDAN", + "셀토스": "SUV", "스포티지": "SUV", "쏘렌토": "SUV", "모하비": "SUV", + "니로": "SUV", "EV6": "SUV", "EV9": "SUV", + "티볼리": "SUV", "코란도": "SUV", "렉스턴": "SUV", "토레스": "SUV", "액티언": "SUV", + "GV70": "SUV", "GV80": "SUV", "GV60": "SUV", + "X1": "SUV", "X2": "SUV", "X3": "SUV", "X4": "SUV", "X5": "SUV", "X6": "SUV", "X7": "SUV", "iX": "SUV", "iX3": "SUV", + "Q2": "SUV", "Q3": "SUV", "Q4": "SUV", "Q5": "SUV", "Q7": "SUV", "Q8": "SUV", + "GLA": "SUV", "GLB": "SUV", "GLC": "SUV", "GLE": "SUV", "GLS": "SUV", + "카이엔": "SUV", "마칸": "SUV", + "레인지로버": "SUV", "디스커버리": "SUV", "디펜더": "SUV", + "래브4": "SUV", "하이랜더": "SUV", "FJ 크루져": "SUV", + "CR-V": "SUV", "HR-V": "SUV", + "티구안": "SUV", "투아렉": "SUV", "아틀라스": "SUV", "ID.4": "SUV", + "트레일블레이저": "SUV", "이쿼녹스": "SUV", "트래버스": "SUV", "트랙스": "SUV", "캡티바": "SUV", "올란도": "SUV", + "XC40": "SUV", "XC60": "SUV", "XC90": "SUV", "V40": "HATCHBACK", "V60": "STATION_WAGON", "V90": "STATION_WAGON", + "F-PACE": "SUV", "E-PACE": "SUV", "I-PACE": "SUV", + "모델 Y": "SUV", "Model Y": "SUV", "모델 X": "SUV", "Model X": "SUV", + "RX": "SUV", "NX": "SUV", "UX": "SUV", "LX": "SUV", + "QX50": "SUV", "QX60": "SUV", "QX80": "SUV", + "QM3": "SUV", "QM6": "SUV", + "레니게이드": "SUV", "컴패스": "SUV", "체로키": "SUV", "랭글러": "SUV", "글래디에이터": "PICKUP", + "에비에이터": "SUV", "노틸러스": "SUV", "코세어": "SUV", + "르반떼": "SUV", "그레칼레": "SUV", + "Z4": "CONVERTIBLE", + # 세단 + "쏘나타": "SEDAN", "아반떼": "SEDAN", "그랜저": "SEDAN", "K5": "SEDAN", + "K8": "SEDAN", "K9": "SEDAN", "K3": "SEDAN", "K7": "SEDAN", + "G70": "SEDAN", "G80": "SEDAN", "G90": "SEDAN", + "1시리즈": "HATCHBACK", + "2시리즈 그란쿠페": "SEDAN", "2시리즈 액티브투어러": "MINIVAN", + "3시리즈": "SEDAN", "3시리즈 GT": "SEDAN", + "5시리즈": "SEDAN", "7시리즈": "SEDAN", "i4": "SEDAN", "i7": "SEDAN", + "A1": "HATCHBACK", "A3": "SEDAN", "A4": "SEDAN", "A6": "SEDAN", "A7": "SEDAN", "A8": "SEDAN", + "A-클래스": "SEDAN", "CLA": "SEDAN", + "C-클래스": "SEDAN", "E-클래스": "SEDAN", "S-클래스": "SEDAN", + "캠리": "SEDAN", "아발론": "SEDAN", "프리우스": "SEDAN", + "어코드": "SEDAN", "시빅": "SEDAN", + "파사트": "SEDAN", "제타": "SEDAN", "아테온": "SEDAN", + "말리부": "SEDAN", "크루즈": "SEDAN", "임팔라": "SEDAN", + "S60": "SEDAN", "S90": "SEDAN", + "IS": "SEDAN", "ES": "SEDAN", "GS": "SEDAN", "LS": "SEDAN", + "Q50": "SEDAN", "Q70": "SEDAN", + "모델 3": "SEDAN", "Model 3": "SEDAN", "모델 S": "SEDAN", "Model S": "SEDAN", + "SM6": "SEDAN", "SM5": "SEDAN", + # 해치백 + "스파크": "HATCHBACK", "모닝": "HATCHBACK", "레이": "HATCHBACK", + "i30": "HATCHBACK", "벨로스터": "HATCHBACK", + "골프": "HATCHBACK", "폴로": "HATCHBACK", "ID.3": "HATCHBACK", + "캐스퍼": "HATCHBACK", + # 미니밴/밴 + "카니발": "MINIVAN", "스타리아": "MINIVAN", "스타렉스": "MINIVAN", + "V-클래스": "MINIVAN", "비토": "VAN", + "시에나": "MINIVAN", "알파드": "MINIVAN", + # 트럭/픽업 + "포터": "PICKUP", "봉고": "PICKUP", "마이티": "TRUCK", + "타코마": "PICKUP", "터프": "PICKUP", + "콜로라도": "PICKUP", "실버라도": "PICKUP", + # 쿠페 + "4시리즈": "COUPE", "8시리즈": "COUPE", "2시리즈": "COUPE", + "A5": "COUPE", "TT": "COUPE", + "RC": "COUPE", "LC": "COUPE", +}