Разбор

ИИ-агент строит карту внутренней перелинковки сайта: SEO-возможности которые ты не видишь

Блог с 80 статьями — 34 пары страниц без единой ссылки между собой при высокой тематической связи. ИИ-агент сканирует каждую страницу и за 30 минут строит полную карту: кто на кого должен ссылаться и через какой анкор.

• 7 мин чтения

У контентного блога с 80 статьями было 34 пары страниц с высокой тематической связью и ни одной ссылкой между ними. Не потому что редактор забыл — просто невозможно удержать в голове 6 320 потенциальных комбинаций страниц и понять которые из них стоит связать.

34
пары тематически близких страниц без единой ссылки — нашёл агент за 28 минут на блоге с 80 статьями

Хаотичная перелинковка — это не проблема дизайна

Поисковый робот Яндекса ходит по сайту как по карте метро. Чем больше путей ведёт к станции — тем чаще он туда приезжает и тем выше ценит эту страницу. Это внутренний PageRank: ссылочный вес от главной и популярных страниц перетекает на те, на которые ссылаются.

У большинства сайтов с 100+ страницами эта система работает случайно. Главная ссылается на 8 разделов. Разделы ссылаются на 10–15 статей. Статьи между собой — почти никак. Написал новый материал — поставил ссылку на 2–3 похожих которые вспомнил. Через год у тебя 80 статей и паутина из 200 хаотичных ссылок.

При этом Яндекс смотрит не только на количество ссылок, но и на анкоры. Если статья про «контент-маркетинг для B2B» получает входящую ссылку с анкором «стратегия контента», это усиливает её по этому запросу. Хаотичные перелинковки дают хаотичные анкоры — сигнал нечёткий, позиции ниже.

Как распределяется ссылочный вес на типичном контентном сайте без оптимизации
Главная страница
100%
Разделы / рубрики
~40%
Топ-10 популярных статей
~18%
Остальные 70 статей
~3%
Схема распределения внутреннего PageRank на сайте с 80 статьями без стратегии перелинковки. Цифры условные, иллюстрируют разрыв между топом и хвостом.

70 статей из 80 получают в среднем 3% от веса главной. Не потому что они плохие — просто никто не провёл линии между ними.

Что агент собирает и как решает что связывать

Полный текстовый анализ 80 страниц — это слишком дорого. Claude прочитает каждую и ответит вам счётом за API на 2 000 рублей. Агент работает хирургически: берёт только то что нужно для тематической классификации.

С каждой страницы собирается четыре элемента:

  • <title> — что поисковик и автор считают главной темой страницы
  • <h1> — заголовок статьи
  • meta description — краткое резюме
  • Первые 500 символов текста — вступление, где обычно сформулирована суть

Этого достаточно чтобы Claude понял: страница про retention-маркетинг для e-commerce или про стратегию email-рассылок? Они близки, но не одно и то же. Агент разберётся.

Дальше Claude получает эти мини-профили всех страниц одним батчем и делает три вещи:

Первое — определяет тематические кластеры. Группирует страницы по смысловой близости, не по рубрикам сайта. Часто статьи из разных разделов оказываются тематически ближе, чем соседи по рубрике.

Второе — ищет недостающие связи. Внутри каждого кластера и между смежными кластерами — какие пары существуют без ссылок, хотя читатель после первой страницы логично пошёл бы на вторую.

Третье — генерирует рекомендации. Для каждой пары: страница-источник, страница-цель, предлагаемый анкор, одно предложение почему.

Схема работы агента

🗺️sitemap.xmlсписок всех URL
🐍BeautifulSoupсбор мета + первый абзац
🤖Claude Sonnetтематический анализ + пары
📋CSV-таблица50–100 рекомендаций

Весь пайплайн — локально. Никаких внешних сервисов кроме Claude API. Запустил, подождал 20–30 минут, получил CSV.

Код агента

Устанавливаем зависимости: pip install requests beautifulsoup4 anthropic

link_agent.py — агент перелинковки
import requests
import xml.etree.ElementTree as ET
from bs4 import BeautifulSoup
import anthropic
import json
import csv
import time

SITE_URL = "https://example.com"          # ваш сайт
SITEMAP_URL = f"{SITE_URL}/sitemap.xml"   # путь к sitemap
CLAUDE_API_KEY = "YOUR_CLAUDE_KEY"        # ключ Anthropic API
OUTPUT_CSV = "link_recommendations.csv"  # куда сохранить

MAX_PAGES = 100   # лимит страниц для анализа
BATCH_SIZE = 25   # страниц на один запрос к Claude


