Разбор URL в Python - нормализация двойной косой черты в путях - PullRequest
5 голосов
/ 19 января 2012

Я работаю над приложением, которое должно анализировать URL-адреса (в основном HTTP-URL) на страницах HTML - я не контролирую ввод, и некоторые из них, как и ожидалось, немного запутаны.

Одна проблема, с которой я часто сталкиваюсь, заключается в том, что urlparse является очень строгим (и, возможно, даже ошибочным?), Когда дело доходит до парсинга и объединения URL-адресов с двойной косой чертой в пути, например:

testUrl = 'http://www.example.com//path?foo=bar'
urlparse.urljoin(testUrl, 
                 urlparse.urlparse(testUrl).path)

Вместо ожидаемого результата http://www.example.com//path (или даже лучше, с нормализованной одиночной косой чертой) я получаю http://path.

Кстати, причина, по которой я запускаю такой код, заключается в том, что пока я нашел единственный способ удалить часть запроса / фрагмента из URL.Может быть, есть лучший способ сделать это, но я не смог его найти.

Кто-нибудь может порекомендовать способ избежать этого, или я должен просто нормализовать путь, используя (я знаю, относительно простое) регулярное выражение?

Ответы [ 7 ]

6 голосов
/ 19 января 2012

Путь (//path) один недопустим, что приводит к путанице в функции и интерпретируется как имя хоста

http://tools.ietf.org/html/rfc3986.html#section-3.3

Если URI не содержитКомпонент полномочий, тогда путь не может начинаться с двух символов косой черты ("//").

Мне не особо нравится ни одно из этих решений, но они работают:

import re
import urlparse

testurl = 'http://www.example.com//path?foo=bar'

parsed = list(urlparse.urlparse(testurl))
parsed[2] = re.sub("/{2,}", "/", parsed[2]) # replace two or more / with one
cleaned = urlparse.urlunparse(parsed)

print cleaned
# http://www.example.com/path?foo=bar

print urlparse.urljoin(
    testurl, 
    urlparse.urlparse(cleaned).path)

# http://www.example.com//path

В зависимости от того, что вы делаете, вы можете выполнить соединение вручную:

import re
import urlparse

testurl = 'http://www.example.com//path?foo=bar'
parsed = list(urlparse.urlparse(testurl))

newurl = ["" for i in range(6)] # could urlparse another address instead

# Copy first 3 values from
# ['http', 'www.example.com', '//path', '', 'foo=bar', '']
for i in range(3):
    newurl[i] = parsed[i]

# Rest are blank
for i in range(4, 6):
    newurl[i] = ''

print urlparse.urlunparse(newurl)
# http://www.example.com//path
4 голосов
/ 19 января 2012

Если вы хотите получить только URL без части запроса, я пропущу модуль urlparse и просто сделаю:

testUrl.rsplit('?')

URL будет по индексу 0 возвращенного списка, а запрос будетindex 1.

Невозможно иметь два '?'в URL, поэтому он должен работать для всех URL.

2 голосов
/ 19 января 2012

В официальных документах по URL-адресу упоминается , что:

Если URL-адрес является абсолютным URL (т. Е. Начинается с // или схема: //), то URL-адресимя хоста и / или схема будут присутствовать в результате.Например,

urljoin('http://www.cwi.nl/%7Eguido/Python.html',
...         '//www.python.org/%7Eguido')
'http://www.python.org/%7Eguido'

Если вы не хотите, чтобы такое поведение, предварительно обработайте URL с помощью urlsplit () и urlunsplit (), удалив возможные части схемы и netloc.

Так что вы можете сделать:

urlparse.urljoin(testUrl,
             urlparse.urlparse(testUrl).path.replace('//','/'))

Выход = 'http://www.example.com/path'

0 голосов
/ 05 декабря 2018

Я согласился с моими потребностями @ yunhasnawa ответ.вот часть:

import urllib2
from urlparse import urlparse, urlunparse

def sanitize_url(url):
    url_parsed = urlparse(url)  
    return urlunparse((url_parsed.scheme, url_parsed.netloc, avoid_double_slash(url_parsed.path), '', '', ''))

def avoid_double_slash(path):
  parts = path.split('/')
  not_empties = [part for part in parts if part]
  return '/'.join(not_empties)


>>> sanitize_url('https://hostname.doma.in:8443/complex-path////next//')
'https://hostname.doma.in:8443/complex-path/next'
0 голосов
/ 29 июня 2018

Этот ответ , похоже, дал лучшие результаты в случаях, когда я пытался исправить двойную косую черту в путях, не касаясь начальной двойной косой черты в http: // bit.

вот код:

from urlparse import urljoin
from functools import reduce


def slash_join(*args):
    return reduce(urljoin, args).rstrip("/")
0 голосов
/ 24 октября 2015

Попробуйте:

def http_normalize_slashes(url):
    url = str(url)
    segments = url.split('/')
    correct_segments = []
    for segment in segments:
        if segment != '':
            correct_segments.append(segment)
    first_segment = str(correct_segments[0])
    if first_segment.find('http') == -1:
        correct_segments = ['http:'] + correct_segments
    correct_segments[0] = correct_segments[0] + '/'
    normalized_url = '/'.join(correct_segments)
    return normalized_url

Примеры URL:

print(http_normalize_slashes('http://www.example.com//path?foo=bar'))
print(http_normalize_slashes('http:/www.example.com//path?foo=bar'))
print(http_normalize_slashes('www.example.com//x///c//v///path?foo=bar'))
print(http_normalize_slashes('http://////www.example.com//x///c//v///path?foo=bar'))

Вернется:

http://www.example.com/path?foo=bar
http://www.example.com/path?foo=bar
http://www.example.com/x/c/v/path?foo=bar
http://www.example.com/x/c/v/path?foo=bar

Надеюсь, это поможет ..:)

0 голосов
/ 19 января 2012

Разве это не может быть решением?

urlparse.urlparse(testUrl).path.replace('//', '/')
...