tl;dr
Meine reguläre Expression schaut folgendermaßen aus:
pattern = (
r"^" # start
r"(https?://)?" # Optionales Protokoll
r"(([a-z\d][a-z\d-]*[a-z\d]\.)+[a-z]{2,}|" # Domain?
r"(\d{1,3}\.){3}(\d{1,3}))" # Oder IPv4?
r"(:\d+)?" # Optionaler Port
r"(/[a-z\d\-%_.~+]*)*" # Optionaler Pfad
r"(\?[a-z\d%_.~+=&]*)?" # Query Parameter
r"(\#[a-z\d_\-]*)?" # Fragmente
r"$" # End
)
Erklärung
Was in jeder Zeile passiert:
^
- Das definiert den Anfang des regulären Ausdrucks. Davor sollte nichts stehen.(https?://)?
- Das Protokoll. Das Fragezeichen am Ende definiert dass das Protokoll garnicht gegeben sein könnte; auch dass
ist optional (dadurch matche ichhttp://
und auchhttps://
). Mir ist klar dass es mehr Protokolle geben kann, aber das genügt aktuell für mich.- Die nächsten zwei Zeilen gehören zusammen - insbesondere sind sie per
|
(am Ende der ersten Zeile) durch ein “ODER” verbunden:([a-z\d][a-z\d-]*[a-z\d]\.)+[a-z]{2,}
definiert die Nutzung eines Domain Namens.
Von hinten nach vorne betrachtet: Wir haben eine Top Level Domain, die aus Buchstaben besteht und mindestens zwei Zeichen lang ist. Das ist der[a-z]{2,}
Teil.
Die Gruppe davor definiert jedes Level der Domain; per+
haben wir mindestens ein Subdomain-Level. Jede Subdomain muss mit Zahl oder Buchstabe enden, in der Mitte dürfen beliebig viele Zahlen, buchstaben und auch-
-Zeichen sein.- Als zweite Option könnte eine IP Adrese vorkommen. Hier beachte ich nur IPv4 Adressen; v6 kommt zu einem späteren Zeitpunkt. Wir haben zuerts drei Blocks mit jeweils zwischen einer und drei Zahlen und einem Punkt; der letzte Block hat wieder ein bis drei Zahlen, diesmal aber keinen Punkt.
- Der Port ist wieder optional und ansonsten recht simpel: Zuerst ein Doppelpunkt, dann einige Zahlen. Wir könnten uns hier technisch gesehen auf Zahlen unter $65535$ beschränken, aber das ist den Aufwand nicht wert.
- Der Pfad ist wiederum simpel: Starte mit einem Schrägstrich, danach kommt eine beliebige Anzahl an Zeichen, die hier definiert sind. Die Anzahl solcher Pfade ist beliebig.
- Query und Fragmente sind etwas schwieriger zu definieren, also habe ich es mir leicht gemacht: Wir starten mit
?
bzw.#
, und dann haben wir eine Anzahl an erlaubten Zeichen. Das ist formal nicht ganz korrekt, aber gut genug. Beide Teile sind optional. - Am Ende wird die reguläre Expression beendet - auch dahinter darf nichts mehr kommen.