Скоротити URL у Python - це один HTTP POST-запит. Ви надсилаєте своє довге посилання до API скорочувача за допомогою бібліотеки requests, передаєте свій API-ключ як Bearer-токен і зчитуєте коротке посилання з JSON-відповіді. Усе це вміщується приблизно у п'ять рядків, а решта цього гайду - те, що варто додати, коли ви робите це по-справжньому: обробку помилок, ідемпотентність, масову обробку та асинхронність.
Це версія для розробників загального гайду про те, як скоротити URL - той матеріал охоплює роботу через дашборд і браузер, а цей - код, який можна вставити у скрипт. Ендпоінти та назви полів нижче стосуються API Elido, але сама форма (POST з адресою, у відповідь - коротке посилання) однакова у більшості сучасних скорочувачів, тож ці патерни переносяться без змін.
Почніть з огляду безкоштовного API скорочувача URL, якщо ви ще не обрали сервіс - там викладено форму запиту, модель авторизації та ліміти безкоштовного тарифу, які ця стаття вважає відомими.
Найшвидший спосіб: один POST через requests
Встановіть requests, покладіть свій API-ключ у змінну середовища й надішліть POST з цільовою URL-адресою на ендпоінт links:
import os
import requests
resp = requests.post(
"https://api.elido.app/v1/links",
headers={"Authorization": f"Bearer {os.environ['ELIDO_API_KEY']}"},
json={"destination_url": "https://example.com/a-very-long-path?with=params"},
timeout=10,
)
resp.raise_for_status()
print(resp.json()["short_url"]) # -> https://s.elido.me/ab12cd
Три речі роблять це продакшн-якісним рішенням, а не іграшкою. API-ключ береться зі змінної середовища, а не з буквального рядка в коді. Встановлено timeout, тож зависле з'єднання швидко провалиться замість того, щоб блокувати виконання назавжди. І raise_for_status() перетворює 4xx чи 5xx на виняток, який можна перехопити, а не дозволяє провальному виклику виглядати як успішний.
Відповідь - це JSON. Зчитайте short_url для готового посилання; більшість скорочувачів також повертають id, який варто зберегти, якщо плануєте пізніше редагувати чи скасовувати посилання.
Правильно обробляйте помилки та ліміти швидкості
Щасливий шлях - п'ять рядків. Скрипт, що працює без нагляду, має пережити нещасливі: тимчасовий 5xx, 429, коли ви йдете надто швидко, мережевий збій. Обгорніть виклик так, щоб він повторював спробу на помилках, які варто повторювати, і здавався на тих, які ніколи не виправляться.
import os
import time
import requests
def shorten(destination: str, *, retries: int = 3) -> str:
headers = {"Authorization": f"Bearer {os.environ['ELIDO_API_KEY']}"}
for attempt in range(retries):
resp = requests.post(
"https://api.elido.app/v1/links",
headers=headers,
json={"destination_url": destination},
timeout=10,
)
if resp.status_code == 429:
time.sleep(int(resp.headers.get("Retry-After", 2)))
continue
if resp.status_code >= 500:
time.sleep(2 ** attempt) # exponential backoff
continue
resp.raise_for_status() # 4xx other than 429 -> raise
return resp.json()["short_url"]
raise RuntimeError(f"shorten failed after {retries} attempts")
401 чи 403 ніколи не виправляться самі повторними спробами, тож вони провалюються до raise_for_status() і зупиняють скрипт. 429 враховує заголовок Retry-After, який надсилає API. 5xx відступає назад експоненційно. Матеріал про ліміти швидкості та ідемпотентність детально пояснює, чому сліпі повторні спроби перетворюють один збій на два.
Надсилайте Idempotency-Key, щоб повторні спроби не дублювали
Ось тонка помилка в циклі повторних спроб вище. Якщо POST-запит успішно виконався на сервері, але відповідь загубилася на шляху назад, ваш код бачить таймаут, повторює спробу і створює другий короткий лінк для тієї самої URL-адреси. Ключ ідемпотентності виправляє це: надсилайте стабільний, унікальний ключ з кожним логічним запитом, і API повертає оригінальне посилання при повторі замість того, щоб карбувати нове.
import uuid
key = str(uuid.uuid4()) # one key per URL you want shortened once
resp = requests.post(
"https://api.elido.app/v1/links",
headers={
"Authorization": f"Bearer {os.environ['ELIDO_API_KEY']}",
"Idempotency-Key": key,
},
json={"destination_url": "https://example.com/launch"},
timeout=10,
)
Генеруйте ключ один раз на кожну URL-адресу й повторно використовуйте його при повторних спробах для тієї самої адреси - не для кожної окремої спроби. Зберігайте його поруч із URL-адресою, якщо саму партію можуть перезапустити. Це найважливіша звичка, коли ви переходите від скорочення одного посилання до скорочення списку, і саме вона закладена в API та SDK з цієї причини.
Скорочення URL-адрес у масовому режимі
Маючи безпечну функцію shorten(), партія - це просто цикл, але цикл, що враховує ліміт швидкості й не втрачає зв'язок між вхідними та вихідними даними:
urls = [
"https://example.com/spring-sale",
"https://example.com/newsletter",
"https://example.com/docs/getting-started",
]
results = {}
for url in urls:
try:
results[url] = shorten(url)
except Exception as err: # log and keep going; one bad URL should not sink the batch
results[url] = f"ERROR: {err}"
for original, short in results.items():
print(f"{short}\t{original}")
Зберігання результату у словнику, ключем якого є оригінальна URL-адреса, означає, що часткова невдача видима й піддається повторному запуску, а не є мовчазною прогалиною. Для кількох сотень посилань ця послідовна версія цілком підходить. Для тисяч затримка кожного запиту-відповіді накопичується, і саме тут асинхронність себе виправдовує.
Асинхронність у масштабі з httpx
Коли партія велика, вузьким місцем стає очікування мережі, а не Python. Асинхронний клієнт на кшталт httpx надсилає багато запитів одночасно, водночас утримуючи стелю на кількість запитів у польоті, тож ви насичуєте з'єднання, не порушуючи ліміт швидкості.
import asyncio
import os
import httpx
async def shorten_all(urls: list[str], concurrency: int = 10) -> dict[str, str]:
headers = {"Authorization": f"Bearer {os.environ['ELIDO_API_KEY']}"}
limit = asyncio.Semaphore(concurrency)
out: dict[str, str] = {}
async with httpx.AsyncClient(timeout=10) as client:
async def one(url: str) -> None:
async with limit:
r = await client.post(
"https://api.elido.app/v1/links",
headers=headers,
json={"destination_url": url},
)
out[url] = r.json()["short_url"]
await asyncio.gather(*(one(u) for u in urls))
return out
# asyncio.run(shorten_all(my_urls))
Semaphore(10) - це весь трюк: він обмежує кількість одночасних запитів десятьма, тож ви прискорюєтеся, не закидуючи API 429-ми відповідями. Налаштуйте це значення під задокументовані ліміти вашого тарифу. Повну форму запиту, назви полів і актуальні ліміти дивіться в документації API, а рішеннях для розробників знайдете SDK, якщо не хочете писати клієнт вручну.
Який підхід обрати
Підбирайте інструмент під розмір завдання. Одне посилання в скрипті: п'ятирядковий виклик requests. Надійне завдання, що виконується за розкладом: версія з повторними спробами й ідемпотентністю. Разова партія з кількох сотень: послідовний цикл. Десятки тисяч у дедлайн: httpx із semaphore.
Хоч би що ви обрали, тримайте ключ у змінній середовища, встановлюйте таймаут і надсилайте ключ ідемпотентності на все, що може повторюватися. Ці три звички - різниця між сніпетом для демо і скриптом, який можна залишити працювати.
Читайте серію опорних матеріалів
Цей матеріал входить у блок про інженерію. Відправна точка - гайд про безкоштовний API скорочувача URL щодо форми ендпоінтів і авторизації, а далі - матеріал про ліміти швидкості та ідемпотентність щодо коректної поведінки під навантаженням. Живий довідник - документація API.
Пов'язане в блозі
Поширені запитання
Як скоротити URL у Python?
Надішліть HTTP POST-запит до API скорочувача URL за допомогою бібліотеки requests, передавши вашу довгу URL-адресу в тілі JSON, а ваш API-ключ - як Bearer-токен, а потім зчитайте коротке посилання з JSON-відповіді. Це приблизно п'ять рядків: зберіть заголовки, надішліть POST з цільовою адресою на ендпоінт links і виведіть response.json()['short_url'].
Чи можна скоротити URL у Python без зовнішньої бібліотеки?
Так. urllib.request зі стандартної бібліотеки може надіслати POST з JSON без встановлення чогось додатково, що зручно в обмеженому середовищі. Це багатослівніше за requests - тіло запиту доводиться кодувати самостійно, заголовки встановлювати вручну, а відповідь зчитувати вручну - але pip install не потрібен.
Як скоротити багато URL-адрес одразу у Python?
Пройдіться циклом по списку й надішліть POST для кожної адреси, але додавайте Idempotency-Key на кожну URL-адресу, щоб повторна спроба ніколи не створювала дублікат, і дотримуйтеся ліміту швидкості API, відступаючи назад при HTTP 429. Для великих партій асинхронний клієнт на кшталт httpx з обмеженим semaphore скорочує тисячі URL-адрес одночасно, а не по одній.
Чому мій запит на скорочення URL у Python повертає 401?
401 означає, що API-ключ відсутній або неправильний у заголовку Authorization. Перевірте, що ви надсилаєте саме 'Authorization: Bearer YOUR_API_KEY', що ключ не відкликано, і що ви завантажили його зі змінної середовища, а не вставили прострочений. 403, натомість, означає, що ключ дійсний, але не має прав (scope) для цієї дії.
Спробуйте Elido
Вставте URL - отримайте коротке посилання
Без реєстрації. Посилання живе 30 днів. Зареєструйтесь, щоб зберегти назавжди.
Безкоштовно, без реєстрації · 2 на день