Par exemple, je veux joindre un chemin de préfixe à des chemins de ressources comme /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 le fait 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 cible le Web, pas 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 les URL? Os.path.join fonctionnera-t-il assez bien? Dois-je simplement rouler le mien?
Puisque, d'après les commentaires postés par l'OP, il semble qu'il ne veut pas vouloir conserver les "URL absolues" dans la jointure (qui est l'un des emplois clés de urlparse.urljoin
;-) , Je recommanderais d'éviter cela. os.path.join
Serait également mauvais, pour exactement la même raison.
Donc, j'utiliserais quelque chose comme '/'.join(s.strip('/') for s in pieces)
(si le premier /
Doit également être ignoré - si le premier morceau doit être dans un boîtier spécial, c'est également possible bien sûr ;-).
Python2
>>> import urlparse
>>> urlparse.urljoin('/media/path/', 'js/foo.js')
'/media/path/js/foo.js'
Mais attention,
>>> import urlparse
>>> urlparse.urljoin('/media/path', 'js/foo.js')
'/media/js/foo.js'
ainsi que
>>> import urlparse
>>> urlparse.urljoin('/media/path', '/js/foo.js')
'/js/foo.js'
Python3
>>> import urllib.parse
>>> urllib.parse.urljoin('/media/path/', 'js/foo.js')
'/media/path/js/foo.js'
La raison pour laquelle vous obtenez des résultats différents de /js/foo.js
et js/foo.js
est parce que le premier commence par une barre oblique qui signifie qu'il commence déjà à la racine du site Web.
Comme tu dis, os.path.join
joint les chemins en fonction du système d'exploitation 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 URL, qui est disponible et fonctionnera sur n'importe quelle plateforme .
Edit: @ La suggestion de Pete est bonne, vous pouvez alias l'importation pour une meilleure lisibilité
from posixpath import join as urljoin
Edit: Je pense que c'est plus clair, ou du moins m'a aidé à comprendre, si vous regardez la source de os.py
(le code ici est de Python 2.7.11, plus j'ai coupé quelques bits). Il y a des importations conditionnelles dans os.py
qui sélectionne le module de chemin d'accès à utiliser dans l'espace de noms os.path
. Tous les modules sous-jacents (posixpath
, ntpath
, os2emxpath
, riscospath
) qui peuvent être importés dans os.py
, alias path
, existe et existe pour être utilisé sur tous les systèmes. os.py
ne fait que 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'
Cela fait bien le travail:
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))
La fonction basejoin du package rllib 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é auparavant, mais urllib.basejoin semble correspondre directement à urlparse.urljoin, ce qui rend ce dernier préféré.
Utilisation de furl, pip install furl
ce sera:
furl.furl('/media/path/').add(path='js/foo.js')
Je sais que c'est un peu plus que ce que l'OP demandait, mais j'avais les morceaux à l'url suivante, et je cherchais un moyen simple de les rejoindre:
>>> url = 'https://api.foo.com/orders/bartag?spamStatus=awaiting_spam&page=1&pageSize=250'
Faire quelques recherches:
>>> split = urlparse.urlsplit(url)
>>> split
SplitResult(scheme='https', netloc='api.foo.com', path='/orders/bartag', query='spamStatus=awaiting_spam&page=1&pageSize=250', fragment='')
>>> type(split)
<class 'urlparse.SplitResult'>
>>> dir(split)
['__add__', '__class__', '__contains__', '__delattr__', '__dict__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__getslice__', '__getstate__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__module__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmul__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', '__weakref__', '_asdict', '_fields', '_make', '_replace', 'count', 'fragment', 'geturl', 'hostname', 'index', 'netloc', 'password', 'path', 'port', 'query', 'scheme', 'username']
>>> split[0]
'https'
>>> split = (split[:])
>>> type(split)
<type 'Tuple'>
Donc, en plus du chemin d'accès qui a déjà été répondu dans les autres réponses, Pour obtenir ce que je cherchais, j'ai fait ce qui suit:
>>> split
('https', 'api.foo.com', '/orders/bartag', 'spamStatus=awaiting_spam&page=1&pageSize=250', '')
>>> unsplit = urlparse.urlunsplit(split)
>>> unsplit
'https://api.foo.com/orders/bartag?spamStatus=awaiting_spam&page=1&pageSize=250'
Selon la documentation il faut EXACTEMENT un Tuple en 5 parties.
Avec le format Tuple suivant:
schéma 0 URL spécificateur de schéma chaîne vide
netloc 1 Network location part chaîne vide
chemin 2 Chaîne vide du chemin hiérarchique
requête 3 Chaîne vide du composant de requête
fragment 4 Identificateur de fragment chaîne vide
Pour améliorer légèrement la réponse d'Alex Martelli, ce qui suit non seulement nettoiera les barres obliques supplémentaires, mais préservera également les barres obliques de fin (qui se terminent), ce qui peut parfois être utile:
>>> items = ["http://www.website.com", "/api", "v2/"]
>>> url = "/".join([(u.strip("/") if index + 1 < len(items) else u.lstrip("/")) for index, u in enumerate(items)])
>>> print(url)
http://www.website.com/api/v2/
Ce n'est pas aussi facile à lire et ne nettoiera pas plusieurs barres obliques supplémentaires.
Rune Kaagaard a fourni une excellente solution compacte qui a fonctionné pour moi, je l'ai développée un peu:
def urljoin(*args):
trailing_slash = '/' if args[-1].endswith('/') else ''
return "/".join(map(lambda x: str(x).strip('/'), args)) + trailing_slash
Cela permet à tous les arguments d'être joints indépendamment des barres obliques de fin et de fin tout en conservant la dernière barre oblique si elle est présente.