urllib.parse — Dividir URLs en componentes

Propósito:Dividir URLs en componentes

El módulo urllib.parse proporciona funciones para manipular URLs y sus componentes, para descomponerlas o construirlas.

Análisis

El valor de retorno de la función urlparse() es un objeto ParseResult que actúa como un tuple con seis elementos.

urllib_parse_urlparse.py
from urllib.parse import urlparse

url = 'http://netloc/path;param?query=arg#frag'
parsed = urlparse(url)
print(parsed)

Las partes de la URL disponibles a través de la interfaz de la tupla son los parámetros de esquema, ubicación de red, ruta, segmento de ruta (separados de la ruta por un punto y coma), consulta y fragmento.

$ python3 urllib_parse_urlparse.py

ParseResult(scheme='http', netloc='netloc', path='/path',
params='param', query='query=arg', fragment='frag')

Aunque el valor de retorno actúa como una tupla, realmente se basa en un namedtuple, una subclase de tuple que soporta acceder a las partes de la URL a través de atributos nombrados, así como índices. Además de ser más fácil de usar para el programador, la interfaz de programación de atributo también ofrece acceso a varios valores no disponibles en la interfaz de programación tuple.

urllib_parse_urlparseattrs.py
from urllib.parse import urlparse

url = 'http://user:pwd@NetLoc:80/path;param?query=arg#frag'
parsed = urlparse(url)
print('scheme  :', parsed.scheme)
print('netloc  :', parsed.netloc)
print('path    :', parsed.path)
print('params  :', parsed.params)
print('query   :', parsed.query)
print('fragment:', parsed.fragment)
print('username:', parsed.username)
print('password:', parsed.password)
print('hostname:', parsed.hostname)
print('port    :', parsed.port)

El username y password están disponibles cuando están presentes en la URL de entrada, y se establece en None cuando no. El hostname tiene el mismo valor que netloc, en minúsculas y con el valor de puerto eliminado. Y el port se convierte en un entero cuando está presente y None cuando no está presente.

$ python3 urllib_parse_urlparseattrs.py

scheme  : http
netloc  : user:pwd@NetLoc:80
path    : /path
params  : param
query   : query=arg
fragment: frag
username: user
password: pwd
hostname: netloc
port    : 80

La función urlsplit() es una alternativa a urlparse(). Se comporta un poco diferente, porque no divide los parámetros de la URL. Esto es útil para las siguientes URLs RFC 2396, que admite parámetros para cada segmento de la ruta.

urllib_parse_urlsplit.py
from urllib.parse import urlsplit

url = 'http://user:pwd@NetLoc:80/p1;para/p2;para?query=arg#frag'
parsed = urlsplit(url)
print(parsed)
print('scheme  :', parsed.scheme)
print('netloc  :', parsed.netloc)
print('path    :', parsed.path)
print('query   :', parsed.query)
print('fragment:', parsed.fragment)
print('username:', parsed.username)
print('password:', parsed.password)
print('hostname:', parsed.hostname)
print('port    :', parsed.port)

Como los parámetros no están divididos, la interfaz de programación de la tupla mostrará cinco elementos en lugar de seis, y no hay ningún atributo params.

$ python3 urllib_parse_urlsplit.py

SplitResult(scheme='http', netloc='user:pwd@NetLoc:80',
path='/p1;para/p2;para', query='query=arg', fragment='frag')
scheme  : http
netloc  : user:pwd@NetLoc:80
path    : /p1;para/p2;para
query   : query=arg
fragment: frag
username: user
password: pwd
hostname: netloc
port    : 80

Para simplemente quitar el identificador del fragmento de una URL, como cuando para encontrar un nombre de página base desde una URL, usa urldefrag().

urllib_parse_urldefrag.py
from urllib.parse import urldefrag

original = 'http://netloc/path;param?query=arg#frag'
print('original:', original)
d = urldefrag(original)
print('url     :', d.url)
print('fragment:', d.fragment)

El valor de retorno es un DefragResult, basado en namedtuple, que contiene la URL base y el fragmento.

$ python3 urllib_parse_urldefrag.py

original: http://netloc/path;param?query=arg#frag
url     : http://netloc/path;param?query=arg
fragment: frag

Combinar

Hay varias formas de ensamblar las partes de una URL dividida juntos en una sola cadena. El objeto URL analizado tiene un método geturl().

urllib_parse_geturl.py
from urllib.parse import urlparse

original = 'http://netloc/path;param?query=arg#frag'
print('ORIG  :', original)
parsed = urlparse(original)
print('PARSED:', parsed.geturl())

geturl() solo funciona en el objeto devuelto por urlparse() o urlsplit().

$ python3 urllib_parse_geturl.py

ORIG  : http://netloc/path;param?query=arg#frag
PARSED: http://netloc/path;param?query=arg#frag

Una tupla regular que contiene cadenas se puede combinar en una URL con urlunparse().

urllib_parse_urlunparse.py
from urllib.parse import urlparse, urlunparse

original = 'http://netloc/path;param?query=arg#frag'
print('ORIG  :', original)
parsed = urlparse(original)
print('PARSED:', type(parsed), parsed)
t = parsed[:]
print('TUPLE :', type(t), t)
print('NEW   :', urlunparse(t))

Mientras que el ParseResult devuelto por urlparse() puede ser utilizado como tupla, este ejemplo crea explícitamente una tupla nueva para mostrar que urlunparse() también funciona con tuplas normales.

$ python3 urllib_parse_urlunparse.py

ORIG  : http://netloc/path;param?query=arg#frag
PARSED: <class 'urllib.parse.ParseResult'>
ParseResult(scheme='http', netloc='netloc', path='/path',
params='param', query='query=arg', fragment='frag')
TUPLE : <class 'tuple'> ('http', 'netloc', '/path', 'param',
'query=arg', 'frag')
NEW   : http://netloc/path;param?query=arg#frag

