Перейти до змісту

Дані про посилання у форматі JSON: $100 безкоштовного кредиту, без картки, вхід через Google.

SEOMasters.dev
SEOMasters.devЗареєструватися

Перемикання сторінок у перевірці зворотних посилань URL без пропусків або нескінченних циклів

Кількість рядків за запитом, сторінки за запитом та жорстка межа в 50 000. Ось цикл, який враховує всі три параметри та повідомляє, коли зупинитися.

Безперервна кам’яна аркова галерея, що перетинає туманну річку без пропусків
Три ключові числа, які варто запам’ятати

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

Чому сортування має передувати пагінації

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

Запит refdomains коштує 2 кредити за запит, тому 50 запитів становлять 100 кредитів, або один цент. Навіть у найгіршому випадку це дешево; справжня проблема — цикл, який не завершується.

Вибір колонки для сортування

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

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

Зафіксуйте свій вибір у звіті, щоб наступний користувач розумів, чому ліміт спрацював саме так.

Записи, що змінюються під час запуску

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

Записуйте час початку на початку логу та номер сторінки в кожному рядку. Тоді невідповідність у виводі вказуватиме на таймінг, а не на помилку парсингу.

Якщо завдання заплановано, уникайте часу запуску одразу перед очікуваним оновленням. У ваших логах оновлення виглядає як зміна загальної кількості між двома запусками, які інакше збігаються, тому вказуйте загальну кількість у кожному рядку логу.

Запуск, що охоплює оновлення, не є помилковим за замовчуванням. Він лише несумісний, тому позначайте такі запуска у логу та повторюйте відповідний зріз, коли це важливо для звіту.

Цикл пагінації, який завершується

TypeScript для Worker у Node 20. Він зупиняється при першій короткій сторінці, при досягненні ліміту або при будь-якому ok, який не є істинним.

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 та напишіть доступ до даних на основі того, що ви бачите, а не того, що обіцяли в підручнику.

Для більш дешевого аналога цієї задачі — оцінки багатьох доменів одночасно без пагінації — прочитайте скрипт масового DR.

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

Потім зафіксуйте її за допомогою тесту. Збережіть одну відповідь як файл фікстури та перевіряйте кількість рядків та ключі першого рядка, щоб майбутня зміна призводила до помилки в CI, а не на графіку у клієнта.

Зберігайте фікстуру в системі контролю версій поруч із тестом, щоб рецензент бачив структуру, яку очікує код, не запускаючи його.

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

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

Підрахуйте збережені рядки

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

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

Зберігайте кількість у тому ж рядку логу, що й номер сторінки, щоб їх можна було прочитати разом одним поглядом.

Розподіл роботи вище ліміту

Деякі запити виходять за межі ліміту. Для списків вище ліміту розділяйте проблему, а не намагайтеся пагінувати далі. Витягніть топ 50 000 за одним сортуванням, потім запитайте інші дії щодо найактивніших доменів, оскільки вони також коштують 2 кредити та відповідають на інші питання. Глибша пагінація ніколи не дозволить обійти відрізання.

Витягніть топ 50 000 за одним сортуванням

Сприймайте ліміт як факт API. Витягніть топ 50 000 рядків за одним сортуванням, збережіть їх та позначте результат як верхній зріз у будь-якому звіті.

Панель керування, яка показує обмежену кількість як повну загальну, вводить в оману читачів. Метка нічого не коштує, а звіт перевірки зворотних посилань URL виглядає більш чесно з нею.

Визначте розмір зрізу до запуску та вкажіть його в заголовку звіту, щоб число ніколи не виглядало більшим, ніж є насправді. Рядок заголовка нічого не коштує, і читач, який пізніше знайде пропущений рядок, подякує вам за це.

Запитайте найактивніші домени про інше

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

Зберігайте їхні результати в окремих таблицях, згрупованих за доменом, щоб пізніший витяг того ж домену міг замінити їх, не чіпаючи рядки верхнього зрізу.

Кожна додаткова дія коштує свої кредити, тому оцініть витрати до запуску, а не після отримання рахунку.

Короткий список найактивніших доменів, витягнутий раз на місяць, часто дає більш корисний контекст, ніж довший список, витягнутий щонічі.

Об’єднання за ключем домену

Коли два витяги перетинаються, об’єднайте їх за ключем домену та збережіть новіший час. Тоді об’єднання ніколи не залежатиме від того, який витяг завершився першим.

Логуйте, який витяг виграв для кожного рядка. Якщо число змінюється між двома звітами, цей лог покаже, чи змінився індекс, чи механізм об’єднання.

Зберігайте об’єднання в одній функції з тестом, оскільки це єдине місце, де зустрічаються два витяги.

Відновлення після збою

Збої трапляються. Деплой перезапускає Worker, ноутбук переходить у режим сну, VPN відключається, і цикл гине на сторінці 31 із 50.

Зберігайте номер сторінки після кожного запиту

Зберігайте номер останньої завершеної сторінки після кожного запиту. Перезапускайтеся з цього номера плюс один, а не з початку, і ви платите за 2 кредити за сторінку, а не перекуповуєте весь витяг.

Зберігайте дані після парсингу відповіді, а не до. Сторінку, яку ви не змогли прочитати, слід витягнути знову, а збережений номер сторінки завжди має означати рядки, які у вас є.

Збереження раз на хвилину замість кожного запиту залишає вікно, де збій може призвести до повторної оплати кредитів. Зробіть запис мінімальним, щоб його було дешево виконувати кожного разу.

Логуйте також час, який займає збереження, і час кожного запиту. Збереження, яке конкурує за час із самим запитом, вказує на шар зберігання, і це варто виправити до розширення циклу.

Upsert за ключем домену

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

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

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

Відхиляйте відновлення, якщо змінилося сортування

Логуйте сортування та порядок разом із номером сторінки. Якщо хтось змінює sort між збоєм та перезапуском, збережений номер сторінки більше не означає той самий зріз. Відхиліть відновлення та почніть чисто.

Зберігайте невеликий запис про відновлення з сортуванням, порядком, сторінкою та часом. Цикл спочатку читає цей запис і відмовляється продовжувати, якщо будь-яке поле більше не відповідає запиту, який він збирається виконати.

Відхилене відновлення — це не збій. Це цикл, який повідомляє, що збережена сторінка більше не означає ті самі рядки, і почати чисто — менша помилка.

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

  1. 1

    Почніть з малого домену

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

  2. 2

    Перевірте одну сторінку візуально

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

  3. 3

    Зберігайте ліміти в конфігурації

    Виставте ліміт та розмір сторінки в одному файлі конфігурації, щоб зміна відображалася в рецензуванні, а не ховалася всередині функції.

  4. 4

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

    Записуйте, чи завершився запуск на короткій сторінці, на ліміті чи на помилці. Ці три завершення означають різне для того, хто читає лог.