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

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

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

Масовий DR для списку клієнтів як 40-рядковий Python-скрипт

У вас є таблиця з доменами та дашборд, якому потрібне одне число на рядок. Інструмент для перевірки доменів посилань для цієї задачі — це насамперед інфраструктура, і цей скрипт є саме нею.

Закінчені дерев'яні блоки, розкладені по стопках на верстаку

Скрипт

Він читає domains.txt, надсилає по 200 доменів за запит, зберігає кожну сир відповідь у SQLite та виводить вартість запуску. Ми зберігаємо весь тіло відповіді, оскільки назви полів найкраще копіювати з реальної відповіді, а не з блогу.

python
import os, sqlite3, time, json, requests

BASE = "https://seomasters.dev/api.php"
KEY = os.environ["SEO_API_KEY"]
BATCH = 200

db = sqlite3.connect("dr.db")
db.execute("create table if not exists raw (ts int, batch text, body text)")

domains = [d.strip() for d in open("domains.txt") if d.strip()]
spent = 0
for i in range(0, len(domains), BATCH):
    chunk = domains[i:i + BATCH]
    for attempt in range(4):
        try:
            r = requests.get(BASE, timeout=30,
                params={"action": "bulk_dr", "domains": ",".join(chunk)},
                headers={"Authorization": "Bearer " + KEY})
            break
        except requests.RequestException:
            time.sleep(2 ** attempt)
    else:
        raise SystemExit(f"network gave up at batch {i}")
    if r.status_code in (401, 402):
        raise SystemExit(f"HTTP {r.status_code}: fix the key or top up, do not retry")
    body = r.json()
    if not body.get("ok"):
        raise SystemExit(f"ok=false at batch {i}: {json.dumps(body)[:300]}")
    db.execute("insert into raw values (?,?,?)", (int(time.time()), str(i), r.text))
    db.commit()
    print(body.get("_credits"))

Що робить кожна частина

action=bulk_dr

Один кредит за виклик. Документальний ліміт — 2000 доменів за виклик.

domains=

GET-параметр. Ендпоінт також приймає тіло POST-запиту, що є кращим варіантом для довгих списків. Перевірте роздільник з двома доменами перед повним запуском.

BATCH = 200

Наша звичка, а не ліміт: 200 коротких доменів тримають URL значно нижче загальних лімітів проксі.

_credits

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

Стовпець сирого body

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

Латунні терези з гирями, що демонструють точний розрахунок

Гіпотетичний запуск з арифметикою

Це приклад, а не історія клієнта. Уявіть агентство, яке відстежує 1800 доменів клієнтів та потенційних клієнтів у Google Sheets і хоче оновлювати DR щопонеділка.

При 200 доменах у батчі це 9 запитів. Дев'ять запитів по 1 кредиту — це 9 кредитів, або $0.0009 за запуск, а за 52 понеділки загальна сума становить 468 кредитів, що округлюється до менше п'яти центів.

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

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

Що може піти не так у цьому скрипті

СимптомПричинаВиправлення
SystemExit: HTTP 402Баланс нульовийПоповніть баланс на сторінці облікового запису, запустіть знову; завершені батчі вже в SQLite
SystemExit: HTTP 401Змінна середовища порожня або помилка в ключіВиведіть len(KEY) та перевірте префікс sk_
ok=false на батчі 3Некоректне значення в списку, наприклад, повний URLВидаліть схему та шлях перед надсиланням, див. таблицю помилок
Такий самий DR, як минулого тижняІндекс ще не перебудованоОчікуване; порівнюйте щомісяця
Довгий URL відхиленоЗанадто великий батч для GETЗменшіть BATCH або перейдіть на тіло POST-запиту

Розмір батчу, GET та POST для довгих списків

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

Чому 200, а не 2000

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

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

Якщо пізніше ви збільшите розмір батчу, зробіть це один раз, змінивши одну константу, і знову запустіть тест з двома доменами. Більше число — не привід пропускати тест.

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

GET для коротких списків, POST для довгих

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

Перевірте роздільник та кодування з двома доменами перед повним запуском. Додаткова кома проявиться в першому батчі, і дешевше побачити її саме там.

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

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

Обрізання та видалення дублікатів перед першим запитом

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

Оберіть одне правило для www. і застосовуйте його в одній функції, щоб один і той самий сайт ніколи не шукався під двома написами. Будь-який інструмент для перевірки доменів посилань, який масштабується на сотні імен, потребує такої дисципліни.

Що має писати лог запуску

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

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

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

Готовний дерев'яний з'єднання, що стоїть окремо від сирової деревини

Три розширення, варті післяобіднього часу

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

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

Запускайте його з GitHub Actions або звичайного cron на маленькому VPS. Підходить будь-який варіант. Важливо, щоб джоб виходив з ненульовим кодом помилки при будь-якому збої, бо тиха помилка виглядатиме як застарілий графік через три тижні.

Сортування виводу — це питання фронтенду. Ранжируйте портфель за показником, згрупуйте його або порівняйте з dr_distribution (1 кредит), щоб побачити, де знаходиться кожен клієнт серед усіх 181 132 097 доменів.

Дрейф полів та сирова таблиця

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

Спочатку збережіть сире тіло

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

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

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

Як виглядає дрейф на практиці

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

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

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

Порядок міграцій з даними

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

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

Тримайте парсер поруч із тестами

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

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

Перший запуск по порядку

  1. 1

    Протестуйте один домен

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

  2. 2

    Запустіть два батчі

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

  3. 3

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

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

  4. 4

    Заплануйте повний список

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

Перед плануванням

  • Спочатку видаліть дублікати та приведіть список до нижнього регістру
  • Вирішіть раз і назавжди в одній функції, чи залишається www., чи ні
  • Тримайте ключ у сховищі секретів, а не в crontab
  • Сповіщайте про ненульовий код виходу
  • Прорахуйте числа бюджету кредитів для вашого власного обсягу