Python에서 URL을 생성 할 때 경로의 구성 요소를 결합하는 방법


103

예를 들어 /js/foo.js와 같은 리소스 경로에 대한 접두사 경로를 결합하고 싶습니다.

결과 경로가 서버의 루트에 상대적 이길 원합니다. 위의 예에서 접두사가 "media"이면 결과는 /media/js/foo.js가됩니다.

os.path.join은이 작업을 정말 잘 수행하지만 경로를 연결하는 방법은 OS에 따라 다릅니다. 이 경우 로컬 파일 시스템이 아닌 웹을 대상으로한다는 것을 알고 있습니다.

URL에서 사용되는 경로로 작업 할 때 가장 좋은 대안이 있습니까? os.path.join이 충분히 잘 작동합니까? 내가 직접 굴려야하나요?


1
os.path.join작동 안 할 것이다. 그러나 단순히 /문자로 결합하는 것은 모든 경우에 작동해야합니다 /. 사양에 따라 HTTP의 표준 경로 구분 기호입니다.
intgr

답변:


60

OP가 게시 한 댓글에서 그는 조인 ( ;- 의 핵심 작업 중 하나)에서 "절대 URL"을 보존하고 싶지 않은urlparse.urljoin같으 므로 피하는 것이 좋습니다. os.path.join정확히 같은 이유로 나쁠 것입니다.

그래서 나는 다음과 같은 것을 사용할 것입니다. '/'.join(s.strip('/') for s in pieces)(리딩 /도 무시해야한다면-리딩 부분이 특수한 경우라면 당연히 가능합니다 ;-).


1
감사. 두 번째 부분의 선행 '/'가있을 수 없다는 것을 너무 많이 요구하지 않았지만 첫 번째 부분의 후행 '/'를 요구하면이 사용 사례에서 urljoin이 아무것도하지 않는 것처럼 느끼게됩니다. 나를 위해. 적어도 join ( "/ media", "js / foo.js") 및 join ( "/ media /", "js / foo.js")가 작동하고 싶습니다. 정답으로 보이는 것에 감사드립니다. 직접 굴려보세요.
amjoconn

나는 무언가가 나를 위해 '/'스트리핑과 결합을 할 수 있기를 바랐다.
statueofmike

아니, 이것은 os.path.join('http://media.com', 'content')wourd가 반환 하는 창문에서는 작동하지 않습니다 http://media.com\content.
SeF

154

다음을 사용할 수 있습니다 urllib.parse.urljoin.

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

그러나 조심하십시오 :

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

그 이유는 당신은 다른 결과를 얻을 /js/foo.jsjs/foo.js전자는 이미 웹 사이트의 루트에서 시작 것을 의미 슬래시로 시작하기 때문이다.

Python 2에서는 다음을 수행해야합니다.

from urlparse import urljoin

그래서 나는 /js/foo.js에서 선행 "/"를 벗어 났지만 os.path.join에서도 마찬가지인 것 같습니다. 미디어 다음에 슬래시를 요구한다는 것은 어쨌든 대부분의 작업을 직접해야한다는 것을 의미합니다.
amjoconn

특히 접두사가 /로 끝나야하고 대상 경로가 /로 시작될 수 없다는 것을 알게되면 연결하는 것이 좋습니다. 이 경우 urljoin이 정말로 도움이되는지 확실하지 않습니까?
amjoconn

3
@MedhatGayed urljoin'/'를 제거 하는 것이 분명하지 않습니다 . urlparse.urljoin('/media/', '/js/foo.js')반환 값으로 호출하면 '/js/foo.js'입니다. 중복 된 '/'가 아닌 모든 미디어를 제거했습니다. 실제로 urlparse.urljoin('/media//', 'js/foo.js')실제로 '/media//js/foo.js'를 반환하므로 중복 항목이 제거되지 않습니다.
amjoconn 2014

8
urljoin은 끝나지 않는 구성 요소를 결합하는 경우 이상한 동작을합니다 ./ 첫 번째 구성 요소를 기본 구성 요소로 제거한 다음 다른 인수를 결합합니다. 내가 기대하는 것이 아닙니다.
Pete

7
안타깝게도 urljoinURL 결합이 아닙니다. 그것은 HTML 문서 등에있는 상대 URL을 해결하기위한 것입니다.
OrangeDog

46

말했듯 os.path.join이 현재 OS를 기반으로 경로를 결합합니다. posixpath네임 스페이스 아래 posix 시스템에서 사용되는 기본 모듈입니다 os.path.

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

따라서 사용 posixpath.join가능하며 모든 플랫폼에서 작동하는 URL 대신 가져 와서 사용할 수 있습니다 .

편집 : @Pete의 제안은 좋은 것입니다. 가독성을 높이기 위해 가져 오기를 별칭으로 지정할 수 있습니다.

from posixpath import join as urljoin

