Перейти к содержимому

Данные о бэклинках в формате JSON: $100 бесплатных кредитов, без карты, вход через Google.

SEOMasters.dev
SEOMasters.devЗарегистрироваться

Прокручивайте URL backlink checker без пропусков и зацикливаний

Строки на вызов, страницы на запрос и жесткий потолок в 50 000. Вот цикл, который учитывает все три параметра и сообщает, когда нужно остановиться.

Бесконечные каменные арочные мосты через туманную реку без пропусков
Три числа, которые нужно запомнить

За один вызов возвращается до 1 000 строк. Каждый вызов оплачивается один раз, независимо от количества строк. Глубокая пагинация ограничена 50 000 строками, что составляет 50 вызовов по 1 000 строк.

Почему сортировка идет перед пагинацией

Пагинация по несортированному списку — причина появления дубликатов. Всегда передавайте sort и order вместе с page и per, чтобы страница 3 означала одно и то же при каждом запросе в рамках запуска.

Запрос refdomains стоит 2 кредита за вызов, поэтому 50 вызовов обойдутся в 100 кредитов, или один цент. Даже в худшем случае это дешево; настоящая проблема — цикл, который не завершается.

Выбор столбца для сортировки

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

Сортировка по полю, которое не имеет отношения к вашему вопросу, может отсечь нужные строки. Сначала вытяните по десять строк для каждого кандидата на сортировку и посмотрите, какой фрагмент останется после применения лимита.

Запишите выбор в отчет, чтобы следующий человек понял, почему отсечение произошло именно так.

Записи, которые меняются во время запуска

Данные не меняются во время запуска, если только ежемесячная перестройка индекса не попадет в его середину. Редко, но возможно. Ночная задача, которая захватывает начало месяца, может вернуть одну страницу из старого индекса, а следующую — из нового, поэтому фиксируйте время начала в логе и сравнивайте.

Записывайте время начала вверху лога и номер страницы в каждой строке. Тогда несовпадение в выводе укажет на проблему времени, а не на ошибку парсинга.

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

Запуск, охватывающий перестройку, не является ошибкой по умолчанию. Он просто дает несогласованные данные, поэтому отмечайте такие запуски в логе и перепускайте затронутый фрагмент, если это важно для отчета.

Цикл пагинации, который завершается

TypeScript для воркера на Node 20. Он останавливается при первой неполной странице, при достижении лимита или при любом ok, который не равен true.

ts
const BASE = "https://seomasters.dev/api.php";
const CAP = 50_000;
const PER = 1000;

export async function pullAll(domain: string, key: string) {
  const rows: unknown[] = [];
  for (let page = 1; rows.length < CAP; page++) {
    const u = new URL(BASE);
    u.search = new URLSearchParams({
      action: "refdomains", q: domain,
      page: String(page), per: String(PER),
      sort: "dr", order: "desc",
    }).toString();
    const res = await fetch(u, { headers: { Authorization: `Bearer ${key}` } });
    if (res.status === 401 || res.status === 402) throw new Error(`stop: HTTP ${res.status}`);
    const body = await res.json();
    if (!body.ok) throw new Error(JSON.stringify(body).slice(0, 300));
    const chunk = findRows(body);   // see note below
    rows.push(...chunk);
    if (chunk.length < PER) break;  // short page means we are done
  }
  return rows;
}

Отладка запуска пагинации

Что вы видитеВероятная причинаЧто делать
Страница 51 возвращается пустой или с ошибкойВы достигли лимита в 50 000Узкий запрос с другим действием или другой sort, затем слияние по id
Один и тот же домен на двух страницахЗапрос без сортировки или перестройка индекса во время запускаПередайте sort и order; удалите дубликаты по ключу домена
Строк меньше, чем общий итог на дашбордеИтог относится ко всему графу, а запрос ограничен лимитомПокажите «топ 50 000» в интерфейсе
Цикл никогда не заканчиваетсяПроверка условия учитывает только rows.lengthПрерывание на неполной странице, как описано выше
Счет выше ожидаемогоПовторная попытка при любом коде, отличном от 200Повторная попытка только при сетевых ошибках, см. ошибки
Садовник осматривает уникальные саженцы в солнечной теплице

Чтение строк без догадок о именах полей

Хелпер findRows выше намеренно оставлен открытым. Сделайте один реальный вызов, посмотрите на JSON и напишите аксессор на основе того, что вы видите, а не того, что обещано в туториале.

