ax.SEO
🐱 🦊 🐼 🦝 🐰 🦉
Кто сегодня с нами? 🐾

Животные меняются при загрузке страницы

Главная / Блог / Анализ конкурентных ссылок через Ahrefs API и Python: пошаговый скрипт

Анализ конкурентных ссылок через Ahrefs API и Python: пошаговый скрипт

Скрипт на Python, который собирает ссылочный профиль конкурентов через Ahrefs API, сохраняет в CSV/SQLite и анализирует DR/UR, типы ссылок и анкоры.
🐱
Читать проще с подсказками

Карточки, чек-листы, таблицы и примеры помогают быстро найти нужный ответ.

✅ чек-листы 📈 SEO-практика ⚡ быстро

В 2026 году конкурентный анализ ссылок остаётся одним из ключевых инструментов для оценки позиций и построения стратегии. В этой статье показан практический скрипт, который автоматически собирает данные о ссылочном профиле конкурентов через Ahrefs API, сохраняет их в удобном формате и проводит первичный анализ качества.

1. Получить API‑ключ Ahrefs и подготовить окружение. 2. Сформировать список конкурентов и их доменов. 3. Запросить ссылочный профиль через API. 4. Сохранить данные в CSV/SQLite. 5. Оценить метрики DR/UR, типы ссылок и анкор. 6. Применить чек‑лист качества. 7. Сформировать рекомендации и план действий.

1. Зачем анализировать конкурентные ссылки?

Конкурентный анализ ссылок – это системный разбор внешних ссылок, которые привлекают трафик и авторитет к сайтам соперников. Он позволяет понять, какие страницы и домены приносят авторитет, какие типы ссылок (текстовые, медиа, баннерные) работают лучше, а также выявить потенциальные источники новых качественных ссылок.

Когда нужен такой анализ? В любой момент, когда необходимо:

  • Запустить новый продукт или сервис и быстро войти в топ.
  • Провести ребрендинг и убедиться, что старые ссылки не навредят новому домену.
  • Отреагировать на санкции поисковых систем и восстановить потерянные позиции.
  • Определить, какие конкуренты растут быстрее, и перенять их стратегии.

Цели и выгоды:

  • Выявление «потенциальных» ссылочных ниш – страниц, которые конкуренты уже привлекают, но которые можно использовать и для себя.
  • Определение слабых мест в ссылочном профиле конкурентов, которые можно эксплуатировать, например, низкая анкор‑диверсификация.
  • Получение точных метрик (доменный рейтинг, Page Authority, количество ссылок) для сравнения и построения KPI.
  • Снижение риска потери позиций: если конкуренты получают новые ссылки, можно быстро реагировать и не отставать.
  • Оптимизация бюджета на контент‑маркетинг – фокус на тех типах ссылок, которые реально работают.

Таким образом, конкурентный анализ ссылок превращается из «просто» в инструмент, который позволяет принимать обоснованные решения о продвижении, экономить ресурсы и ускорять рост органического трафика.

2. Подготовка окружения и доступа к Ahrefs API

Получить ключ Ahrefs API: зайдите в личный кабинет, откройте раздел API, скопируйте токен. Храните его в безопасном месте, например, в файле .env. Установите Python 3.10+ и pip. Установите библиотеки requests и python-dotenv. После установки создайте файл .env в корне проекта и добавьте строку API_KEY=ваш_ключ. В коде импортируйте dotenv и получите переменную через os.getenv.

  • API ключ в .env
  • Python 3.10+ установлен
  • pip, requests, python-dotenv установлены
  • Файл .env в корне проекта
  • Переменная API_KEY доступна в коде
  1. pip install requests python-dotenv
  2. echo "API_KEY=xxxxxxxx" > .env
  3. В коде: from dotenv import load_dotenv; load_dotenv(); key = os.getenv("API_KEY")

3. Сбор списка конкурентов и их доменов

  1. Сформируйте список ключевых запросов, которые отражают вашу нишу.
  2. Вызовите Ahrefs API keywords_to_sites для каждого запроса, получив список сайтов, ранжирующих по ним.
  3. Извлеките из ответа поле domain и добавьте его в общую коллекцию.
  4. Убедитесь, что коллекция уникальна – удалите дубликаты.
  5. Сохраните результат в CSV или JSON, чтобы использовать в следующих шагах.
import requests, json

