Как узнать сайт за google.com/goto в AI Overviews без перехода — и почему парсер выдаёт за источник Google

Я снимал AI Overviews через API выдачи, и вместо адресов сайтов источники приходили ссылками google.com/goto?url=CAES… На 1 022 ответах с обзором я посчитал, сколько доменов узнаётся без перехода. При прямых ссылках в органике (обычной выдаче под обзором) узнаётся половина. Но главным источником в первом отчёте оказался сам Google.
Что лежит внутри ссылки google.com/goto и почему её нельзя раскодировать
Ссылки в выдаче Google идут через редирект, то есть через промежуточный адрес. Google подтвердил это 26 августа 2026. Источники ИИ-обзоров Google в моих логах приходят такими же ссылками. Обёртку base64url с параметра url снять легко. Открытым текстом адреса под ней нет: там шифр.
import base64
import math
from collections import Counter
from urllib.parse import urlparse, parse_qs
def goto_payload(link):
token = parse_qs(urlparse(link).query)["url"][0]
raw = base64.urlsafe_b64decode(token + "=" * (-len(token) % 4))
size, shift, i = 0, 0, 3
while True: # длина поля 2 — varint
size |= (raw[i] & 0x7F) << shift
shift += 7
i += 1
if raw[i - 1] < 0x80:
break
return raw[:3].hex(" "), raw[i:i + size]
def entropy(data):
n = len(data)
return -sum(c / n * math.log2(c / n) for c in Counter(data).values())
Я разобрал 2 000 токенов из ссылок в моих логах. У всех первые три байта одинаковы: 08 01 12. Это protobuf, бинарный формат сообщений. Префикс CAES, с которого начинается каждый токен, и есть эти три байта в base64.
Дальше лежит байтовая строка длиной от 72 до 616 байт. Перед ней записана её длина в формате varint, функция читает этот счётчик и отрезает строку ровно по нему.
Энтропия показывает, насколько байты похожи на случайные. На одном коротком токене она выходит заниженной. Поэтому я склеил строки первых 500 токенов из этих 2 000. Вышло около 64 тыс. байт. Функция entropy на этой склейке дала 7,96 бит на байт. Потолок здесь 8 бит.
Такая энтропия бывает и у сжатых данных, поэтому я проверил второй признак. Печатных символов ASCII в строке 37,7%. У случайных байтов их столько же, 37,1%. Открытым текстом адреса внутри нет.
SerpApi пишет, что адрес лежит внутри ссылки шифром, упакованным в protobuf. Про ключ у них ничего нет. Мой осторожный вывод: расшифровать адрес может только Google.
Ходить по редиректу тоже не выйдет. В robots.txt Google путь /goto закрыт для всех роботов, массовые переходы ловят капчу. В 1 022 ответах было 5 640 ссылок на источники, и на каждую пришлось бы отправить отдельный запрос к Google.
Как достать домен источника, сверив заголовок с органикой того же ответа
API выдачи отдаёт блок Google AI Overview и органику одним JSON. У источников обзора адреса зашифрованы. У органики ссылки обычно прямые, и заголовок источника часто находится среди её заголовков.
Google дописывает к заголовку источника хвост «Страница откроется в новой вкладке» для экранного диктора. Его я срезаю и сравниваю первые 35 символов. Не нашлось — ищу домен в самом заголовке: некоторые сайты пишут его в title, например «… - Lifel.ru».
import re
TAIL = re.compile(r"\.\s*Страница откроется в новой вкладке\.?\s*$")
DOMAIN_IN_TITLE = re.compile(r"\b([a-z0-9][a-z0-9-]{1,60}\.(?:ru|com|org|net|io|рф))\b", re.I)
def resolve_domain(ref_title, organic):
title = TAIL.sub("", ref_title or "").strip().lower()
if not title:
return None
for item in organic:
if "/goto?" in item["url"]:
continue # у такой органики домен будет google.com
org_title = item["title"].strip().lower()
if org_title and (org_title[:35] in title or title[:35] in org_title):
return item["domain"]
m = DOMAIN_IN_TITLE.search(title)
return m.group(1) if m else None
Проверял на заголовке «Прозвища футбольных клубов России - Блоги Sports.ru». В органике первая ссылка шла через /goto, вторая была прямой. Функция пропустила первую и вернула sports.ru.

На 646 ответах с прямой органикой опознано 49,3% источников. Сверка дала 47,8%, домен из заголовка добавил ещё 1,5%. От ниши к нише доля скачет от 31% до 90%. Опознанные стоят в органике по всей первой странице почти равномерно, от первой позиции до девятой. Если источника нет среди первых десяти результатов того же ответа, сверка его не найдёт.
Почему при органике через /goto парсер записывает источником сам google.com
В первой версии кода строки с continue не было, и домен органики брался из её URL. Если органика тоже приходит через /goto, её домен Google. Заголовки совпадают, и источником записывается Google. Ошибка тихая: поле домена заполнено, и ничего не падает.
Первый счётчик шёл по всем записям, вместе с повторными разборами тех же ответов. Он опознал 2 938 ссылок из 6 048, это 48,6%. У 1 014 опознанных доменом стоял Google. На втором месте была Википедия с 47.
Ответов, где органика шла через /goto, набралось 376. В этих ответах 45,0% источников получили ложный Google. Настоящий домен нашёлся только у 2,4%. В 290 ответах из 376 не опознан ни один настоящий домен.

По всей выборке, вместе с такими ответами, настоящий домен у 30,5% источников. Ещё 18,0% занял ложный Google. Почти все ответы с органикой через /goto пришлись на несколько дней подряд, в остальное время это единичные случаи. Поведение выдачи меняется, так что проверка живёт в коде.
Как собирать домены источников обзора, чтобы в отчёт не попал Google
Сверять заголовок источника с органикой того же ответа, запасным ходом брать домен из заголовка.
Пропускать органику со ссылкой /goto.
Хранить рядом с долей опознанных число всех источников и число неопознанных.
Не раскрывать ссылки переходами.
Проверять, что среди опознанных нет Google.
Google среди доменов источников теперь для меня сигнал проверить код.
Чего не скажут доли опознанных доменов: вопросы были на русском, заголовки неопознанных не сохранились
Почему неопознанные источники не совпали с органикой, по логам не сказать: их заголовки я не сохранял. Вопросы были на русском, регион в настройках стоял Россия, на других языках доли могут выйти другими. Сверка по 35 символам изредка может склеить два похожих заголовка.