6 Min. LesezeitEngineering

Wie man eine URL in Python mit der requests-Bibliothek kürzt

URL in Python mit wenigen Zeilen requests kürzen: ein POST an die API eines Shorteners, den Kurzlink zurücklesen, dann Retries, Idempotenz, Bulk und Async ergänzen.

Marius Voß
DevRel · edge infra
Wie man eine URL in Python kürzt: ein requests-POST sendet eine Ziel-URL an die API eines Shorteners und liest einen Kurzlink zurück, mit Retry- und Idempotenz-Schritten

Eine URL in Python zu kürzen ist ein einziger HTTP-POST. Sie senden Ihren langen Link mit der requests-Bibliothek an die API eines Shorteners, übergeben Ihren API-Schlüssel als Bearer-Token und lesen den Kurzlink aus der JSON-Antwort aus. Das Ganze passt in etwa fünf Zeilen, und der Rest dieses Leitfadens beschreibt, was Sie ergänzen, sobald Sie es im Ernstfall einsetzen: Fehlerbehandlung, Idempotenz, Bulk und Async.

Dies ist die Entwickler-Version der allgemeinen Anleitung zum Kürzen einer URL - jene behandelt den Dashboard- und Browser-Ablauf, dies hier ist Code, den Sie in ein Skript einfügen können. Die Endpunkte und Feldnamen unten verwenden die Elido-API, aber die Grundform (eine Ziel-URL per POST senden, eine kurze URL zurückbekommen) ist bei den meisten modernen Shortenern gleich, sodass sich die Muster übertragen lassen.

Beginnen Sie mit dem Überblick über die kostenlose URL-Shortener-API, falls Sie noch keinen Dienst gewählt haben - dort werden die Form der Anfrage, das Auth-Modell und die Grenzen der kostenlosen Stufe erläutert, von denen dieser Artikel ausgeht.

Der schnellste Weg: ein einziger POST mit requests

Installieren Sie requests, legen Sie Ihren API-Schlüssel in einer Umgebungsvariable ab und senden Sie die Ziel-URL per POST an den Links-Endpunkt:

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

Drei Dinge machen daraus produktionsreifen Code statt eines Spielzeugs. Der API-Schlüssel kommt aus der Umgebung, niemals als Literal im Quellcode. timeout ist gesetzt, sodass eine hängende Verbindung schnell fehlschlägt, statt endlos zu blockieren. Und raise_for_status() verwandelt einen 4xx- oder 5xx-Fehler in eine Exception, die Sie abfangen können, statt einen fehlgeschlagenen Aufruf wie einen Erfolg aussehen zu lassen.

Die Antwort ist JSON. Lesen Sie short_url für den fertigen Link aus; die meisten Shortener geben zudem eine id zurück, die Sie speichern, falls Sie den Link später bearbeiten oder ablaufen lassen möchten.

Fehler und Rate-Limits richtig behandeln

Der Idealfall sind fünf Zeilen. Ein Skript, das unbeaufsichtigt läuft, muss auch die unschönen Fälle überstehen: einen vorübergehenden 5xx, einen 429, wenn Sie zu schnell unterwegs sind, einen kurzen Netzwerkaussetzer. Umschließen Sie den Aufruf so, dass er bei den Fehlern erneut versucht, bei denen sich das lohnt, und bei denen aufgibt, die ohnehin nie gelingen.

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")

Ein 401 oder 403 behebt sich niemals von selbst durch erneutes Versuchen, deshalb fallen diese durch zu raise_for_status() und stoppen das Skript. Ein 429 respektiert den Retry-After-Header, den die API sendet. Ein 5xx bremst exponentiell ab. Der Deep-Dive zu Rate-Limits und Idempotenz erklärt, warum blindes erneutes Versuchen aus einem Ausfall zwei macht.

Ein Python-requests-Aufruf sendet eine Ziel-URL und einen Idempotency-Key mit einem Bearer-Token an den Links-Endpunkt des Shorteners, die API antwortet mit HTTP 201 und einer short_url, und eine Retry-Schleife bremst bei 429- und 5xx-Antworten ab

Einen Idempotency-Key senden, damit Retries nicht duplizieren

Hier ist der subtile Fehler in der obigen Retry-Schleife. Gelingt ein POST auf dem Server, geht die Antwort aber auf dem Rückweg verloren, sieht Ihr Code ein Timeout, versucht es erneut und erzeugt einen zweiten Kurzlink für dieselbe URL. Ein Idempotency-Key behebt das: Senden Sie mit jeder logischen Anfrage einen stabilen, eindeutigen Schlüssel, und die API gibt bei einer Wiederholung den ursprünglichen Link zurück, statt einen neuen zu prägen.

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,
)

Erzeugen Sie den Schlüssel einmal pro URL und verwenden Sie ihn bei allen Retries derselben URL wieder - nicht pro Versuch. Speichern Sie ihn zusammen mit der URL, falls der Batch selbst erneut ausgeführt werden könnte. Das ist die mit Abstand wichtigste Gewohnheit, wenn Sie vom Kürzen eines einzelnen Links zum Kürzen einer Liste übergehen, und genau deshalb ist sie fest in API und SDKs eingebaut.

URLs in großen Mengen kürzen

Mit einer sicheren shorten()-Funktion in der Hand ist ein Batch nur eine Schleife - aber eine Schleife, die das Rate-Limit respektiert und die Zuordnung zwischen Eingabe und Ausgabe nicht verliert:

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}")