def parse_sitemap(sitemap_url: str) -> list[str]:
  """Получаем список URL из sitemap.xml."""
  resp = requests.get(sitemap_url, timeout=15)
  resp.raise_for_status()
  root = ET.fromstring(resp.content)
  ns = {'sm': 'http://www.sitemaps.org/schemas/sitemap/0.9'}
  urls = [loc.text for loc in root.findall('.//sm:loc', ns)]
  # Фильтруем служебные страницы
  skip = ['/tag/', '/category/', '/page/', '/author/', '/feed']
  return [u for u in urls if not any(s in u for s in skip)][:MAX_PAGES]


def scrape_page_meta(url: str) -> dict | None:
  """Собираем заголовок, h1, description и первый абзац."""
  try:
      resp = requests.get(url, timeout=10, headers={
          'User-Agent': 'Mozilla/5.0 (SEO-audit-bot)'
      })
      if resp.status_code != 200:
          return None
      soup = BeautifulSoup(resp.text, 'html.parser')
      title = soup.find('title')
      h1 = soup.find('h1')
      desc = soup.find('meta', attrs={'name': 'description'})
      # Первый непустой абзац из основного контента
      paragraphs = soup.find_all('p')
      first_para = next(
          (p.get_text(strip=True) for p in paragraphs if len(p.get_text(strip=True)) > 80),
          ''
      )
      return {
          'url': url,
          'title': title.get_text(strip=True) if title else '',
          'h1': h1.get_text(strip=True) if h1 else '',
          'description': desc.get('content', '') if desc else '',
          'intro': first_para[:500],
      }
  except Exception:
      return None


def analyze_batch_with_claude(pages: list[dict], client: anthropic.Anthropic) -> list[dict]:
  """Анализируем батч страниц — Claude находит пары для перелинковки."""
  pages_json = json.dumps(pages, ensure_ascii=False, indent=2)
  prompt = f"""Ты SEO-специалист. Проанализируй список страниц сайта и найди пары для внутренней перелинковки.

Страницы для анализа:
{pages_json}

Задача: найти пары (страница А → страница Б) где:
1. Тематическая связь сильная — читатель который прочёл А, вероятно заинтересован в Б
2. Ссылка улучшит пользовательский путь и поможет роботу понять структуру сайта
3. Предлагаемый анкор — конкретные 2-5 слов из темы страницы Б

Верни JSON-массив объектов. Каждый объект:
{{
"source_url": "URL страницы А",
"source_title": "Заголовок страницы А",
"target_url": "URL страницы Б",
"target_title": "Заголовок страницы Б",
"anchor": "предлагаемый анкор",
"reason": "почему эта связь важна (1 предложение)",
"priority": "high|medium|low"
}}

Найди 10-20 пар из этого батча. Возвращай только JSON, без пояснений."""

  message = client.messages.create(
      model="claude-sonnet-4-5",
      max_tokens=4096,
      messages=[{"role": "user", "content": prompt}]
  )
  text = message.content[0].text.strip()
  # Извлекаем JSON если Claude обернул в markdown
  if text.startswith("```"):
      text = text.split("```")[1]
      if text.startswith("json"):
          text = text[4:]
  try:
      return json.loads(text)
  except json.JSONDecodeError:
      return []


def save_to_csv(recommendations: list[dict], output_path: str):
  """Сохраняем рекомендации в CSV для контент-менеджера."""
  if not recommendations:
      print("Рекомендаций нет — нечего сохранять.")
      return
  fields = ['source_url', 'source_title', 'target_url', 'target_title',
            'anchor', 'reason', 'priority']
  with open(output_path, 'w', newline='', encoding='utf-8') as f:
      writer = csv.DictWriter(f, fieldnames=fields)
      writer.writeheader()
      writer.writerows(recommendations)
  print(f"Сохранено {len(recommendations)} рекомендаций → {output_path}")