API_KEY = "YOUR_AHREFS_API_KEY"
KEYWORDS = ["example keyword 1", "example keyword 2"]
domains = set()

for kw in KEYWORDS:
    url = f"https://apiv2.ahrefs.com/keywords_to_sites?token={API_KEY}&keyword={kw}&limit=100"
    resp = requests.get(url)
    if resp.status_code != 200:
        continue
    data = resp.json()
    for site in data.get("results", []):
        domains.add(site["domain"])

with open("competitor_domains.json", "w") as f:
    json.dump(list(domains), f, indent=2)

4. Запрос ссылочного профиля через Ahrefs API

Для получения ссылочного профиля Ahrefs API нужно сформировать запрос к https://apiv2.ahrefs.com с параметрами target, from и limit. Пагинация реализуется через from – смещение, а limit – размер страницы (максимум 200). В каждом ответе указано next_from, которое можно использовать для следующего шага. Аутентификация – токен в заголовке Authorization: Bearer YOUR_TOKEN или в параметре token.

import os
import requests
import time

API_URL = "https://apiv2.ahrefs.com"
TOKEN = os.getenv("AHREFS_TOKEN")  # Установите в окружении

def fetch_link_profile(domain, limit=200, pause=1.0):
    """Возвращает список всех ссылок на domain."""
    results = []
    params = {
        "token": TOKEN,
        "target": domain,
        "from": 0,
        "limit": limit,
        "mode": "backlinks",
        "output": "json"
    }

    while True:
        resp = requests.get(API_URL, params=params)
        if resp.status_code != 200:
            raise RuntimeError(f"Error {resp.status_code}: {resp.text}")

        data = resp.json()
        backlinks = data.get("backlinks", [])
        results.extend(backlinks)

        # Пагинация
        next_from = data.get("next_from")
        if not next_from:
            break

        params["from"] = next_from
        time.sleep(pause)  # соблюдаем лимиты API

    return results

if __name__ == "__main__":
    domain = "example.com"
    links = fetch_link_profile(domain)
    print(f"Получено {len(links)} ссылок для {domain}")

5. Парсинг и хранение данных в CSV/SQLite

Парсинг JSON из Ahrefs API, сохранение в CSV и импорт в SQLite – это два простых шага, которые позволяют быстро превратить сырые данные в удобный формат для анализа и дальнейшего использования в отчётах.

import json
import csv
import sqlite3
import requests
from pathlib import Path

API_KEY = 'YOUR_AHREFS_API_KEY'
BASE_URL = 'https://apiv2.ahrefs.com'
DOMAIN = 'example.com'
OUTPUT_DIR = Path('output')
OUTPUT_DIR.mkdir(exist_ok=True)

def fetch_links(domain: str) -> dict:
    """Получаем JSON с внешними ссылками."""
    params = {
        'token': API_KEY,
        'from': 'backlinks',
        'target': domain,
        'mode': 'domain',
        'limit': 1000,
        'output': 'json'
    }
    resp = requests.get(BASE_URL, params=params)
    resp.raise_for_status()
    return resp.json()

def save_to_csv(data: dict, file_path: Path) -> None:
    """Сохраняем массив ссылок в CSV."""
    backlinks = data.get('backlinks', [])
    if not backlinks:
        return
    fieldnames = backlinks[0].keys()
    with file_path.open('w', newline='', encoding='utf-8') as f:
        writer = csv.DictWriter(f, fieldnames=fieldnames)
        writer.writeheader()
        writer.writerows(backlinks)

def init_sqlite(db_path: Path) -> sqlite3.Connection:
    """Создаём базу и таблицу, если их нет."""
    conn = sqlite3.connect(db_path)
    conn.execute('''
        CREATE TABLE IF NOT EXISTS backlinks (
            id INTEGER PRIMARY KEY,
            source_url TEXT,
            target_url TEXT,
            anchor TEXT,
            anchor_text TEXT,
            anchor_type TEXT,
            ref_subdomain TEXT,
            ref_domain TEXT,
            ref_page TEXT,
            ref_text TEXT,
            ref_text_length INTEGER,
            ref_text_type TEXT,
            ref_text_anchor TEXT,
            ref_text_anchor_type TEXT,
            ref_text_anchor_text TEXT,
            ref_text_anchor_type_text TEXT,
            ref_text_anchor_type_text_type TEXT,
            ref_text_anchor_type_text_type_text TEXT,
            ref_text_anchor_type_text_type_text_type TEXT,
            ref_text_anchor_type_text_type_text_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type TEXT,
            ref_text_anchor_type_text_type_text_type_type_text_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type_type
        );
    ''')
    conn.commit()
    return conn

