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

amj*_*onn 84 python url

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

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

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

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

Ben*_*mes 136

Python2

>>> import urlparse
>>> urlparse.urljoin('/media/path/', 'js/foo.js')
'/media/path/js/foo.js'
Run Code Online (Sandbox Code Playgroud)

但要注意,

>>> import urlparse
>>> urlparse.urljoin('/media/path', 'js/foo.js')
'/media/js/foo.js'
Run Code Online (Sandbox Code Playgroud)

以及

>>> import urlparse
>>> urlparse.urljoin('/media/path', '/js/foo.js')
'/js/foo.js'
Run Code Online (Sandbox Code Playgroud)

Python3

>>> import urllib.parse
>>> urllib.parse.urljoin('/media/path/', 'js/foo.js')
'/media/path/js/foo.js'
Run Code Online (Sandbox Code Playgroud)

究其原因,你得到不同的结果/js/foo.jsjs/foo.js是因为前者始于这表示它已经开始在网站根斜线.

  • 在Python 3中,那就是`import urllib.parse`和`urllib.parse.urljoin`. (15认同)
  • urljoin有一些奇怪的行为,如果你要加入一个没有结束的组件/它将第一个组件剥离到它的基础然后加入其他args.不是我所期待的. (7认同)
  • 因此,我删除了 /js/foo.js 上的前导“/”,但 os.path.join 似乎也是如此。需要在媒体之后进行斜线意味着无论如何我都必须自己完成大部分工作。 (4认同)
  • 不幸的是,`urljoin`不是用于加入URL.它用于解析HTML文档等中的相对URL. (4认同)
  • @MedhatGayed我不清楚`urljoin`是否会删除'/'.如果我用`urlparse.urljoin('/ media /','/ js/foo.js')调用它,则返回值为'/js/foo.js'.它删除了所有媒体,而不是重复的'/'.实际上`urlparse.urljoin('/ media //','js/foo.js')`实际上返回'/media//js/foo.js',所以没有重复删除. (3认同)

Ale*_*lli 55

因为,从评论的OP发布,看来他希望保留在参加"绝对URL"(这是重点工作之一urlparse.urljoin;-),我建议你避免. os.path.join因为完全相同的原因,也会很糟糕.

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

  • 不,这在 Windows 上不起作用,其中 `os.path.join('http://media.com', 'content')` 将返回 `http://media.com\content`。 (2认同)

GP8*_*P89 44

就像你说的,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'
Run Code Online (Sandbox Code Playgroud)

所以你可以只导入和使用posixpath.joinurls,这是可用的,可以在任何平台上使用.

编辑: @ Pete的建议很好,你可以为导入设置别名以提高可读性

from posixpath import join as urljoin
Run Code Online (Sandbox Code Playgroud)

编辑:我认为这更清楚,或者至少帮助我理解,如果你查看源os.py代码(这里的代码来自Python 2.7.11,加上我修剪了一些位).这里有条件导入os.py选择在命名空间中使用哪个路径模块os.path.所有底层模块(posixpath,ntpath,os2emxpath,riscospath),其可以在进口os.py,别名为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'
Run Code Online (Sandbox Code Playgroud)

  • `from posixpath import join as urljoin`很好地将它别名化为易于阅读的东西. (4认同)
  • 这在Windows上并不明显,但确实如此. (3认同)

Run*_*ard 27

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

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))
Run Code Online (Sandbox Code Playgroud)


mwc*_*wcz 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.
Run Code Online (Sandbox Code Playgroud)

编辑:我之前没有注意到,但urllib.basejoin似乎直接映射到urlparse.urljoin,使后者成为首选.


Vas*_*cal 7

使用furl, pip install furl它将是:

 furl.furl('/media/path/').add(path='js/foo.js')
Run Code Online (Sandbox Code Playgroud)


cba*_*are 7

我发现上述所有解决方案都不受欢迎,所以我想出了自己的解决方案。此版本确保部件用单个斜杠连接,并单独留下前导斜杠和尾随斜杠。不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/'
Run Code Online (Sandbox Code Playgroud)


jmu*_*sch 5

我知道这比OP要求的要多,但是我拥有以下URL的组成部分,并且正在寻找一种简单的方法来加入它们:

>>> url = 'https://api.foo.com/orders/bartag?spamStatus=awaiting_spam&page=1&pageSize=250'
Run Code Online (Sandbox Code Playgroud)

环顾四周:

>>> 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'>
Run Code Online (Sandbox Code Playgroud)

因此,除了在其他答案中已经回答过的路径联接之外,为了获得我想要的东西,我还做了以下工作:

>>> 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'
Run Code Online (Sandbox Code Playgroud)

根据文档,它只需要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
Run Code Online (Sandbox Code Playgroud)

这允许连接所有参数,而不管尾部和结束斜杠,同时保留最后一个斜杠(如果存在)。

  • 您可以使用列表理解使最后一行更短、更Python化,例如: `return "/".join([str(x).strip("/") for x in args]) + Trailing_slash` (2认同)