편집 : 소스를 살펴보면 이것이 더 명확 해졌거나 적어도 이해하는 데 도움이되었다고 생각합니다 os.py(여기 코드는 Python 2.7.11에서 가져온 것입니다. os.py네임 스페이스에서 사용할 경로 모듈을 선택하는 조건부 가져 오기가 있습니다 os.path. 모든 기본 모듈 ( posixpath, ntpath, os2emxpath, riscospath)에서 가져올 수 os.py별칭으로, path, 거기 모든 시스템에서 사용할 수 존재한다. 현재 OS를 기반으로 런타임에 os.py네임 스페이스 os.path에서 사용할 모듈 중 하나를 선택하는 것입니다 .

# 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'

4
from posixpath import join as urljoin읽기 쉬운 것으로 멋지게 별칭을 지정합니다.
Pete

29

이것은 일을 훌륭하게 수행합니다.

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))

9

urllib 패키지 의 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에 직접 매핑되어 후자가 선호됩니다.


9

furl을 사용 pip install furl하면 다음과 같습니다.

 furl.furl('/media/path/').add(path='js/foo.js')

1
당신이 결과가 문자열 싶은 경우에 당신은 추가 할 수 있습니다 .url말 :furl.furl('/media/path/').add(path='js/foo.js').url
Eyal 님 레빈

furl은 python 2 atleast (y)의 urlparse.urljoin에 비해 URL 결합에서 더 잘 작동합니다.
Ciasto piekarz

그것은 수행하는 것이 좋습니다 furl('/media/path/').add(path=furl('/js/foo.js').path).url때문 furl('/media/path/').add(path='/js/foo.js').url입니다/media/path//js/foo.js
BARTOLO - otrit

5

나는 이것이 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'

문서 에 따르면 정확히 5 부분 튜플이 필요합니다.

다음 튜플 형식을 사용합니다.

체계 0 URL 체계 지정자 빈 문자열

netloc 1 네트워크 위치 부분 빈 문자열

경로 2 계층 적 경로 빈 문자열

쿼리 3 쿼리 구성 요소 빈 문자열

조각 4 조각 식별자 빈 문자열


5

Rune Kaagaard는 저에게 효과가있는 훌륭하고 간결한 솔루션을 제공했으며 약간 확장했습니다.

def urljoin(*args):
    trailing_slash = '/' if args[-1].endswith('/') else ''
    return "/".join(map(lambda x: str(x).strip('/'), args)) + trailing_slash

이렇게하면 마지막 슬래시가있는 경우 유지하면서 후행 및 종료 슬래시에 관계없이 모든 인수를 결합 할 수 있습니다.


다음과 같이 목록 이해력을 사용하여 마지막 줄을 조금 더 짧고 더 Pythonic하게 만들 수 있습니다.return "/".join([str(x).strip("/") for x in args]) + trailing_slash
Dan Coates

3

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/

그러나 읽기가 쉽지 않으며 여러 개의 추가 슬래시를 정리하지 않습니다.


3

위의 모든 솔루션에서 마음에 들지 않는 것을 발견하여 내 솔루션을 생각해 냈습니다. 이 버전은 부품이 단일 슬래시로 결합되도록하고 선행 및 후행 슬래시 만 남겨 둡니다. 아니 pip install, 이상하지 않습니다 urllib.parse.urljoin.

In [1]: from functools import reduce

In [2]: def join_slash(a, b):
   ...:     return a.rstrip('/') + '/' + b.lstrip('/')
   ...:

In [3]: def urljoin(*args):
   ...:     return reduce(join_slash, args) if args else ''
   ...:

In [4]: parts = ['https://foo-bar.quux.net', '/foo', 'bar', '/bat/', '/quux/']

In [5]: urljoin(*parts)
Out[5]: 'https://foo-bar.quux.net/foo/bar/bat/quux/'

In [6]: urljoin('https://quux.com/', '/path', 'to/file///', '//here/')
Out[6]: 'https://quux.com/path/to/file/here/'

In [7]: urljoin()
Out[7]: ''

In [8]: urljoin('//','beware', 'of/this///')
Out[8]: '/beware/of/this///'

In [9]: urljoin('/leading', 'and/', '/trailing/', 'slash/')
Out[9]: '/leading/and/trailing/slash/'

0

사용 북마크 공유정규식 (파이썬 3)

>>> import re
>>> import furl
>>> p = re.compile(r'(\/)+')
>>> url = furl.furl('/media/path').add(path='/js/foo.js').url
>>> url
'/media/path/js/foo.js'
>>> p.sub(r"\1", url)
'/media/path/js/foo.js'
>>> url = furl.furl('/media/path').add(path='js/foo.js').url
>>> url
'/media/path/js/foo.js'
>>> p.sub(r"\1", url)
'/media/path/js/foo.js'
>>> url = furl.furl('/media/path/').add(path='js/foo.js').url
>>> url
'/media/path/js/foo.js'
>>> p.sub(r"\1", url)
'/media/path/js/foo.js'
>>> url = furl.furl('/media///path///').add(path='//js///foo.js').url
>>> url
'/media///path/////js///foo.js'
>>> p.sub(r"\1", url)
'/media/path/js/foo.js'
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.