def insert_backlinks(conn: sqlite3.Connection, backlinks: list[dict]) -> None:
    """Вставляем данные в SQLite."""
    cur = conn.cursor()
    cur.executemany('''
        INSERT INTO backlinks (
            source_url, target_url, anchor, anchor_text, anchor_type,
            ref_subdomain, ref_domain, ref_page, ref_text, ref_text_length,
            ref_text_type, ref_text_anchor, ref_text_anchor_type,
            ref_text_anchor_text, ref_text_anchor_type_text,
            ref_text_anchor_type_text_type, ref_text_anchor_type_text_type_type
        ) VALUES (
            :source_url, :target_url, :anchor, :anchor_text, :anchor_type,
            :ref_subdomain, :ref_domain, :ref_page, :ref_text, :ref_text_length,
            :ref_text_type, :ref_text_anchor, :ref_text_anchor_type,
            :ref_text_anchor_text, :ref_text_anchor_type_text,
            :ref_text_anchor_type_text_type, :ref_text_anchor_type_text_type_type
        )
    ''', backlinks)
    conn.commit()

def main():
    data = fetch_links(DOMAIN)
    csv_path = OUTPUT_DIR / f'{DOMAIN}_backlinks.csv'
    save_to_csv(data, csv_path)

    db_path = OUTPUT_DIR / f'{DOMAIN}_backlinks.db'
    conn = init_sqlite(db_path)
    insert_backlinks(conn, data.get('backlinks', []))
    conn.close()
    print(f'CSV и SQLite сохранены в {OUTPUT_DIR}')

if __name__ == '__main__':
    main()

6. Анализ качества ссылок: метрики и типы

При оценке внешних связей важно смотреть не только на их количество, но и на качество. Ключевыми метриками являются трафик, Domain Rating (DR) и URL Rating (UR). Трафик показывает, сколько посетителей пришло по ссылке, DR отражает авторитет домена-источника, а UR указывает на силу конкретной страницы. Чем выше эти показатели, тем более ценна ссылка.

Далее стоит различать типы ссылок. Follow передают ссылочный вес и влияют на ранжирование, тогда как nofollow не передают вес, но могут приносить прямой трафик и улучшать видимость в поиске. В большинстве случаев полезно держать баланс: большинство ссылок – follow, но не забывать о nofollow для спам-источников и контекстных рекламных площадок.

Anchor‑текст – это видимый текст ссылки. Он должен быть релевантным, разнообразным и не переоптимизированным. Рекомендуется использовать комбинацию брендовых, точных, частичных и естественных фраз. Переизбыток точных ключевых слов может вызвать подозрение у поисковых систем, тогда как полностью случайный текст выглядит спамно. Анализ anchor‑текста помогает выявить чрезмерную оптимизацию и корректировать стратегию линкбилдинга.

ПараметрЧто смотреть
ТрафикОбщее количество органических посетителей, приходящих с внешнего источника.
Domain Rating (DR)Авторитет домена‑источника (0–100). Чем выше, тем сильнее ссылка.
URL Rating (UR)Сила конкретной страницы (0–100). Влияет на вес ссылки.
Тип ссылкиFollow – передаёт вес; nofollow – не передаёт, но приносит трафик.
Anchor‑текстРелевантность, разнообразие, отсутствие переоптимизации.

7. Фильтрация и чек‑лист качества ссылок

Качество обратной ссылки измеряется не только её количеством, но и набором параметров, которые влияют на SEO‑ценность. Важнейшие критерии включают: релевантность контента, авторитетность домена, тип ссылки, естественность анкор‑текста, наличие индексации и отсутствие штрафов. Фильтры, основанные на Ahrefs API, позволяют быстро исключить «плохие» ссылки и сосредоточиться на тех, которые действительно повышают рейтинг.

  • Domain Rating (DR) ≥ 30 – фильтр: `dr >= 30`
  • URL Rating (UR) ≥ 20 – фильтр: `ur >= 20`
  • Релевантность тематики – проверка ключевых слов в `title` и `meta description`
  • Анкор‑текст: не более 30% «exact match», > 70% естественный или брендовый
  • Тип ссылки: nofollow только в случае партнёрских площадок, иначе follow
  • Наличие индексации: `status_code == 200` и `is_indexed == true`
  • Отсутствие санкций: `penalty == 0` и `spam_score
  • Источники: исключить «link farms» – `referring_domain_category != "Link Farm"`
  • Трафик рефералов: > 100 посетителей в месяц – фильтр: `referring_domain_traffic >= 100`
  • Период обновления: ссылки актуальны ≤ 12 мес – фильтр: `last_update
  • Проверка на «псевдо‑рекламные» площадки: `site_type != "Ad Network"`
  • Проверка на дублирование: `unique_anchor == true`