Si la URL de entrada incluía partes superfluas, esas pueden ser eliminadas de la URL reconstruida.

urllib_parse_urlunparseextra.py
from urllib.parse import urlparse, urlunparse

original = 'http://netloc/path;?#'
print('ORIG  :', original)
parsed = urlparse(original)
print('PARSED:', type(parsed), parsed)
t = parsed[:]
print('TUPLE :', type(t), t)
print('NEW   :', urlunparse(t))

En este caso, parameters, query y fragment falta todos en la URL original. La nueva URL no se ve igual que la original, pero es equivalente según la norma.

$ python3 urllib_parse_urlunparseextra.py

ORIG  : http://netloc/path;?#
PARSED: <class 'urllib.parse.ParseResult'>
ParseResult(scheme='http', netloc='netloc', path='/path',
params='', query='', fragment='')
TUPLE : <class 'tuple'> ('http', 'netloc', '/path', '', '', '')
NEW   : http://netloc/path

Unir

Además de analizar las URL, urlparse incluye urljoin() para construir URLs absolutas a partir de fragmentos relativos.

urllib_parse_urljoin.py
from urllib.parse import urljoin

print(urljoin('http://www.example.com/path/file.html',
              'anotherfile.html'))
print(urljoin('http://www.example.com/path/file.html',
              '../anotherfile.html'))

En el ejemplo, se toma la parte relativa de la ruta ("../") en cuenta cuando se calcula la segunda URL.

$ python3 urllib_parse_urljoin.py

http://www.example.com/path/anotherfile.html
http://www.example.com/anotherfile.html

Las rutas no relativas se manejan de la misma manera que por os.path.join().

urllib_parse_urljoin_with_path.py
from urllib.parse import urljoin

print(urljoin('http://www.example.com/path/',
              '/subpath/file.html'))
print(urljoin('http://www.example.com/path/',
              'subpath/file.html'))

Si la ruta que se une a la URL comienza con una barra (/), se restablece la ruta de la URL al nivel superior. Si no comienza con un barra, se anexa al final de la ruta de la URL.

$ python3 urllib_parse_urljoin_with_path.py

http://www.example.com/subpath/file.html
http://www.example.com/path/subpath/file.html

Codificar argumentos de consulta

Antes de que se puedan agregar argumentos a una URL, deben codificarse.

urllib_parse_urlencode.py
from urllib.parse import urlencode

query_args = {
    'q': 'query string',
    'foo': 'bar',
}
encoded_args = urlencode(query_args)
print('Encoded:', encoded_args)

La codificación reemplaza los caracteres especiales como los espacios para garantizar que se pasan al servidor utilizando un formato que cumple con el estándar.

$ python3 urllib_parse_urlencode.py

Encoded: q=query+string&foo=bar

Para pasar una secuencia de valores usando apariciones separadas de la variable en la cadena de consulta, establezca doseq en True al llamar urlencode().

urllib_parse_urlencode_doseq.py
from urllib.parse import urlencode

query_args = {
    'foo': ['foo1', 'foo2'],
}
print('Single  :', urlencode(query_args))
print('Sequence:', urlencode(query_args, doseq=True))

El resultado es una cadena de consulta con varios valores asociados con el mismo nombre.

$ python3 urllib_parse_urlencode_doseq.py

Single  : foo=%5B%27foo1%27%2C+%27foo2%27%5D
Sequence: foo=foo1&foo=foo2

Para decodificar la cadena de consulta, usa parse_qs() o parse_qsl().

urllib_parse_parse_qs.py
from urllib.parse import parse_qs, parse_qsl

encoded = 'foo=foo1&foo=foo2'

print('parse_qs :', parse_qs(encoded))
print('parse_qsl:', parse_qsl(encoded))

El valor de retorno de parse_qs() es un diccionario que asigna nombres a valores, mientras que parse_qsl() devuelve una lista de tuplas que contienen un nombre y un valor.

$ python3 urllib_parse_parse_qs.py

parse_qs : {'foo': ['foo1', 'foo2']}
parse_qsl: [('foo', 'foo1'), ('foo', 'foo2')]

Caracteres especiales dentro de los argumentos de consulta que podrían causar problemas en el análisis de la URL en el lado del servidor se «citan» cuando se pasan a urlencode(). Para citarlos localmente para hacer versiones seguras de las cadenas, usa las funciones quote() o quote_plus() directamente.

urllib_parse_quote.py
from urllib.parse import quote, quote_plus, urlencode

url = 'http://localhost:8080/~hellmann/'
print('urlencode() :', urlencode({'url': url}))
print('quote()     :', quote(url))
print('quote_plus():', quote_plus(url))

La implementación de la cita en quote_plus() es más agresiva con los caracteres que reemplaza.

$ python3 urllib_parse_quote.py

urlencode() : url=http%3A%2F%2Flocalhost%3A8080%2F%7Ehellmann%2F
quote()     : http%3A//localhost%3A8080/%7Ehellmann/
quote_plus(): http%3A%2F%2Flocalhost%3A8080%2F%7Ehellmann%2F

Para revertir las operaciones de la cita, usa unquote() o unquote_plus(), según corresponda.

urllib_parse_unquote.py
from urllib.parse import unquote, unquote_plus

print(unquote('http%3A//localhost%3A8080/%7Ehellmann/'))
print(unquote_plus(
    'http%3A%2F%2Flocalhost%3A8080%2F%7Ehellmann%2F'
))

El valor codificado se convierte de nuevo a una URL de cadena normal.

$ python3 urllib_parse_unquote.py

http://localhost:8080/~hellmann/
http://localhost:8080/~hellmann/

Ver también