たとえば、プレフィックスパスを/js/foo.jsなどのリソースパスに結合したいと思います。
結果のパスは、サーバーのルートを基準にしたいです。上記の例では、プレフィックスが「media」の場合、結果は/media/js/foo.jsになります。
os.path.joinはこれを本当にうまく行いますが、パスを結合する方法はOSに依存します。この場合、ローカルファイルシステムではなく、Webをターゲットにしていることがわかります。
URLで使用されることがわかっているパスを使用している場合、最適な代替手段はありますか? os.path.joinは十分に機能しますか?自分で転がす必要がありますか?
OPが投稿したコメントから、彼はdoes n't結合で「絶対URL」を保持したいようです(これは_urlparse.urljoin
_;-の主要な仕事の1つです)。それを避けることをお勧めします。 _os.path.join
_も、まったく同じ理由で悪いでしょう。
したがって、私は'/'.join(s.strip('/') for s in pieces)
のようなものを使用します(先頭の_/
_も無視する必要がある場合-先頭の部分を特殊なケースにする必要がある場合は、もちろん実現可能です;-)。
Python2
>>> import urlparse
>>> urlparse.urljoin('/media/path/', 'js/foo.js')
'/media/path/js/foo.js'
ただし注意、
>>> import urlparse
>>> urlparse.urljoin('/media/path', 'js/foo.js')
'/media/js/foo.js'
同様に
>>> 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'
/js/foo.js
とjs/foo.js
で異なる結果が得られる理由は、前者が既にWebサイトのルートで始まることを示すスラッシュで始まるためです。
あなたが言うように、os.path.join
は現在のOSに基づいてパスを結合します。 posixpath
は、名前空間os.path
の下でposixシステムで使用される基礎となるモジュールです。
>>> os.path.join is posixpath.join
True
>>> posixpath.join('/media/', 'js/foo.js')
'/media/js/foo.js'
そのため、URLの代わりにposixpath.join
をインポートして使用することができます。これは利用可能であり、任意のプラットフォームで動作します。
編集: @Peteの提案は良いものです。読みやすさを高めるためにインポートをエイリアスできます
from posixpath import join as urljoin
編集:os.py
(ここのコードはPython 2.7からのソースを調べると、これがより明確になり、少なくとも理解に役立ったと思います。 11、プラス私はいくつかのビットをトリミングしました)。 os.py
には、名前空間os.path
で使用するパスモジュールを選択する条件付きインポートがあります。 posixpath
としてエイリアスされたos2emxpath
にインポートできるすべての基礎モジュール(ntpath
、riscospath
、os.py
、path
)がありますすべてのシステムで使用するために存在します。 os.py
は、現在のOSに基づいて実行時に名前空間os.path
で使用するモジュールの1つを選択しています。
# 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'
これは仕事をうまくします:
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))
rllibパッケージのbasejoin関数は、あなたが探しているものかもしれません。
basejoin = urljoin(base, url, allow_fragments=True)
Join a base URL and a possibly relative URL to form an absolute
interpretation of the latter.
編集:以前は気づいていませんでしたが、urllib.basejoinはurlparse.urljoinに直接マップされているようで、後者が優先されます。
Furlを使用して、pip install furl
そうなる:
furl.furl('/media/path/').add(path='js/foo.js')
私はこれがOPが要求したものよりも少し大きいことを知っていますが、次のURLにピースがあり、それらを結合する簡単な方法を探していました:
>>> url = 'https://api.foo.com/orders/bartag?spamStatus=awaiting_spam&page=1&pageSize=250'
周りを見回してやる:
>>> 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'>
したがって、他の回答ですでに回答されているパス参加に加えて、私が探していたものを取得するために、私は次のことをしました:
>>> 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'
documentation によれば、まさに5部のタプルが必要です。
次のタプル形式:
スキーム0 URLスキーム指定子空文字列
netloc 1ネットワークロケーションパーツの空の文字列
パス2階層パスの空の文字列
query 3クエリコンポーネントの空の文字列
フラグメント4フラグメント識別子の空の文字列
Alex Martelliの応答をわずかに改善するために、以下は余分なスラッシュをクリーンアップするだけでなく、末尾の(終了)スラッシュも保持します。
>>> 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/
しかし、読むのは簡単ではなく、複数の余分な末尾のスラッシュをクリーンアップしません。
Rune Kaagaardは、私にとってうまく機能する素晴らしいコンパクトなソリューションを提供してくれたので、少し拡張しました。
def urljoin(*args):
trailing_slash = '/' if args[-1].endswith('/') else ''
return "/".join(map(lambda x: str(x).strip('/'), args)) + trailing_slash
これにより、末尾のスラッシュと終了のスラッシュに関係なく、最後のスラッシュを保持しながらすべての引数を結合できます。