8. Выводы и рекомендации по ссылочной стратегии

Подход к ссылочному анализуПреимуществаНедостаткиОптимальные рекомендации
Классический ручной аудит Тщательный контроль контента, точная оценка качества ссылок Высокая трудоемкость, низкая масштабируемость, задержка в принятии решений Использовать как проверочный этап после автоматической обработки, фокусироваться на критичных доменах
Автоматический анализ через Ahrefs API + Python Быстрый сбор данных, возможность обработки тысяч доменов, точная метрика DR/UR Зависимость от API‑лимитов, риск недостоверности данных без ручной проверки, сложность настройки Настроить лимиты, добавить фильтры по anchor‑text и трафику, периодически сверять с ручным аудиторским чек‑листом
Гибридный вариант (Python + ручная валидация) Комбинация скорости и точности, гибкость в выборе критериев Сложность реализации, необходимость поддерживать два потока данных Разработать модульный скрипт, который выводит только «потенциально ценные» ссылки, а остальные проверяются вручную
  1. Сформировать список целевых доменов: 50–100 наиболее релевантных по тематике и DR.
  2. Запустить скрипт Ahrefs+Python, экспортировать JSON‑файл с полями: домен, DR, UR, anchor‑text, трафик.
  3. Отфильтровать результаты по порогу DR>60 и трафика >500.
  4. Создать CSV‑таблицу для ручной проверки: проверить наличие nofollow, релевантность контента, наличие спама.
  5. На основе проверенных ссылок сформировать outreach‑план: персонализированные письма, список контактов, сроки.
  6. Запустить outreach, отслеживать отклики через CRM, обновлять статус в таблице.
  7. Через 2 недели проанализировать изменения DR/UR, обновить скрипт с новыми фильтрами.

9. Мониторинг и обновление скрипта

После запуска скрипта ключевой задачей становится постоянный контроль над его работой. Основные показатели: сколько запросов к Ahrefs сделано, сколько ошибок возникло, насколько свежи данные в базе, и как быстро они обновляются. Важно не просто запустить cron, но и знать, когда он падает, когда лимит API достигается и как быстро появляются новые ссылки. Весь мониторинг можно собрать в одном месте – лог‑файлы, метрики и уведомления.

  • Проверка лимитов Ahrefs: GET https://apiv2.ahrefs.com/api/v2/limits?token=YOUR_TOKEN – выводит remaining_requests и reset_time.
  • Логи cron: убедитесь, что каждая задача завершает работу без ошибок (строка CRON_SUCCESS=true).
  • Скорость обновления: сравните timestamp последнего обновления в базе с текущим временем.
  • Количество новых ссылок: если прирост меньше 1 % от общего объёма, возможно, нужно пересмотреть диапазон дат.
  • Память и размер БД: следите за ростом файлов links.db и log.txt.
  • Уведомления: настройте e‑mail (или Slack) при превышении 80 % лимита API или при ошибке 5xx.
  • Периодическое тестирование: раз в неделю запускайте скрипт вручную, проверяя, что он не дублирует данные.
# /etc/cron.d/ahrefs_links
# Run every 12 hours
0 */12 * * *   www-data   /usr/bin/python3 /home/www/ahrefs_links.py >> /var/log/ahrefs_links.log 2>&1

10. Частые ошибки и риски при работе с Ahrefs API

