Как присоединиться к компонентам пути при создании URL-адреса в Python
Например, я хочу присоединиться к пути префикса к пути к ресурсам, например /js/foo.js.
Я хочу, чтобы результирующий путь был относительно корня сервера. В приведенном выше примере, если префикс был "носителем", я хотел бы, чтобы результат был /media/js/foo.js.
os.path.join делает это действительно хорошо, но как он соединяет пути, зависит от ОС. В этом случае я знаю, что я нацелен на Интернет, а не на локальную файловую систему.
Есть ли лучшая альтернатива, когда вы работаете с путями, которые, как вы знаете, будут использоваться в URL-адресах? Будет ли os.path.join работать достаточно хорошо? Должен ли я просто рулон мой?
Ответы
Ответ 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/'