2012-01-19 7 views
5

Estoy trabajando en una aplicación que necesita analizar URL (principalmente URL de HTTP) en páginas HTML. No tengo control sobre la entrada y parte de ella es, como era de esperar, un poco desordenado.Análisis de URL en Python: normalización de doble barra en las rutas

Un problema que estoy encontrando con frecuencia es que urlparse es muy estricto cuando se trata de análisis y de empalme URL que tienen dobles barras en la parte de la ruta, por ejemplo (y posiblemente incluso buggy?):

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

En lugar del resultado esperado http://www.example.com//path (o mejor aún, con una barra única normalizada), termino con http://path.

Por cierto, la razón por la que estoy ejecutando dicho código es porque es la única forma que he encontrado hasta ahora de quitar la parte de consulta/fragmento de las URL. Tal vez hay una mejor manera de hacerlo, pero no pude encontrar uno.

¿Alguien puede recomendar una forma de evitar esto, o debería simplemente normalizar el camino usando una expresión regular (lo sé)?

+0

¿Qué quiere decir con "es la única manera de despojar a la parte de consulta/fragmento"? ¿Qué tiene que ver la barra oblicua con la consulta? – jknupp

+0

No tiene nada que ver con la consulta, la razón por la que estoy analizando una URL y luego uniéndome a su propia ruta es porque quiero quitar la consulta y el fragmento. Si hubiera una mejor manera de hacerlo, no necesitaría resolver este problema. – shevron

+2

Creo que urlparse está implementando el RFC de URLs correctamente, eso especifica que después de la parte : parece ser una sola barra (http: //tools.ietf.org/html/rfc1738) - así que en tu caso trataría de quitar la barra adicional antes de pasarla a urlparse. – BergmannF

Respuesta

4

Si sólo desea obtener la url sin la parte de consulta, me saltaría el módulo urlparse y sólo hacer:

testUrl.rsplit('?') 

La url estará en el índice 0 de la lista devuelta y la consulta en el índice 1.

No es posible tener dos '?' en una url, por lo que debería funcionar para todas las URL.

+0

Esto no responde a ningún problema urlparse, pero definitivamente resuelve mi caso de uso de una manera muy simple. ¡Gracias! – shevron

1

Se menciona en official urlparse docs que:

si la URL es una URL absoluta (es decir, comenzando con // o esquema: //), el nombre de host de la URL y/o esquema será presente en el resultado. Por ejemplo

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

Si no desea que el comportamiento procesar previamente el URL con urlsplit() y urlunsplit(), la eliminación de posible esquema y partes netloc.

Así que usted puede hacer:

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

salida = 'http://www.example.com/path'

0

¿No puede ser una solución?

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

El camino (//path) por sí sola no es válido, lo que confunde la función y obtiene interpreta como un nombre de host

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

Si un URI no contiene un componente de autoridad, entonces la ruta no puede comenzar con dos caracteres de barra ("//").

Particularmente no me gusta ninguna de estas soluciones, pero que funcionan:

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 

Dependiendo de lo que está haciendo, usted podría hacer la unión manualmente:

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 
+0

La URL es de hecho válida, porque ** contiene ** una sección de autoridad, por lo que la URL puede comenzar con '//'. En cualquier caso, incluso si no es capaz de analizar URL inválidas, sino "reales", podría ser útil. – shevron

+0

@ShaharEvron buen punto - respuesta editada – dbr

0

Try Esto:

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 

Ejemplos de 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')) 

volveremos: el

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 

creo que sirve .. :)

Cuestiones relacionadas