Англоязычные SEO-инструменты занижают русские тексты. Померили разрыв на 98 блоках

Прогнали свой сайт через популярный инструмент оценки цитируемости для ИИ-поиска. Он поставил F почти всем блокам и средний балл 31 из 100. Выглядело как приговор: тексты никуда не годятся, переписывать всё.
Переписали рубрику под русский язык, ничего не меняя в весах и в логике измерений. Тот же сайт, те же 98 блоков текста, тот же алгоритм. Получили 42,5.
Разрыв 11,2 пункта взялся не из щедрости новой рубрики. Он взялся из трёх регулярных выражений.
Откуда это вылезло
Мы делаем GEO-аудиты, то есть смотрим, насколько сайт пригоден для цитирования ИИ-поисковиками. В наборе инструментов есть скрипт, который режет страницу на смысловые блоки и оценивает каждый по нескольким измерениям: есть ли в начале законченный ответ, самодостаточен ли блок без соседей, насколько плотно в нём факты, есть ли конкретика.
Логика здравая. Модель вытаскивает из страницы фрагмент и вставляет в ответ. Фрагмент, который начинается с «это» и не содержит ни одного числа, вставить некуда.
Скрипт написан на английском корпусе. Мы прогнали им русский сайт и получили картину, которая не билась с ощущением от текста. Решили проверить.
Три места, где ломается
Определения
Инструмент ищет в начале блока законченное определение. Шаблоны такие:
r'\b\w+\s+(?:is|are)\s+(?:a|an|the)\b|\brefers to\b|\bmeans\b'
X is a Y, X refers to Y, X means Y. Три конструкции, покрывающие почти все способы дать определение по-английски.
По-русски определение выглядит иначе: «X это Y», «X называется Y», «X представляет собой Y». Ни одна из них под шаблон не попадает. Русский блок, который открывается идеальным определением, получает ноль за это измерение.
Вес измерения в рубрике самый большой. Двадцать баллов из пятидесяти пяти.
Имена собственные
Конкретику скрипт ищет через имена собственные:
r'\b[A-Z][a-z]+\b'
Заглавная латинская буква, за ней строчные латинские. Кириллица не подходит под класс [A-Z] и под [a-z] тоже. «Яндекс», «Ленинградская область», «Сбербанк» для этого выражения не существуют.
Русский текст, набитый названиями компаний и городов, получает по этому измерению ровно ноль. Всегда. Независимо от содержания.
Деньги и проценты
r'\$[\d,]+(?:\.\d+)?'
Доллар, цифры, запятые как разделители тысяч. Рубли не ловятся ни в одном написании: ни «15 000 рублей», ни «15 000 ₽», ни «15 тыс. руб.».
С процентами тоньше. Выражение \d+\s*% работает на обоих языках, но в русских текстах процент часто пишут словом: «доля отказов выше 65 процентов». Такая запись мимо.
Как это выглядит на одном блоке
Возьмём один и тот же смысл на двух языках и прогоним упрощённой рубрикой. Четыре измерения, максимум 55 баллов.
import re
# Как ищет англоязычная рубрика
EN_DEFINITION = re.compile(
r'\b\w+\s+(?:is|are)\s+(?:a|an|the)\b|\brefers to\b|\bmeans\b', re.I)
EN_PROPER_NOUN = re.compile(r'\b[A-Z][a-z]+\b')
EN_MONEY = re.compile(r'\$[\d,]+(?:\.\d+)?')
EN_PERCENT = re.compile(r'\b\d+(?:\.\d+)?\s*%')
# То же самое под русский
RU_DEFINITION = re.compile(
r'\b\w+\s+[-–—]\s+это\b|\bназывается\b|\bпредставляет собой\b|\bозначает\b', re.I)
RU_PROPER_NOUN = re.compile(r'\b[А-ЯЁ][а-яё]+\b')
RU_MONEY = re.compile(r'\b\d[\d\s]*(?:руб|₽|рубл)', re.I)
RU_PERCENT = re.compile(r'\b\d+(?:[.,]\d+)?\s*(?:%|процент)', re.I)
def score_block(text, lang='en'):
if lang == 'en':
definition, proper, money, percent = (
EN_DEFINITION, EN_PROPER_NOUN, EN_MONEY, EN_PERCENT)
else:
definition, proper, money, percent = (
RU_DEFINITION, RU_PROPER_NOUN, RU_MONEY, RU_PERCENT)
words = text.split()
if not words:
return {'answer': 0, 'self': 0, 'stats': 0, 'unique': 0, 'total': 0}
# 1. Законченный ответ в начале блока
head = ' '.join(words[:40])
answer = 20 if definition.search(head) else 0
# 2. Самодостаточность
self_contained = 15
if re.match(r'^\s*(?:это|он|она|они|it|they|this)\b', text, re.I):
self_contained -= 8
if len(words) < 20:
self_contained -= 7
self_contained = max(self_contained, 0)
# 3. Плотность фактов
facts = len(money.findall(text)) + len(percent.findall(text))
facts += len(re.findall(r'\b\d{2,}\b', text))
stats = min(facts * 3, 15)
# 4. Конкретика через имена собственные
unique = min(len(set(proper.findall(text))), 5)
total = answer + self_contained + stats + unique
return {'answer': answer, 'self': self_contained,
'stats': stats, 'unique': unique, 'total': total}
Текст для проверки, один смысл в двух версиях:
RU = ("Поведенческие факторы - это сигналы, которые поисковик снимает с поведения "
"посетителей: доля отказов, глубина просмотра, возвраты в выдачу. "
"По нашим замерам на 40 сайтах доля отказов выше 65 процентов роняет позиции "
"в среднем на 4 пункта за 3 месяца. Яндекс учитывает их с 2011 года.")
EN = ("Behavioral factors are a set of signals that a search engine collects from "
"visitor behavior: bounce rate, scroll depth, returns to the results page. "
"Across 40 sites we measured, a bounce rate above 65% drops rankings by "
"4 positions on average over 3 months. Yandex has used them since 2011.")
Результат:
Английская рубрика:
русский ответ 0 самодост. 15 факты 9 имена 0 итого 24 из 55
английский ответ 20 самодост. 15 факты 12 имена 3 итого 50 из 55
Русская рубрика:
русский ответ 20 самодост. 15 факты 12 имена 3 итого 50 из 55
Двадцать четыре против пятидесяти на одном и том же содержании. Разница целиком собрана из трёх мест: определение не распознано, имена собственные не посчитаны, проценты словом не найдены.
Что получилось на реальной выборке
Девять страниц, 98 блоков текста. Коммерческие страницы, статьи блога, одна английская страница для контроля.
Рубрика | Среднее | Топ-5 блоков |
|---|---|---|
Англоязычная, как в скрипте | 31,3 | 36,8 |
Адаптированная под русский | 42,5 | 48,9 |
Разрыв | 11,2 | 12,1 |
Вклад по измерениям, среднее по выборке:
Измерение | Англоязычная | Русская |
|---|---|---|
Ответ в начале блока | 9,8 | 17,2 |
Самодостаточность | 14,1 | 16,4 |
Плотность фактов | 1,2 | 2,9 |
Конкретика | 0,0 | 1,3 |
Самодостаточность почти не поехала: это измерение считает длину и наличие местоимения в начале, оно от языка почти не зависит. Всё остальное поехало сильно.
Распределение оценок по 98 блокам:
Оценка | Англоязычная | Русская |
|---|---|---|
F | 65 | 27 |
D | 26 | 26 |
C | 7 | 42 |
B | 0 | 3 |
A | 0 | 0 |
Шестьдесят пять блоков из девяноста восьми получили F. Это не диагноз тексту, это диагноз инструменту.
Контрольная проверка
Возражение очевидное: может, русская рубрика просто мягче, вот и рисует цифры повыше.
Проверили на английской странице того же сайта. Если русская рубрика мягче в принципе, она и на английском тексте даст больше.
Получилось наоборот. На английской странице англоязычная рубрика дала 36,3, а русская 29,7. Знак перевернулся.
Это и есть доказательство. Каждая рубрика лучше работает на своём языке, и разрыв в 11 пунктов на русском корпусе это артефакт языка, а не подкрутка весов.
Что с этим делать
Не верить абсолютной цифре инструмента на русском тексте. Если инструмент собран на английском корпусе и внутри у него регулярные выражения, его балл на кириллице систематически занижен. Насколько именно, зависит от того, сколько в вашем тексте определений, названий и денег.
Смотреть на относительную динамику, а не на абсолют. Если гоняете один и тот же инструмент по одному и тому же сайту раз в месяц, смещение постоянное и сокращается. Балл вырос с 31 до 35, значит текст стал лучше, даже если сама шкала врёт.
Проверять контролем на другом языке. Если у сайта есть англоязычная версия, прогоните её тем же инструментом. Расхождение между версиями при одинаковом качестве текста и есть величина смещения.
Чинить содержание, а не подгонять под регулярки. Это важнее всего. Обнаружив, что скрипт не видит определения, соблазнительно начать писать «X is a» посреди русского текста. Смысла ноль: цитировать вас будет модель, а не регулярка. Модель русские определения понимает прекрасно.
Реальные проблемы, которые инструмент вскрыл у нас, от языка не зависели: ни на одной странице не было законченного ответа в первых ста словах, средняя секция на главной занимала 10,2 слова, и ни одна не попадала в диапазон, который модель может вынуть целиком. Вот это чинить надо. А низкий балл за отсутствие знака доллара чинить не надо.
Ограничения
Выборка небольшая. Девяносто восемь блоков с девяти страниц одного сайта. Величина смещения на другом корпусе будет другой: у текста с большим числом названий компаний разрыв окажется шире, у текста без цифр уже.
Рубрику мы переписывали сами. Веса и логика измерений сохранены один в один, но набор русских шаблонов для определений собран нами и полным не является. «X подразумевает Y» или «под X понимают Y» в него не вошли, значит русский балл тоже занижен, просто меньше.
Оригинальный скрипт мы не запускали. В системном Python не оказалось нужных библиотек, поэтому функцию скоринга перенесли один в один, а извлечение блоков воспроизвели по той же логике. Расхождение на несколько блоков возможно.
Мы не проверяли, коррелирует ли балл с реальными цитированиями. Ни англоязычный, ни русский. Это отдельная работа, и без доступа к логам ИИ-поисковиков она толком не делается.
Итог
Если вы гоняете русский сайт через инструмент, написанный на английском корпусе, первым делом посмотрите, что у него внутри. Пятнадцать минут чтения регулярных выражений экономят неделю переписывания текстов, с которыми всё в порядке.
И наоборот: проблемы, которые инструмент нашёл и которые от языка не зависят, стоит чинить сразу. Их обычно достаточно.
KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.