if __name__ == "__main__":
  client = anthropic.Anthropic(api_key=CLAUDE_API_KEY)

  print("Парсим sitemap...")
  urls = parse_sitemap(SITEMAP_URL)
  print(f"Найдено URL: {len(urls)}")

  print("Собираем мета-данные страниц...")
  pages = []
  for i, url in enumerate(urls):
      meta = scrape_page_meta(url)
      if meta:
          pages.append(meta)
      if (i + 1) % 10 == 0:
          print(f"  {i+1}/{len(urls)} страниц обработано")
      time.sleep(0.3)  # вежливый краулер не спешит
  print(f"Собрано страниц: {len(pages)}")

  print("Анализируем через Claude...")
  all_recommendations = []
  for i in range(0, len(pages), BATCH_SIZE):
      batch = pages[i:i + BATCH_SIZE]
      print(f"  Батч {i//BATCH_SIZE + 1}: страницы {i+1}–{i+len(batch)}")
      recs = analyze_batch_with_claude(batch, client)
      all_recommendations.extend(recs)
      time.sleep(2)  # пауза между запросами к API

  # Сортируем: high priority вверх
  priority_order = {'high': 0, 'medium': 1, 'low': 2}
  all_recommendations.sort(key=lambda x: priority_order.get(x.get('priority', 'low'), 2))

  save_to_csv(all_recommendations, OUTPUT_CSV)
  print(f"Готово. Итого рекомендаций: {len(all_recommendations)}")

Несколько вещей которые нужно знать перед запуском.

Пауза time.sleep(0.3) между запросами к сайту — не для красоты. Некоторые хостинги блокируют краулеры при более чем 2–3 запросах в секунду. Если сайт на shared-хостинге или за Cloudflare — увеличь до 0.8–1.0.

claude-sonnet-4-5 в коде — можно заменить на claude-haiku-3-5 если нужна экономия. Haiku справляется с тематическим анализом текстов примерно так же, стоит в 8 раз дешевле. На 80 страницах разница — около 15 рублей против 2 рублей.

Батч из 25 страниц — это максимум который помещается в разумный контекст без потери качества. Если делать батч 50+ страниц, Claude начинает находить более слабые связи просто чтобы выдать достаточное количество рекомендаций.

Что получается на выходе: реальный пример

Блог с 80 статьями про digital-маркетинг. Агент отработал за 28 минут, отдал 47 рекомендаций. Вот 8 из них с разными уровнями приоритета:

Страница-источникСтраница-цельАнкорПриоритет
Как писать email-цепочки для e-commerceСегментация базы по RFM: пошаговый гайдсегментации базывысокий
Контент-план для B2B: шаблон и примерыКак измерить ROI контент-маркетингаизмерить эффективность контентавысокий
SEO для интернет-магазина: с чего начатьВнутренняя перелинковка категорий каталогаперелинковки категорийвысокий
Как снизить CPA в Яндекс.ДиректРетаргетинг: 7 стратегий для возврата клиентовретаргетинг для снижения CPAвысокий
Автоворонки в мессенджерах: инструкцияЧат-бот для лидогенерации: кейсчат-бота для сбора лидовсредний
UX-копирайтинг: как писать CTAA/B-тестирование заголовков: методологияA/B-тестирование текстов кнопоксредний
Продвижение в Telegram: органический ростКак монетизировать Telegram-каналмонетизации каналасредний
Аналитика для малого бизнеса: базовый стекЯндекс.Метрика: полное руководство 2025настройки Яндекс.Метрикинизкий

34 из 47 рекомендаций прошли ручную проверку — у редактора не было возражений по тематической связи. 9 пар оказались слишком слабыми и были отброшены. 4 — дублировали уже существующие ссылки которые агент не нашёл через парсинг (они были в JavaScript-рендере).

Важный момент: агент не проверяет существующие ссылки на странице. Он анализирует тематику и предлагает связи — но не знает что ссылка уже стоит. Перед внедрением нужно быстро глазами пробежаться по каждой рекомендации. 2–3 минуты на пару.

Проверь свой сайт: квиз

Насколько хаотична перелинковка на вашем сайте?

4 вопроса — поймём нужен ли вам агент

Вопрос 1 из 4
Сколько страниц / статей на вашем сайте?
Вопрос 2 из 4
Когда последний раз специально ставили внутренние ссылки между статьями?
Вопрос 3 из 4
Есть ли у вас статьи которые получают мало трафика, хотя тема хорошая?
Вопрос 4 из 4
Вы знаете какие страницы вашего сайта получают больше всего внутренних ссылок?

Как внедрять: от таблицы к ссылкам

Агент отдал CSV. Дальше — три шага.

1
Ручная проверка приоритетных пар

Открываем рекомендации с пометкой «высокий приоритет» — обычно это 15–20 пар. Для каждой: открываем страницу-источник и проверяем есть ли там уже ссылка на цель. Если нет — смотрим есть ли в тексте абзац где упоминается тема целевой страницы. Это займёт 30–40 минут.

2
Добавляем ссылки по очереди

Контент-менеджер берёт первые 20 пар по приоритету — это один рабочий день. Каждую правку сохраняем. Не нужно добавлять все 50 пар за раз — поисковик увидит резкий прирост внутренних ссылок и это выглядит неестественно. Растягивайте на 3–4 недели.