Для более дешевой версии этой задачи — одновременного скора множества доменов без пагинации — прочитайте скрипт bulk DR.

Заморозьте один ответ как фикстуру

Затем проверьте его с помощью теста. Сохраните один ответ как файл-фикстуру и проверьте количество строк и ключи первой строки, чтобы будущие изменения ломали CI, а не графики у клиентов.

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

Когда API добавит новое поле позже, разница в фикстуре покажет, заметил ли ваш аксессор это или проигнорировал молча.

Называйте фикстуру в соответствии с запросом, из которого она получена, например, действием, доменом и сортировкой. Файл response1.json ничего не говорит следующему читателю о том, что он доказывает.

Посчитайте сохраненные строки

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

Запускайте эту проверку после каждого тестового запроса, а не только после первого. Расхождение в счете обычно — первый признак изменения сортировки или перестройки индекса во время запуска.

Храните счет в той же строке лога, что и номер страницы, чтобы их можно было считать вместе одним взглядом.

Разделение работы выше потолка

Некоторые задачи превышают лимит. Для списков выше потолка разбивайте задачу, а не усложняйте пагинацию. Вытяните топ 50 000 по одной сортировке, затем задайте другие действия для самых активных доменов, так как они тоже стоят 2 кредита и отвечают на другие вопросы. Усложнение пагинации никогда не позволит обойти отсечение.

Вытяните топ 50 000 по одной сортировке

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

Дашборд, который показывает ограниченный счет как полный итог, вводит в заблуждение читателей. Метка ничего не стоит, и отчет URL backlink checker с ней выглядит честнее.

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

Задайте самым активным доменам другой вопрос

anchors и toppages отвечают на другие вопросы, например, какой анкорный текст доминирует или какие страницы привлекают ссылки. Для домена, прошедшего лимит, эти ответы могут быть важнее следующей страницы того же списка.

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

Каждое дополнительное действие стоит своих кредитов, поэтому оцените стоимость до запуска, а не после прихода счета.

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

Слияние по ключу домена

Когда два запроса пересекаются, сливайте их по ключу домена и сохраняйте более новую метку времени. Слияние тогда не зависит от того, какой запрос завершился первым.

Логируйте, какой запрос выиграл для каждой строки. Если число меняется между двумя отчетами, этот лог покажет, изменился ли индекс или слияние.

Храните слияние в одной функции с тестом, так как это единственное место, где встречаются два запроса.

Возобновление после сбоя

Сбои случаются. Деплой перезапускает воркер, ноутбук уходит в сон, VPN отключается, и цикл умирает на странице 31 из 50.

Сохраняйте страницу после каждого вызова

Сохраняйте номер последней завершенной страницы после каждого вызова. Перезапускайтесь с этого номера плюс один, а не с начала, и вы платите за 2 кредита за страницу, а не перепокупаете весь запрос.

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

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

Логируйте время, которое занимает сохранение, а также время каждого вызова. Сохранение, сопоставимое по времени с вызовом, указывает на слой хранения, и это стоит исправить до расширения цикла.

Апсерт по ключу домена

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

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

Скрипт починки часто пишется в спешке после неудачного запуска. Именно тогда проскакивает второй ключ, поэтому проверяйте его так же тщательно, как основной цикл.

Отклоняйте возобновление, если изменилась сортировка

Логируйте сортировку и порядок вместе с номером страницы. Если кто-то изменит sort между сбоем и перезапуском, сохраненный номер страницы больше не означает тот же фрагмент. Отклоните возобновление и начните с чистого листа.

Храните небольшую запись о возобновлении с сортировкой, порядком, страницей и временем. Цикл сначала читает эту запись и отказывается продолжать, если какое-либо поле больше не соответствует запросу, который он собирается выполнить.

Отклоненное возобновление — не сбой. Это цикл, говорящий, что сохраненная страница больше не означает те же строки, и начало с чистого листа — более дешевая ошибка.

Запуск цикла в продакшене

  1. 1

    Начните с небольшого домена

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

  2. 2

    Проверьте одну страницу вручную

    Откройте один сохраненный ответ и подтвердите, что аксессор все еще читает строки. Не доверяйте парсингу, который вы не проверяли глазами.

  3. 3

    Храните лимиты в конфиге

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

  4. 4

    Логируйте причину завершения цикла

    Записывайте, завершился ли запуск на неполной странице, на лимите или из-за ошибки. Эти три завершения означают разное для того, кто читает лог.