Das Ergebnis in einem dict zu halten, das nach der ursprünglichen URL geschlüsselt ist, sorgt dafür, dass ein teilweiser Fehlschlag sichtbar und erneut ausführbar ist, statt eine stille Lücke zu sein. Für ein paar Hundert Links ist diese sequenzielle Version völlig ausreichend. Bei Tausenden summiert sich die Round-Trip-Latenz, und genau da verdient sich Async seinen Platz.

Async im großen Maßstab mit httpx

Ist der Batch groß, liegt der Engpass im Warten auf das Netzwerk, nicht in Python. Ein asynchroner Client wie httpx sendet viele Anfragen gleichzeitig und hält dabei eine Obergrenze ein, wie viele davon gerade unterwegs sind, sodass Sie die Verbindung ausschöpfen, ohne das Rate-Limit auszulösen.

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) ist der ganze Trick: Es begrenzt gleichzeitige Anfragen auf zehn, sodass Sie schnell vorankommen, ohne die API in 429-Fehler zu hämmern. Passen Sie den Wert an das dokumentierte Limit Ihres Plans an. Für die vollständige Form der Anfrage, die Feldnamen und die aktuellen Limits ist die API-Dokumentation die Referenz, und Lösungen für Entwickler bietet die SDKs, falls Sie den Client lieber nicht selbst bauen möchten.

Sequenzielles Bulk-Kürzen sendet eine Anfrage nach der anderen und wartet jeweils auf die Antwort, im Vergleich zu einem asynchronen Ansatz, der über ein semaphore eine begrenzte Anzahl von Anfragen gleichzeitig sendet, um einen großen Batch schneller zu kürzen

Welchen Ansatz Sie wählen sollten

Wählen Sie das Werkzeug passend zur Größe der Aufgabe. Ein Link in einem Skript: der fünfzeilige requests-Aufruf. Ein verlässlicher, zeitgesteuerter Job: die Version mit Retry und Idempotenz. Ein einmaliger Batch von ein paar Hundert: die sequenzielle Schleife. Zehntausende unter Zeitdruck: httpx mit einem semaphore.

Für welchen Sie sich auch entscheiden: Bewahren Sie den Schlüssel in der Umgebung auf, setzen Sie ein Timeout, und senden Sie einen Idempotency-Key bei allem, was erneut versucht werden könnte. Diese drei Gewohnheiten sind der Unterschied zwischen einem Snippet, das nur vorführt, und einem Skript, das Sie laufen lassen können.

Die Grundlagenserie lesen

Dieser Beitrag gehört zum Engineering-Cluster. Der Ausgangspunkt ist der Leitfaden zur kostenlosen URL-Shortener-API für die Form der Endpunkte und die Authentifizierung, danach der Beitrag zu Rate-Limits und Idempotenz für gutes Verhalten unter Last. Die lebende Referenz ist die API-Dokumentation.

Verwandte Beiträge im Blog

Häufig gestellte Fragen

Wie kürze ich eine URL in Python?

Senden Sie mit der requests-Bibliothek einen HTTP-POST an die API eines URL-Shorteners, übergeben Sie Ihre lange URL im JSON-Body und Ihren API-Schlüssel als Bearer-Token, und lesen Sie den Kurzlink anschließend aus der JSON-Antwort aus. Das sind etwa fünf Zeilen: die Header aufbauen, die Ziel-URL an den Links-Endpunkt posten und response.json()['short_url'] ausgeben.

Kann ich eine URL in Python ohne externe Bibliothek kürzen?

Ja. Das urllib.request-Modul der Standardbibliothek kann JSON per POST senden, ohne dass irgendetwas installiert werden muss, was in einer abgeriegelten Umgebung praktisch ist. Es ist umständlicher als requests - Sie kodieren den Body selbst, setzen die Header manuell und lesen die Antwort aus - aber es braucht kein pip install.

Wie kürze ich in Python viele URLs auf einmal?

Iterieren Sie über die Liste und senden Sie für jede einen POST, aber schicken Sie pro URL einen Idempotency-Key, damit ein Retry niemals ein Duplikat erzeugt, und respektieren Sie das Rate-Limit der API, indem Sie bei HTTP 429 abbremsen. Bei großen Batches kürzt ein asynchroner Client wie httpx mit einem begrenzten semaphore Tausende URLs gleichzeitig, statt eine nach der anderen.

Warum liefert meine Python-Anfrage an den URL-Shortener einen 401 zurück?

Ein 401 bedeutet, dass der API-Schlüssel im Authorization-Header fehlt oder falsch ist. Prüfen Sie, dass Sie genau 'Authorization: Bearer YOUR_API_KEY' senden, dass der Schlüssel nicht widerrufen wurde, und dass Sie ihn aus einer Umgebungsvariable geladen haben, statt einen abgelaufenen einzufügen. Ein 403 bedeutet dagegen, dass der Schlüssel gültig ist, aber für diese Aktion nicht die nötige Berechtigung (Scope) hat.

Elido testen

URL einfügen, kurzer Link in Sekunden

Kein Konto nötig. Link bleibt 30 Tage aktiv. Konto erstellen, um ihn dauerhaft zu behalten.

Kostenlos, keine Anmeldung erforderlich · 2 pro Tag

Elido testen

URL-Shortener mit EU-Hosting: eigene Domains, tiefe Analytik und eine offene API. Kostenloser Tarif - keine Kreditkarte nötig.

Tags
how to shorten a url in python
python url shortener
shorten url python requests
url shortener api python
python requests post json
bulk shorten urls python

Weiterlesen