Подтвердить что ты не робот

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

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

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

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

Есть ли лучшая альтернатива, когда вы работаете с путями, которые, как вы знаете, будут использоваться в URL-адресах? Будет ли os.path.join работать достаточно хорошо? Должен ли я просто рулон мой?

4b9b3361

Ответ 1

Поскольку из комментариев, опубликованных OP, кажется, что не хочет сохранить абсолютные URL-адреса в соединении (что является одним из ключевых заданий urlparse.urljoin;-), Я бы рекомендовал это избежать. os.path.join также будет плохим, по той же причине.

Итак, я бы использовал что-то вроде '/'.join(s.strip('/') for s in pieces) (если ведущий / также должен быть проигнорирован - если ведущая часть должна быть специально обрезанной, это тоже возможно, -).

Ответ 2

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, состоит в том, что первая начинается с косой черты, которая означает, что она уже начинается с корня веб-сайта.

Ответ 3

Как вы говорите, 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.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

Это делает работу красиво:

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

Функция 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.

Изменить: раньше я не заметил, но urllib.basejoin, похоже, непосредственно отображается в urlparse.urljoin, что делает последнее предпочтительным.

Ответ 6

Используя furl, pip install furl, он будет выглядеть следующим образом:

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

Ответ 7

Я знаю, что это немного больше, чем запрашивал ОП, однако я привел фрагменты к следующему 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 идентификатор фрагмента пустая строка

Ответ 8

Чтобы немного улучшить ответ 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/

Это не так просто читать, и не будет очищать несколько лишних косых черт.

Ответ 9

Rune Kaagaard предоставил мне отличное и компактное решение, которое я сработал, я немного расширил его:

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

Это позволяет объединять все аргументы независимо от конечного и конечного слэша, сохраняя последний слеш, если он присутствует.

Ответ 10

Использование furl и regex (python 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'

Ответ 11

Я нашел вещи, которые не нравятся во всех вышеупомянутых решениях, поэтому я придумал свое собственное. Эта версия гарантирует, что части соединяются одним слешем и оставляют только начальные и конечные косые черты. Нет 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/'