Le parsing d'URL (de l'anglais parse, « analyser ») est la décomposition de l'adresse d'une page web en ses parties constitutives : protocole, domaine, port, chemin, paramètres de requête et ancre. D'une chaîne « brute » comme https://example.com/catalog?page=2, on obtient un objet structuré dont chaque champ est accessible séparément et modifiable au besoin.
À première vue, la tâche semble triviale — « découper la chaîne sur des séparateurs ». En pratique, l'URL a une spécification formelle (RFC 3986) avec une foule de subtilités : encodage des caractères spéciaux, liens relatifs, parties facultatives de l'adresse, normalisation. C'est pourquoi presque chaque langage possède un outil standard de parsing d'URL, et écrire son propre découpage avec des expressions régulières n'est pratiquement jamais nécessaire.
De quoi se compose une URL
Décomposons une adresse la plus « complète » possible pour voir tous les composants :
https://user:pass@example.com:8080/catalog/items?page=2&sort=price#reviews
└─┬─┘ └──┬───┘ └────┬────┘└─┬─┘└─────┬──────┘ └──────┬──────┘ └──┬──┘
scheme userinfo host port path query fragment
| Composant | Valeur dans l'exemple | Rôle |
|---|---|---|
| scheme (schéma) | https |
Protocole d'accès : http, https, ftp, mailto, etc. |
| userinfo | user:pass |
Identifiants (rarement utilisé, surtout pour FTP/basic-auth) |
| host (hôte) | example.com |
Nom de domaine ou adresse IP |
| port (port) | 8080 |
Port ; s'il n'est pas indiqué, on prend le standard du schéma (80 pour http, 443 pour https) |
| path (chemin) | /catalog/items |
Chemin vers la ressource sur le serveur |
| query (requête) | page=2&sort=price |
Paramètres de requête au format « clé=valeur » |
| fragment (ancre) | reviews |
Renvoi vers une section précise de la page ; n'est pas envoyé au serveur |
Ce sont précisément ces parties que le parseur extrait. On peut ensuite travailler avec : récupérer la valeur d'un paramètre GET précis, remplacer le domaine, écarter l'ancre, assembler une nouvelle URL à partir des composants modifiés.
À quoi sert le parsing d'URL
Le parsing d'URL apparaît dans pratiquement toute application qui manipule des adresses web. Les scénarios les plus fréquents :
- Extraction des paramètres de requête. Il faut savoir sur quelle page du catalogue se trouve l'utilisateur (
?page=5), quel filtre est choisi ou quel tag UTM est présent. Le parseur transforme la chaîne de requête en dictionnaire commode. - Validation et assainissement des liens. Avant d'enregistrer une URL fournie par l'utilisateur ou de l'insérer dans une redirection, il faut s'assurer que le schéma est autorisé (par exemple, pas
javascript:) et que le domaine figure dans la liste blanche. - Routage. Les frameworks web décomposent le chemin et les paramètres pour déterminer quel gestionnaire appeler.
- Analytique et logs. Pour regrouper les accès par domaines ou chemins, on commence par décomposer les adresses.
- Web scraping et crawling. Ici, le parsing d'URL est particulièrement important — nous y venons en détail.
Le parsing d'URL dans la construction de crawlers
Le crawler (robot d'exploration) parcourt un site en suivant les liens de page en page. Pour bien saisir ce qui distingue le crawling du scraping et du parsing proprement dit, nous recommandons de lire d'abord notre article « Scraping, parsing et crawling : quelles différences ? ». En bref : le crawler se charge du parcours et de la découverte des pages, le scraper — de l'extraction des données, et le parsing — de l'analyse du contenu obtenu, adresses comprises.
Le grand « piège » du parcours d'un site : les liens dans le HTML sont presque toujours relatifs, et non absolus. Dans le balisage, vous rencontrerez :
<a href="/about">À propos</a>
<a href="../catalog/phones">Téléphones</a>
<a href="page2.html">Suivante</a>
<a href="#reviews">Avis</a>
Le robot ne peut pas suivre directement le lien /about ou page2.html — ce ne sont pas des adresses complètes. Pour poursuivre le parcours, le lien relatif doit être converti en forme absolue en s'appuyant sur l'adresse de la page courante (l'URL de base). C'est exactement le rôle du parsing d'URL :
| URL de base de la page | Lien relatif | Résultat (URL absolue) |
|---|---|---|
https://example.com/catalog/items/ |
/about |
https://example.com/about |
https://example.com/catalog/items/ |
../phones |
https://example.com/catalog/phones |
https://example.com/catalog/items/ |
page2.html |
https://example.com/catalog/items/page2.html |
https://example.com/catalog/items/ |
https://cdn.example.com/x |
https://cdn.example.com/x (déjà absolue) |
Cette opération s'appelle la résolution de lien (URL resolution), et presque tous les langages ont une fonction prête pour cela : urljoin en Python, le constructeur new URL(href, base) en JavaScript, URI.resolve() en Java, ResolveReference() en Go.
Normalisation et élimination des doublons
Au-delà de la conversion en absolu, le crawler doit normaliser les adresses pour ne pas parcourir plusieurs fois la même page. Techniques typiques :
- écarter l'ancre
#fragment— elle pointe vers une zone à l'intérieur de la page, pas vers une ressource distincte (/page#topet/page, c'est la même page) ; - mettre le schéma et le domaine en minuscules ;
- retirer le port standard (
:443pour https) ; - ordonner ou filtrer les paramètres de requête (par exemple, jeter les tags UTM).
Après normalisation, les URL absolues sont stockées dans l'ensemble des adresses déjà visitées — ainsi le robot ne tourne pas en rond et ne fait pas de requêtes superflues.
Exemples d'implémentation de parseurs d'URL
Voici comment décomposer une URL et résoudre un lien relatif avec les outils standard des langages populaires.
Python
Dans la bibliothèque standard, tout passe par le module urllib.parse.
from urllib.parse import urlparse, urljoin, urldefrag, parse_qs
url = "https://example.com:8080/catalog/items?page=2&sort=price#reviews"
parsed = urlparse(url)
print(parsed.scheme) # https
print(parsed.hostname) # example.com
print(parsed.port) # 8080
print(parsed.path) # /catalog/items
print(parsed.query) # page=2&sort=price
print(parsed.fragment) # reviews
# Paramètres de requête -> dictionnaire
print(parse_qs(parsed.query)) # {'page': ['2'], 'sort': ['price']}
# Résolution d'un lien relatif (pour le crawler)
base = "https://example.com/catalog/items/"
print(urljoin(base, "../about")) # https://example.com/catalog/about
print(urljoin(base, "/contacts")) # https://example.com/contacts
print(urljoin(base, "page2.html")) # https://example.com/catalog/items/page2.html
# Écarter l'ancre lors de la normalisation
print(urldefrag("https://example.com/page#reviews")[0]) # https://example.com/page
JavaScript (navigateur et Node.js)
La classe intégrée URL fonctionne à l'identique dans le navigateur et dans Node.js. Elle sait aussi résoudre les liens relatifs — l'adresse de base se passe en second argument.
const url = new URL("https://example.com:8080/catalog/items?page=2&sort=price#reviews");
console.log(url.protocol); // "https:"
console.log(url.hostname); // "example.com"
console.log(url.port); // "8080"
console.log(url.pathname); // "/catalog/items"
console.log(url.search); // "?page=2&sort=price"
console.log(url.hash); // "#reviews"
// Paramètres de requête
console.log(url.searchParams.get("page")); // "2"
// Résolution d'un lien relatif
const base = "https://example.com/catalog/items/";
console.log(new URL("../about", base).href); // "https://example.com/catalog/about"
console.log(new URL("/contacts", base).href); // "https://example.com/contacts"
PHP
La décomposition de base est assurée par parse_url(), et parse_str() transforme la chaîne de requête en tableau.
$url = "https://example.com:8080/catalog/items?page=2&sort=price#reviews";
$parts = parse_url($url);
echo $parts['scheme']; // https
echo $parts['host']; // example.com
echo $parts['port']; // 8080
echo $parts['path']; // /catalog/items
echo $parts['fragment']; // reviews
parse_str($parts['query'], $query);
print_r($query); // ['page' => '2', 'sort' => 'price']
PHP n'a pas d'équivalent intégré de urljoin ; pour résoudre les liens relatifs dans les crawlers, on branche donc habituellement une bibliothèque — par exemple league/uri ou les helpers de Guzzle (GuzzleHttp\Psr7\UriResolver).
Java
La bibliothèque standard offre la classe java.net.URI pour travailler avec les adresses. La méthode resolve() donne directement la résolution d'un lien relatif.
import java.net.URI;
URI uri = URI.create("https://example.com:8080/catalog/items?page=2&sort=price#reviews");
System.out.println(uri.getScheme()); // https
System.out.println(uri.getHost()); // example.com
System.out.println(uri.getPort()); // 8080
System.out.println(uri.getPath()); // /catalog/items
System.out.println(uri.getQuery()); // page=2&sort=price
System.out.println(uri.getFragment()); // reviews
// Résolution d'un lien relatif
URI base = URI.create("https://example.com/catalog/items/");
System.out.println(base.resolve("../about")); // https://example.com/catalog/about
System.out.println(base.resolve("/contacts")); // https://example.com/contacts
Go
Le paquet net/url de la bibliothèque standard couvre aussi bien la décomposition que la résolution de liens (ResolveReference).
package main
import (
"fmt"
"net/url"
)
func main() {
u, _ := url.Parse("https://example.com:8080/catalog/items?page=2&sort=price#reviews")
fmt.Println(u.Scheme) // https
fmt.Println(u.Hostname()) // example.com
fmt.Println(u.Port()) // 8080
fmt.Println(u.Path) // /catalog/items
fmt.Println(u.RawQuery) // page=2&sort=price
fmt.Println(u.Query().Get("page")) // 2
// Résolution d'un lien relatif
base, _ := url.Parse("https://example.com/catalog/items/")
ref, _ := url.Parse("../about")
fmt.Println(base.ResolveReference(ref)) // https://example.com/catalog/about
}
Exemple pratique : collecter tous les liens d'une page
Assemblons le tout — l'étape typique d'un crawler : parser le HTML, récupérer tous les liens et les convertir en absolu. Exemple en Python avec requests et BeautifulSoup :
import requests
from bs4 import BeautifulSoup
from urllib.parse import urljoin, urldefrag
def extract_links(page_url: str) -> set[str]:
response = requests.get(page_url, timeout=10)
soup = BeautifulSoup(response.text, "html.parser")
links = set()
for a in soup.find_all("a", href=True):
# 1. on convertit le lien relatif en absolu
absolute = urljoin(page_url, a["href"])
# 2. on écarte l'ancre pour ne pas multiplier les doublons
absolute, _ = urldefrag(absolute)
# 3. on ne garde que les liens http(s)
if absolute.startswith(("http://", "https://")):
links.add(absolute)
return links
for link in extract_links("https://example.com/catalog/"):
print(link)
Ici, le parsing d'URL remplit deux missions clés : urljoin transforme les href relatifs en adresses complètes que le robot pourra suivre, et urldefrag évite de compter /page et /page#top comme deux pages différentes.
Erreurs fréquentes
- Parser les URL avec des expressions régulières. Vu l'abondance de cas limites dans la spécification, un découpage maison se révèle presque toujours incomplet. Utilisez les bibliothèques standard.
- Oublier les liens relatifs. Sans conversion en absolu, le crawler « trébuche » dès le premier lien interne.
- Ne pas normaliser les adresses. Sans écarter l'ancre, ordonner les paramètres et unifier la casse, le robot parcourt les mêmes pages en boucle.
- Faire confiance au schéma sans vérification. Avant une redirection ou l'insertion d'un lien, assurez-vous que le schéma est sûr (
http/https), sinon la porte s'ouvre au XSS viajavascript:.
Conclusion
Le parsing d'URL est une opération basique mais importante : elle transforme une chaîne-adresse en structure commode à manipuler. Dans les applications ordinaires, il sert à extraire les paramètres, valider et router ; dans les crawlers, il devient critique — sans conversion des liens relatifs en absolu et sans normalisation, le parcours d'un site est tout simplement impossible. La bonne nouvelle : presque chaque langage dispose pour cela d'un outil standard fiable, et il n'y a pas à réinventer la roue.