Реализация дедупликации спарсенных данных
Парсинг нескольких источников неизбежно приводит к дублям: один товар присутствует на сайте производителя, в трёх дистрибьюторских каталогах и на маркетплейсе. Наивное сравнение по URL или названию работает плохо — нужны более умные подходы.
Уровни дедупликации
Уровень 1 — Точное совпадение. По нормализованному ключу: SKU, EAN/GTIN, артикул производителя. Самый надёжный способ, работает там, где есть уникальный идентификатор.
def normalize_sku(raw_sku: str) -> str:
# убираем пробелы, дефисы, приводим к верхнему регистру
return re.sub(r'[\s\-_/]', '', raw_sku).upper()
Уровень 2 — Хеширование контента. Для контента (статьи, описания) — нормализуем текст и считаем хеш.
def content_hash(text: str) -> str:
normalized = ' '.join(text.lower().split()) # убираем лишние пробелы
return hashlib.sha256(normalized.encode()).hexdigest()
Уровень 3 — Нечёткое совпадение (fuzzy matching). Для товаров без явного SKU — сравнение названий по расстоянию Левенштейна или алгоритмам Token Sort/Token Set Ratio.
from rapidfuzz import fuzz, process
def find_duplicate(new_title: str, existing_titles: list[str], threshold=85):
result = process.extractOne(
new_title,
existing_titles,
scorer=fuzz.token_sort_ratio
)
if result and result[1] >= threshold:
return result[0]
return None
token_sort_ratio сортирует слова перед сравнением — хорошо работает с перестановками слов в названиях товаров.
Уровень 4 — Векторное сходство. Для текстов с семантическим значением — embeddings через sentence-transformers и cosine similarity.
from sentence_transformers import SentenceTransformer
import numpy as np
model = SentenceTransformer('paraphrase-multilingual-MiniLM-L12-v2')
def are_similar(text1: str, text2: str, threshold=0.92) -> bool:
embeddings = model.encode([text1, text2])
cosine_sim = np.dot(embeddings[0], embeddings[1]) / (
np.linalg.norm(embeddings[0]) * np.linalg.norm(embeddings[1])
)
return float(cosine_sim) >= threshold
Для больших объёмов — индекс в pgvector (PostgreSQL) или Milvus для приближённого поиска по векторам.
Производительность
При миллионах записей попарное сравнение неприемлемо. Стратегии:
- MinHash + LSH (Locality Sensitive Hashing) — быстрое нахождение кандидатов на дубли в больших наборах текстов
- Blocking — сначала фильтруем по точным атрибутам (категория, ценовой диапазон), потом нечёткое сравнение только внутри блока
-
Индексы в PostgreSQL —
pg_trgmдля нечёткого поиска по строкам сsimilarity()и%оператором
-- Установка расширения
CREATE EXTENSION pg_trgm;
CREATE INDEX ON products USING GIN (title gin_trgm_ops);
-- Поиск похожих названий
SELECT id, title, similarity(title, 'Iphone 15 pro max 256') AS sim
FROM products
WHERE title % 'Iphone 15 pro max 256'
ORDER BY sim DESC
LIMIT 10;
Управление дублями
Найденные дубли не удаляются автоматически. Система формирует группы кандидатов с вычисленным score совпадения. Финальное решение — либо автоматическое (при score > 95%), либо через интерфейс ручной проверки.
Время реализации системы дедупликации с несколькими уровнями: 4–7 рабочих дней.







