178 votes

Comment joindre les composants d'un chemin lors de la construction d'une URL en Python ?

Par exemple, je veux joindre un chemin de préfixe à des chemins de ressources tels que /js/foo.js.

Je veux que le chemin résultant soit relatif à la racine du serveur. Dans l'exemple ci-dessus, si le préfixe était "media", je voudrais que le résultat soit /media/js/foo.js.

os.path.join fait cela très bien, mais la façon dont il joint les chemins dépend du système d'exploitation. Dans ce cas, je sais que je vise le web, et non le système de fichiers local.

Existe-t-il une meilleure alternative lorsque vous travaillez avec des chemins dont vous savez qu'ils seront utilisés dans des URL ? Est-ce que os.path.join fonctionnera suffisamment bien ? Est-ce que je devrais simplement créer le mien ?

2 votes

os.path.join ne fonctionnera pas. Mais le simple fait d'adhérer à la / devrait fonctionner dans tous les cas -- / est le séparateur de chemin standard dans HTTP, conformément à la spécification.

256voto

Ben James Points 41165

Vous pouvez utiliser urllib.parse.urljoin :

>>> from urllib.parse import urljoin
>>> urljoin('/media/path/', 'js/foo.js')
'/media/path/js/foo.js'

Mais attention :

>>> urljoin('/media/path', 'js/foo.js')
'/media/js/foo.js'
>>> urljoin('/media/path', '/js/foo.js')
'/js/foo.js'

La raison pour laquelle vous obtenez des résultats différents de /js/foo.js y js/foo.js car le premier commence par une barre oblique, ce qui signifie qu'il commence déjà à la racine du site web.

Sur Python 2, vous devez faire

from urlparse import urljoin

4 votes

J'ai donc supprimé le "/" de tête sur /js/foo.js, mais il semble que ce soit aussi le cas avec os.path.join. Exiger la barre oblique après media signifie que je dois faire la plus grande partie du travail moi-même de toute façon.

1 votes

Plus précisément, une fois que j'ai compris que le préfixe doit se terminer par / et que le chemin cible ne peut pas commencer par /, je pourrais tout aussi bien concaténer. Dans ce cas, je ne suis pas sûr que urljoin soit vraiment utile ?

1 votes

@amjoconn L'avantage d'utiliser urlparse.urljoin est qu'il supprime les slashes en double entre les parties jointes de l'url. Vous n'avez donc pas à vous soucier de les vérifier manuellement et vous pouvez vous concentrer sur l'ajout/la suppression des slashes au début ou à la fin de l'url résultante.

90voto

Alex Martelli Points 330805

Puisque, d'après les commentaires postés par l'OP, il semble qu'il ne veulent préserver les "URL absolus" dans la jointure (ce qui est l'une des principales fonctions de l'application urlparse.urljoin ;-), je recommande de l'éviter. os.path.join serait également mauvais, pour exactement la même raison.

J'utiliserais donc quelque chose comme '/'.join(s.strip('/') for s in pieces) (si l'en-tête / doit également être ignoré -- si le morceau de tête doit être mis en majuscules, c'est également possible bien sûr;-).

1 votes

Merci. Cela ne me dérangeait pas trop d'exiger que le '/' de tête de la deuxième partie ne puisse pas être là, mais exiger le '/' de fin de la première partie me donne l'impression que dans ce cas d'utilisation, urljoin ne fait rien pour moi. J'aimerais qu'au moins join("/media", "js/foo.js") et join("/media/", "js/foo.js") fonctionnent. Merci pour ce qui semble être la bonne réponse : lancez-vous.

1 votes

J'espérais que quelque chose ferait le dépouillement et la jonction '/' pour moi.

3 votes

Non, cela ne fonctionnera pas sous Windows, où os.path.join('http://media.com', 'content') reviendrait http://media.com\content .

63voto

GP89 Points 2024

Comme vous le dites, os.path.join joint les chemins en fonction de l'OS actuel. posixpath est le module sous-jacent utilisé sur les systèmes posix sous l'espace de noms os.path :

>>> os.path.join is posixpath.join
True
>>> posixpath.join('/media/', 'js/foo.js')
'/media/js/foo.js'

Vous pouvez donc simplement importer et utiliser posixpath.join à la place pour les urls, qui est disponible et fonctionnera sur toute plate-forme .

Editer : La suggestion de @Pete est bonne, vous pouvez aliaser l'importation pour améliorer la lisibilité.

from posixpath import join as urljoin

Editer : Je pense que cela est plus clair, ou du moins que cela m'a aidé à comprendre, si vous regardez la source de os.py (le code ici est celui de Python 2.7.11, et j'en ai coupé quelques parties). Il y a des importations conditionnelles dans os.py qui choisit le module de chemin à utiliser dans l'espace de noms os.path . Tous les modules sous-jacents ( posixpath , ntpath , os2emxpath , riscospath ) qui peuvent être importés dans les os.py , alias path sont là et existent pour être utilisés sur tous les systèmes. os.py consiste simplement à choisir l'un des modules à utiliser dans l'espace de noms os.path au moment de l'exécution en fonction du système d'exploitation actuel.

# os.py
import sys, errno

_names = sys.builtin_module_names

if 'posix' in _names:
    # ...
    from posix import *
    # ...
    import posixpath as path
    # ...

elif 'nt' in _names:
    # ...
    from nt import *
    # ...
    import ntpath as path
    # ...

elif 'os2' in _names:
    # ...
    from os2 import *
    # ...
    if sys.version.find('EMX GCC') == -1:
        import ntpath as path
    else:
        import os2emxpath as path
        from _emx_link import link
    # ...

elif 'ce' in _names:
    # ...
    from ce import *
    # ...
    # We can use the standard Windows path.
    import ntpath as path

elif 'riscos' in _names:
    # ...
    from riscos import *
    # ...
    import riscospath as path
    # ...

else:
    raise ImportError, 'no os specific module found'

6 votes

from posixpath import join as urljoin l'aliaser de manière à ce qu'il soit facile à lire.

39voto

Rune Kaagaard Points 2044

Ce produit fait très bien l'affaire :

def urljoin(*args):
    """
    Joins given arguments into an url. Trailing but not leading slashes are
    stripped for each argument.
    """

    return "/".join(map(lambda x: str(x).rstrip('/'), args))

10voto

mwcz Points 2520

Le basejoin dans la fonction urllib pourrait être ce que vous recherchez.

basejoin = urljoin(base, url, allow_fragments=True)
    Join a base URL and a possibly relative URL to form an absolute
    interpretation of the latter.

Edit : Je ne l'avais pas remarqué avant, mais urllib.basejoin semble correspondre directement à urlparse.urljoin, ce qui rend ce dernier préférable.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X