Python: как разрешить URL, содержащие '..' - PullRequest
7 голосов
/ 30 ноября 2010

Мне нужно однозначно идентифицировать и хранить некоторые URL-адреса.Проблема в том, что иногда они содержат "..", например http://somedomain.com/foo/bar/../../some/url, что в принципе равно http://somedomain.com/some/url, если я не ошибаюсь.

Есть ли функция Python или хитрый способ разрешить эти URL?

Ответы [ 6 ]

11 голосов
/ 30 ноября 2010

Существует простое решение, использующее urlparse .urljoin:

>>> import urlparse
>>> urlparse.urljoin('http://www.example.com/foo/bar/../../baz/bux/', '.')
'http://www.example.com/baz/bux/'

Однако, если косой черты нет (последний компонент - это файл, а не каталог), последний компонент будет удален.

Это исправление использует функцию urlparse для извлечения пути, а затем использует (версия posixpath) os.path для нормализации компонентов. Компенсировать загадочную проблему с завершающими косыми чертами , затем соединить URL-адрес вместе. doctest способен:

import urlparse
import posixpath

def resolveComponents(url):
    """
    >>> resolveComponents('http://www.example.com/foo/bar/../../baz/bux/')
    'http://www.example.com/baz/bux/'
    >>> resolveComponents('http://www.example.com/some/path/../file.ext')
    'http://www.example.com/some/file.ext'
    """

    parsed = urlparse.urlparse(url)
    new_path = posixpath.normpath(parsed.path)
    if parsed.path.endswith('/'):
        # Compensate for issue1707768
        new_path += '/'
    cleaned = parsed._replace(path=new_path)
    return cleaned.geturl()
4 голосов
/ 30 ноября 2010

Это пути к файлам.Посмотрите на os.path.normpath :

>>> import os
>>> os.path.normpath('/foo/bar/../../some/url')
'/some/url'

РЕДАКТИРОВАТЬ:

Если это в Windows, ваш путь ввода будет использовать обратную косую черту вместокосых черт.В этом случае вам по-прежнему нужно os.path.normpath, чтобы избавиться от шаблонов ..// и /./ и всего остального, что является избыточным), а затем преобразовать обратные слэши в прямые косые черты:

def fix_path_for_URL(path):
    result = os.path.normpath(path)
    if os.sep == '\\':
        result = result.replace('\\', '/')
    return result

РЕДАКТИРОВАТЬ 2:

Если вы хотите нормализовать URL-адреса, сделайте это (до того, как вы удалите метод и т.п.) с помощью модуля urlparse , как показано в1023 * ответ на этот вопрос .

РЕДАКТИРОВАТЬ 3:

Кажется, что urljoin не нормализует базузаданный путь:

>>> import urlparse
>>> urlparse.urljoin('http://somedomain.com/foo/bar/../../some/url', '')
'http://somedomain.com/foo/bar/../../some/url'

normpath сам по себе тоже не совсем обрезает:

>>> import os
>>> os.path.normpath('http://somedomain.com/foo/bar/../../some/url')
'http:/somedomain.com/some/url'

Обратите внимание, что начальная двойная косая черта была съедена.

Итакмы должны заставить их объединить свои силы:

def fix_URL(urlstring):
    parts = list(urlparse.urlparse(urlstring))
    parts[2] = os.path.normpath(parts[2].replace('/', os.sep)).replace(os.sep, '/')
    return urlparse.urlunparse(parts)

Использование:

>>> fix_URL('http://somedomain.com/foo/bar/../../some/url')
'http://somedomain.com/some/url'
2 голосов
/ 01 ноября 2012

Я хотел бы прокомментировать функцию resolveComponents в верхнем ответе.

Обратите внимание: если ваш путь /, код добавит еще один, который может быть проблематичным Поэтому я изменил условие IF на:

if parsed.path.endswith( '/' ) and parsed.path != '/':
1 голос
/ 10 ноября 2016

urljoin не будет работать , поскольку он разрешает точечные сегменты, только если второй аргумент не является абсолютным (!?) или пустым.Мало того, он не обрабатывает чрезмерно .. с в соответствии с RFC 3986 (они должны быть удалены; urljoin не делает этого).posixpath.normpath также нельзя использовать (намного меньше os.path.normpath), поскольку он разрешает несколько косых черт в строке только в одну (например, ///// становится /), что является неправильным поведением для URL-адресов.


Следующая короткая функция правильно разрешает любую строку пути URL-адреса. Однако не следует использовать с относительными путями , поскольку в этом случае потребуются дополнительные решения относительно ее поведения.made (вызвать ошибку при чрезмерном .. s? Вначале удалить .? Оставить их обоих?) - вместо этого объедините URL-адреса, прежде чем разрешать, если вы знаете, что можете обрабатывать относительные пути.*

Это обрабатывает сегменты конечных точек (то есть без завершающей косой черты) и правильных последовательных слешей. Чтобы разрешить весь URL-адрес, вы можете использовать следующую оболочку (или просто встроенную функцию разрешения путив это).

try:
    # Python 3
    from urllib.parse import urlsplit, urlunsplit
except ImportError:
    # Python 2
    from urlparse import urlsplit, urlunsplit

def resolve_url(url):
    parts = list(urlsplit(url))
    parts[2] = resolve_url_path(parts[2])
    return urlunsplit(parts)

Затем вы можете назвать это так:

>>> resolve_url('http://example.com/../thing///wrong/../multiple-slashes-yeah/.')
'http://example.com/thing///multiple-slashes-yeah/'

Правильное разрешение URL имеет более чем несколько ловушек, оказывается!

0 голосов
/ 17 октября 2017
import urlparse
import posixpath

parsed = list(urlparse.urlparse(url))
parsed[2] = posixpath.normpath(posixpath.join(parsed[2], rel_path))
proper_url = urlparse.urlunparse(parsed)
0 голосов
/ 25 декабря 2013

Согласно RFC 3986 это должно происходить как часть процесса "относительного разрешения". Таким образом, ответ может быть urlparse.urljoin(url, ''). Но из-за ошибки urlparse.urljoin не удаляет точечные сегменты, когда вторым аргументом является пустой URL. Вы можете использовать yurl - альтернативную библиотеку манипулирования URL. Делай это правильно:

>>> import yurl
>>> print yurl.URL('http://somedomain.com/foo/bar/../../some/url') + yurl.URL()
http://somedomain.com/some/url
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...