3
Переобход и проверка через 2–4 недели

Запускаем Screaming Frog или аналог — смотрим что изменилось в распределении внутренних ссылок. Отслеживаем позиции страниц которые получили новые входящие ссылки. Заметный эффект обычно виден через 4–8 недель после индексации.

Одна вещь которую нужно сделать до старта: снять снапшот текущего состояния. Screaming Frog (бесплатно до 500 URL) выгрузит отчёт по внутренним ссылкам. Сохраните его — потом будет с чем сравнивать.

Где агент ошибается

Три типичных промаха из практики.

Агент иногда рекомендует связи между страницами с похожими словами в заголовках, но разным смыслом. «Контент-маркетинг для стартапов» и «Контент для Instagram» — оба про контент, но аудитории и цели разные. Проверка руками отловит такое за секунды.

Агент не знает о пользовательском пути на конкретном сайте. Он анализирует тематику, но не данные о том откуда реально приходят пользователи и куда уходят. Если у вас есть данные Метрики по поведенческим паттернам — дополните промпт этим контекстом.

Агент не учитывает коммерческий контекст. Иногда выгоднее вести с информационной статьи на коммерческую страницу, а не на другую статью — даже если тематически они дальше. Такие пары нужно добавлять вручную.


80 статей, 34 незамеченных связи, 28 минут работы агента. Это не магия — это просто то что человек не может держать в голове, а скрипт может. Контент уже написан, деньги на него потрачены. Осталось провести между ними правильные линии.

Источники

Источники

Часто задаваемые вопросы

Что такое внутренняя перелинковка и зачем её оптимизировать?
Внутренняя перелинковка — это система ссылок между страницами одного сайта. Поисковый робот обходит сайт по этим ссылкам: чем больше ссылок ведёт на страницу, тем выше её внутренний PageRank и тем выше она ранжируется. Без оптимизации ссылочный вес распределяется случайно — важные страницы получают меньше веса, чем второстепенные.
Сколько страниц нужно для того, чтобы перелинковка стала проблемой?
Уже с 50–60 страниц ручной контроль перестаёт быть реалистичным: одному редактору невозможно держать в голове все тематические связи. На практике у большинства контентных сайтов с 100+ страницами 30–40% возможных тематических связей не реализованы — страницы существуют изолированно, хотя должны усиливать друг друга.
Как ИИ-агент определяет, какие страницы должны ссылаться друг на друга?
Агент собирает H1, title, мета-описание и первые 500 символов текста каждой страницы. Claude анализирует тематическую близость: совпадение сущностей, вопросов которые отвечают страницы, и логику читательского пути. Рекомендует пару только если тематическая связь сильная и в тексте страницы-источника есть естественное место для вставки ссылки.
Какой код нужен для запуска агента перелинковки?
Python с тремя библиотеками: requests и BeautifulSoup для сканирования сайта, anthropic для анализа через Claude. Скрипт читает sitemap.xml или обходит страницы рекурсивно, собирает мета-данные и первый абзац, отправляет в Claude батчами по 20–30 страниц. Итого ~150 строк кода. Полный пример — в статье.
Сколько стоит прогнать агент по сайту с 80 страницами?
Один полный прогон по сайту с 80 страницами — около 15–25 рублей через Claude API (claude-sonnet-4-5 или claude-haiku-3-5 для экономии). Если запускать раз в квартал при обновлении контента — расходы практически нулевые. Для сравнения: ручной SEO-аудит перелинковки в агентстве стоит 20 000–50 000 рублей.
Как внедрять рекомендации агента по перелинковке?
Агент выдаёт таблицу: страница-источник, страница-цель, предлагаемый анкор, обоснование. Контент-менеджер открывает страницу-источник, находит абзац где упоминается тема целевой страницы, добавляет ссылку. Обычно правка занимает 2–3 минуты на пару. 50 пар — это полный рабочий день. После — переобход краулером чтобы убедиться что все ссылки проставлены.
Обсуждение

    Пока без комментариев. Будьте первым.

    Войдите, чтобы отправить комментарий

    Вы сможете комментировать статьи, сохранять материалы

    или войдите по email

    Бесплатная диагностика · 30 минут · без обязательств

    Маркетинг работает, но продажи не растут?

    Отвечу на 3–5 вопросов о вашем бизнесе — и мы вместе разберём, где именно теряются клиенты и что с этим делать.

    Без продаж. Без навязчивых звонков.