При работе с Ahrefs API часто встречаются три критические ошибки: переполнение лимитов, неправильный парсинг и неправильное хранение данных.

  • Переполнение лимитов
    Последствия: API‑ответы прерываются, запросы отклоняются, данные обрываются, что приводит к неполным метрикам и ошибочным выводам.
    Как избежать: Считайте rate_limit в каждом ответе, реализуйте экспоненциальный back‑off, ставьте таймауты и ограничивайте частоту запросов до 1‑2 в секунду.
  • Неправильный парсинг
    Последствия: Неверные ключи, пропущенные поля, смешивание форматов JSON/XML приводит к потере информации и некорректным аналитическим выводам.
    Как избежать: Используйте типизированные модели (pydantic, dataclasses), валидируйте схемы через jsonschema, логируйте ошибки парсинга и проверяйте sample‑ответы.
  • Неправильное хранение
    Последствия:ем> Данные теряются при сбоях, нарушается согласованность, возможны дубли, что усложняет последующий анализ и ухудшает производительность.
    Как избежать: Сохраняйте raw JSON в объектный хранилище (S3, GCS), используйте транзакционные БД (PostgreSQL, ClickHouse) с уникальными ключами, применяйте idempotent‑пакеты и проверяйте checksum.

Вопросы и ответы

Как получить API‑ключ Ahrefs?

Для получения ключа необходимо зарегистрироваться на сайте Ahrefs, перейти в раздел API, выбрать тарифный план, подтвердить оплату и скопировать ключ из личного кабинета. Ключ хранится в секрете и используется в заголовке запроса.

Можно ли использовать скрипт для большого количества конкурентов?

Да, скрипт можно применить к множеству сайтов, однако Ahrefs ограничивает количество запросов в секунду и в сутки. При работе с большим списком конкурентов стоит реализовать паузы и проверять статус кода 429, чтобы избежать блокировки.

Что такое Ahrefs API?

Ahrefs API – это набор REST‑эндпоинтов, позволяющих получать данные о ссылочной структуре, органическом поиске, контенте и т.д. API возвращает JSON, который можно парсить в Python для дальнейшего анализа.

Как настроить окружение для работы со скриптом?

Установите Python 3.8+, затем pip install requests pandas. Создайте файл .env с переменной API_KEY, импортируйте её в скрипт через os.getenv. Это позволит безопасно хранить ключ и использовать его в запросах.

Как обрабатывать ошибки при работе с API?

Проверяйте статус код ответа: 200 – всё ок, 403 – недоступен ключ, 429 – превышен лимит. В случае ошибки выводите сообщение и при необходимости делайте паузу перед повторной попыткой. Логи помогают отслеживать сбои.

Как сохранить результаты анализа?

После получения JSON‑ответа преобразуйте его в DataFrame pandas, затем используйте df.to_csv('output.csv', index=False) или df.to_json('output.json', orient='records'). Это упрощает дальнейший экспорт в BI‑системы.

Как фильтровать ссылки по домену?

В ответе API содержится поле domain, которое можно использовать для фильтрации. В pandas примените df[df['domain'] == 'example.com'] чтобы оставить только ссылки, относящиеся к нужному домену.

Как интерпретировать показатель Domain Rating?

Domain Rating – это логарифмическая шкала от 0 до 100, отражающая силу ссылочного профиля. Чем выше значение, тем более авторитетен сайт. Для сравнения конкурентов смотрите разницу в 5–10 точек как значимую.

Как обновлять данные по времени?

Ahrefs предоставляет поле last_update. Сохраняйте его и при следующем запуске скрипта сравнивайте с текущей датой. Если разница меньше 7 дней, можно пропустить запрос, чтобы экономить лимит.

Как избежать блокировки аккаунта при частых запросах?

Соблюдайте лимиты Ahrefs: не превышайте 10 запросов в секунду и 2000 в сутки. Добавляйте случайные паузы, используйте заголовок User-Agent, и если получаете 429, ждите 60 секунд перед продолжением.

Важно

Материал носит информационный характер. Перед внедрением рекомендаций учитывайте нишу, регион, конкурентов, текущее состояние сайта и бизнес-цели проекта.

Редакционная проверка

Материал подготовлен и проверен редакцией AX.SEO

Проверено
AX
Автор Редакция AX.SEO
Digital-редактор 7 лет опыта

Редакция AX.SEO готовит материалы о SEO, разработке, AI, аналитике, маркетинге и росте digital-проектов.

Проверил Александр SEO
SEO-специалист 10 лет опыта

Проверяет практическую применимость рекомендаций, корректность терминов и соответствие материала digital-тематике.

AX.SEO объясняет digital простым языком: без магии, пустых обещаний и “секретных кнопок роста”.