首页 文章

在Python中构建URL时如何加入路径的组件

提问于
浏览
70

例如,我想加入资源路径的前缀路径,如/js/foo.js .

我希望生成的路径相对于服务器的根目录 . 在上面的例子中,如果前缀是“media”,我希望结果是/media/js/foo.js .

os.path.join做得非常好,但它如何加入路径依赖于操作系统 . 在这种情况下,我知道我的目标是网络,而不是本地文件系统 .

当您使用您知道将在URL中使用的路径时,是否有最佳选择? os.path.join会运行得好吗?我应该自己滚吗?

8 回答

  • 109

    因为,从OP发布的评论来看,似乎他想要在联接中保留"absolute URLs"(这是 urlparse.urljoin ;-)的关键工作之一,我建议避免这样做 . 由于完全相同的原因, os.path.join 也会很糟糕 .

    所以,我会使用类似 '/'.join(s.strip('/') for s in pieces) 的东西(如果领先的 / 也必须被忽略 - 如果领先的作品必须是特殊的,那当然也是可行的;-) .

  • 19

    Python2

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

    But beware

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

    as well as

    >>> 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.jsjs/foo.js 获得不同结果的原因是因为前者以斜杠开头,表示它已经从网站根目录开始 .

  • 4

    就像你说的那样, os.path.join 根据当前操作系统加入路径 . posixpath 是在命名空间 os.path 下的posix系统上使用的基础模块:

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

    因此,您只需导入并使用 posixpath.join 代替网址,这是可用的,可以在任何平台上使用 .

    Edit: @ Pete的建议很好,您可以为导入添加别名以提高可读性

    from posixpath import join as urljoin
    

    Edit: 我认为这更清楚,或者至少帮助我理解,如果你查看 os.py 的源代码(这里的代码来自Python 2.7.11,加上've trimmed some bits). There'中的条件导入 os.py ,它选择使用哪个路径模块命名空间 os.path . 可以在 os.py 中导入的所有底层模块( posixpathntpathos2emxpathriscospath ,别名为 path )存在并且存在于所有系统中. 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'
    
  • 8

    这很好地完成了这项工作:

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

    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,使后者成为首选 .

  • 36

    使用furl, pip install furl 它将是:

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

    我知道这比OP要求的要多一些,但我有以下网址的部分,并且正在寻找一种简单的方式来加入它们:

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

    所以除了已经在其他答案中回答的路径连接之外, To get what I was looking for I did the following:

    >>> 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个元组 .

    使用以下元组格式:

    scheme 0 URL方案说明符空字符串netloc 1网络位置部分空字符串路径2分层路径空字符串查询3查询组件空字符串片段4片段标识符空字符串

  • 38

    为了略微改进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/
    

    虽然它并不容易阅读,也不会清除多个额外的尾部斜杠 .

相关问题