Подсчёт упоминаний бренда и ключей в текстах и выдаче: регулярное выражение по подстроке ошибается, лемматизация нет
При разметке выдачи и ответов нейросетей нужно считать, где назван бренд и где стоит ключ. Поиск подстрокой даёт ложные срабатывания: nord находит «nordwind», основа мар\w* для имени «Мария» находит «маршрут». В обратную сторону подстрока теряет склонения: «авторских туров на Бали» не совпадает с ключом «авторский тур бали».
Бренд латиницей ищется как целое слово, с границами по буквам и дефису (просмотр назад и вперёд из модуля re):
python
import re
def has_brand(text, brand):
return re.search(rf'(?<![\w-]){re.escape(brand)}(?![\w-])', text, re.I) is not None
>>> bool(re.search('nord', 'перелёт nordwind, туры на север', re.I))
True
>>> has_brand('перелёт nordwind, туры на север', 'nord')
False
>>> has_brand('Nord Travel: туры на север', 'nord')
True
Русские ключи и имена сравниваются по начальным формам слов. Для этого подходит pymorphy3, морфологический анализатор для русского языка, продолжение pymorphy2 (документация):
python
import pymorphy3
morph = pymorphy3.MorphAnalyzer()
def lemmas(text):
return {morph.parse(w)[0].normal_form
for w in re.findall(r'[а-яёa-z0-9-]+', text.lower())}
def has_key(text, key):
return lemmas(key) <= lemmas(text)
>>> has_key('авторских туров на Бали', 'авторский тур бали')
True
>>> has_key('тур на Бали', 'авторский тур бали')
False
>>> bool(re.search(r'\bмар\w*', 'маршрут по острову'))
True
>>> has_key('маршрут по острову', 'Мария')
False
>>> lemmas('Марией Марию Марии')
{'мария'}
Ключ засчитывается, если все его слова есть в тексте в любой форме и в любом порядке. Порядок и соседство слов для ключа обычно не важны: «туры на Бали авторские» тот же запрос. Если важна точная фраза, сравнивается последовательность лемм.
Тот же приём проверяет минус-слова рекламной кампании. Минус-слово, чья лемма совпадает с леммой рабочего ключа, отрезает этот ключ от показов. Такую пару стоит найти до запуска:
python
def conflicts(keys, minus):
m = {morph.parse(w)[0].normal_form: w for w in minus}
return [(k, m[l]) for k in keys for l in lemmas(k) if l in m]
>>> conflicts(['котельная под ключ', 'блочные котельные цена'], ['ключи', 'бесплатно'])
[('котельная под ключ', 'ключи')]
Автоматический подсчёт остаётся черновым. Подписи источников в ответах нейросетей похожи на названия компаний, у имени клиента бывают однофамильцы и тёзки. Итоговая разметка «назван или не назван» ставится чтением каждого ответа, скрипт задаёт только список мест для проверки.
Источники: