Как объединить компоненты пути при создании URL-адреса в Python


например, я хочу присоединить путь префикса к путям ресурсов, таким как /js/foo.js.

Я хочу, чтобы результирующий путь должен быть относительно корня сервера. В приведенном выше примере, если префикс был "media", я бы хотел, чтобы результат был /media/js/foo.js.

ОС.путь.join делает это очень хорошо,но как он соединяет пути зависит от ОС. В этом случае я знаю, что ориентируюсь на веб, а не на локальную файловую систему.

есть ли лучшая альтернатива, когда вы не работа с путями, которые вы знаете, будут использоваться в URL-адресах? Уилл ОС.путь.работа достаточно хорошо? Я должен просто свернуть свой собственный?

8 68

8 ответов:

поскольку, из комментариев, опубликованных OP, кажется, что он не хотите сохранить "абсолютные URL" в соединении (что является одним из ключевых заданий urlparse.urljoin; -), Я бы рекомендовал избегать этого. 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 это потому, что первый начинается с косой черты, которая означает, что он уже начинается в корне сайта.

как вы говорите, os.path.join соединяет пути на основе текущей ОС. posixpath является базовым модулем, который используется в системах posix под пространством имен os.path:

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

так что вы можете просто импортировать и использовать posixpath.join вместо URL, который доступен и будет работать на любой платформе.

Edit: @предложение Пита является хорошим, вы можете псевдоним импорта для увеличения читаемость

from posixpath import join as urljoin

Edit: я думаю, что это стало яснее, или, по крайней мере, помогло мне понять, если вы посмотрите в источник 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'

это делает работу хорошо:

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

на basejoin на 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.

Edit: я не заметил раньше, но 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'

По словам документация это занимает ровно 5 часть кортеж.

со следующим форматом кортежа:

схема 0 url спецификатор схемы пустая строка

netloc 1 сетевое расположение часть пустая строка

путь 2 иерархический путь пустая строка

запрос 3 компонент запроса пустая строка

фрагмент 4 идентификатор фрагмента пустая строка

чтобы немного улучшить ответ Алекса Мартелли, следующее будет не только очищать дополнительные косые черты, но и сохранять конечные (конечные) косые черты, которые иногда могут быть полезны :

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

это не так легко читать, хотя,и не будет очистки нескольких дополнительных конечных косых черт.