programing

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

abcjava 2023. 6. 25. 18:11
반응형

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

예를 들어 /js/foo.js와 같은 리소스 경로의 접두사 경로에 가입하려고 합니다.

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

os.path.join은 이를 매우 잘 수행하지만 경로에 가입하는 방법은 OS에 따라 다릅니다.이 경우에는 로컬 파일 시스템이 아닌 웹을 대상으로 합니다.

URL에서 사용할 경로를 알고 작업할 때 가장 좋은 대안이 있습니까?will os.path.join은 충분히 잘 작동합니까?제가 직접 굴릴까요?

다음을 사용할 수 있습니다.

>>> 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.js그리고.js/foo.js전자가 이미 웹 사이트 루트에서 시작됨을 나타내는 슬래시로 시작하기 때문입니다.

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

from urlparse import urljoin

OP가 올린 댓글을 보면, 그는 조인에서 "절대 URL"을 보존하고 싶지 않은 것 같습니다(이것은 주요 작업 중 하나입니다).urlparse.urljoin;-), 저는 그것을 피하는 것을 추천합니다. os.path.join또한 좋지 않을 것입니다, 정확히 같은 이유로.

그래서, 저는 그런 것을 사용합니다.'/'.join(s.strip('/') for s in pieces)에 있는 경우 (선행는경우하경))/또한 무시해야 합니다. -- 만약 주연 작품이 특별한 경우에도 당연히 가능합니다;-).

처럼, 네말로대,로.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 모듈module)posixpath,ntpath,os2emxpath,riscospath할 수 입니다.os.py로서 별칭이 붙은.path모든 시스템에서 사용할 수 있도록 존재합니다. os.py에 사용할 모듈 중 하나를 입니다.os.path현재 OS를 기준으로 실행 시.

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

저는 위의 모든 솔루션에 대해 마음에 들지 않는 점을 발견하고 저만의 솔루션을 생각해 냈습니다.이 버전에서는 부품이 단일 슬래시로 결합되고 선행 슬래시와 후행 슬래시만 남게 됩니다.아니요.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/'

urllib 패키지의 기본 조인 기능이 당신이 찾고 있는 것일 수 있습니다.

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에 직접 매핑되어 후자를 선호하는 것 같습니다.

사용해서, 서해이용을털,,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'

설명서에 따르면 정확히 5개의 파트 태플이 필요합니다.

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

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

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

경로 2 계층 경로 빈 문자열

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

fragment 4 fragment 식별자 빈 문자열

Rune Kaagard는 저에게 효과가 있는 훌륭하고 컴팩트한 솔루션을 제공했습니다. 저는 이에 대해 조금 더 자세히 설명했습니다.

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

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

다음은 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/

하지만 읽기가 그렇게 쉽지 않고 추가 후행 슬래시를 정리하지도 않습니다.

이거 어때:어느 정도 효율적이고 어느 정도 단순합니다.url 경로의 '2' 부분만 가입하면 됩니다.

def UrlJoin(a , b):
    a, b = a.strip(), b.strip()
    a = a if a.endswith('/') else a + '/'
    b = b if not b.startswith('/') else b[1:]
    return a + b

OR: 일반적이지만 경로의 URL 부분을 2개만 결합하면 효율성이 떨어집니다.

def UrlJoin(*parts):
    return '/'.join([p.strip().strip('/') for p in parts])

테스트 사례:

>>> UrlJoin('https://example.com/', '/TestURL_1')
'https://example.com/TestURL_1'

>>> UrlJoin('https://example.com', 'TestURL_2')
'https://example.com/TestURL_2'

참고: 여기서 머리를 쪼개고 있을 수도 있지만, 적어도 좋은 방법이며 잠재적으로 더 읽기 쉽습니다.

full정규식 사용(피톤 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'

라이너 하나:

from functools import reduce
reduce(lambda x,y: '{}/{}'.format(x,y), parts) 

여기서 부품은 ['https://api.somecompany.com/v1', '날씨', '비']입니다.

제가 사용하고 있는 안전한 버전이 있습니다.접두사와 후행 슬래시를 처리합니다.끝 URI에 대한 후행 슬래시는 별도로 처리됩니다.

def safe_urljoin(*uris) -> str:
    """
    Joins the URIs carefully considering the prefixes and trailing slashes.
    The trailing slash for the end URI is handled separately.
    """
    if len(uris) == 1:
        return uris[0]

    safe_urls = [
        f"{url.lstrip('/')}/" if not url.endswith("/") else url.lstrip("/")
        for url in uris[:-1]
    ]
    safe_urls.append(uris[-1].lstrip("/"))
    return "".join(safe_urls)

산출물

>>> safe_urljoin("https://a.com/", "adunits/", "/both/", "/left")
>>> 'https://a.com/adunits/both/left'

>>> safe_urljoin("https://a.com/", "adunits/", "/both/", "right/")
>>> 'https://a.com/adunits/both/right/'

>>> safe_urljoin("https://a.com/", "adunits/", "/both/", "right/", "none")
>>> 'https://a.com/adunits/both/right/none'

>>> safe_urljoin("https://a.com/", "adunits/", "/both/", "right/", "none/")
>>> 'https://a.com/adunits/both/right/none/'

고유한 기능을 가진 또 다른 변형:

def urljoin(base:str, *parts:str) -> str:
    for part in filter(None, parts):
        base = '{}/{}'.format(base.rstrip('/'), part.lstrip('/'))
    return base
  • 후행 슬래시를 기본 또는 마지막 부분에 유지
  • 빈 부품은 무시됩니다.
  • 비어 있지 않은 각 부품에 대해 베이스에서 후행을 제거하고 부품에서 선행을 제거한 후 단일 부품으로 결합합니다./
urljoin('http://a.com/api',  '')  -> 'http://a.com/api'
urljoin('http://a.com/api',  '/') -> 'http://a.com/api/'
urljoin('http://a.com/api/', '')  -> 'http://a.com/api/'
urljoin('http://a.com/api/', '/') -> 'http://a.com/api/'
urljoin('http://a.com/api/', '/a/', '/b', 'c', 'd/') -> 'http://a.com/api/a/b/c/d/'

네, 제가 한 일이 바로 그거예요. 미리 정의된 뿌리로부터 완전히 독립해야 했기 때문입니다.

def url_join(base: str, *components: str, slash_left=True, slash_right=True) -> str:
    """Join two or more url components, inserting '/' as needed.
    Optionally, a slash can be added to the left or right side of the URL.
    """
    base = base.lstrip('/').rstrip('/')
    components = [component.lstrip('/').rstrip('/') for component in components]
    url = f"/{base}" if slash_left else base
    for component in components:
        url = f"{url}/{component}" 
    return f"{url}/" if slash_right else url

url_join("http://whoops.io", "foo/", "/bar", "foo", slash_left=False)
# "http://whoops.io/foo/bar/foo/"
url_join("foo", "bar")
# "/foo/bar/""

언급URL : https://stackoverflow.com/questions/1793261/how-to-join-components-of-a-path-when-you-are-constructing-a-url-in-python

반응형