urllib.parse - Divide un URL nei suoi Componenti

Scopo: Divide un URL nei suoi Componenti

Il modulo urllib.parse fornisce funzioni per manipolare gli URL e le parti con le quali sono costituiti, sia per comporli che per decomporli.

Ottenere le Parti

Il valore di ritorno della funzione urlparse() è un oggetto ParseResult che agisce come una tupla di sei elementi.

# urllib_parse_urlparse.py

from urllib.parse import urlparse

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

Le parti dell'URL disponibili tramite la tupla di interfaccia sono lo schema, la locazione della rete, il percorso, i parametri di segmento del percorso (separati dal percorso da un punto e virgola), query e frammento.

$ python3 urllib_parse_urlparse.py

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

Sebbene il valore ritornato agisca come una tupla, in realtà è basato su una namedtuple, una sottoclasse di tuple che supporta l'accesso alle parti dell'URL tramite attributi nominali e numerici. Oltre a essere più facile da usare per gli sviluppatori, l'API offre accesso a parecchi valori non disponibili nell'API di 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('schema      :', parsed.scheme)
print('loc. di rete:', parsed.netloc)
print('percorso    :', parsed.path)
print('parametri   :', parsed.params)
print('query       :', parsed.query)
print('frammento   :', parsed.fragment)
print('nome utene  :', parsed.username)
print('password    :', parsed.password)
print('nome host   :', parsed.hostname)
print('porta       :', parsed.port)

username e password sono disponibili se presenti nell'URL elaborato, e impostati a None in caso contrario. hostname ha lo stesso valore di netloc, in caratteri minuscoli e con il valore della porta eliminato, port viene convertito in un intero se presente e a None altrimenti.

$ python3 urllib_parse_urlparseattrs.py

schema      : http
loc. di rete: user:pwd@NetLoc:80
percorso    : /path
parametri   : param
query       : query=arg
frammento   : frag
nome utene  : user
password    : pwd
nome host   : netloc
porta       : 80

La funzione urlsplit() è una alternativa ad urlparse(). Si comporta in maniera leggermente diversa, visto che non separa i parametri dall'URL. Questo è utile per gli URL che seguono le direttive RFC 2396, che supportano parametri per ciascun segmento del percorso.

# urllib_parse_urlsplit.py

from urllib.parse import urlsplit

url = 'http://user:pwd@NetLoc:80/path;param?query=arg#frag'
parsed = urlsplit(url)
print(parsed)
print('schema      :', parsed.scheme)
print('loc. di rete:', parsed.netloc)
print('percorso    :', parsed.path)
print('query       :', parsed.query)
print('frammento   :', parsed.fragment)
print('nome utene  :', parsed.username)
print('password    :', parsed.password)
print('nome host   :', parsed.hostname)
print('porta       :', parsed.port)

Visto che i parametri non sono estrapolati, l'API di tuple mostrerà solo cinque elementi in luogo di sei, e non c'è l'attributo params.

$ python3 urllib_parse_urlsplit.py

SplitResult(scheme='http', netloc='user:pwd@NetLoc:80', path='/path;param', query='query=arg', fragment='frag')
schema      : http
loc. di rete: user:pwd@NetLoc:80
percorso    : /path;param
query       : query=arg
frammento   : frag
nome utene  : user
password    : pwd
nome host   : netloc
porta       : 80

Per estrapolare l'identificatore del frammento da un URL, come quando si deve trovare un nome di pagina base per un URL, si usa urldefrag().

# urllib_parse_urldefrag.py

from urllib.parse import urldefrag

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

Il valore ritornato è un oggetto DefragResult, basato su namedtuple, che contiene l'URL base e il frammento.

$ python3 urllib_parse_urldefrag.py

originale: http://netloc/path;param?query=arg#frag
url      : http://netloc/path;param?query=arg
frammento: frag

Assemblare le Parti

Ci sono parecchi modi per assemblare in una singola stringa le parti di un URL separate. L'oggetto URL separato ha un metodo geturl().

# urllib_parse_geturl.py

from urllib.parse import urlparse

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

geturl() funziona solo con oggetti restituiti da urlparse() ed urlsplit().

$ python3 urllib_parse_geturl.py

ORIGINALE  : http://netloc/path;param?query=arg#frag
ASSEMBLATO : http://netloc/path;param?query=arg#frag

Una tupla normale che contenga stringhe può essere combinata in un URL con urlunparse().

# urllib_parse_urlunparse.py

from urllib.parse import urlparse, urlunparse

original = 'http://netloc/path;param?query=arg#frag'
print('ORIGINALE  :', original)
parsed = urlparse(original)
print('ASSEMBLATO :', type(parsed), parsed)
t = parsed[:]
print('TUPLA      :', type(t), t)
print('NUOVO      :', urlunparse(t))

Mentre l'oggetto ParseResult ritornato da urlparse() può essere usato come tupla, questo esempio crea esplicitamente una nuova tupla per mostrare che urlunparse() funziona anche con le normali tuple.

$ python3 urllib_parse_urlunparse.py

ORIGINALE  : http://netloc/path;param?query=arg#frag
ASSEMBLATO : <class 'urllib.parse.ParseResult'> ParseResult(scheme='http', netloc='netloc', path='/path', params='param', query='query=arg', fragment='frag')
TUPLA      : <class 'tuple'> ('http', 'netloc', '/path', 'param', 'query=arg', 'frag')
NUOVO      : http://netloc/path;param?query=arg#frag

Se l'URL in input comprende anche parti superflue, esse potranno essere ignorate nella ricostruzione dell'URL.

# urllib_parse_urlunparseextra.py

from urllib.parse import urlparse, urlunparse

original = 'http://netloc/path;?#'
print('ORIGINALE :', original)
parsed = urlparse(original)
print('ASSEMBLATO:', type(parsed), parsed)
t = parsed[:]
print('TUPLA     :', type(t), t)
print('NUOVO     :', urlunparse(t))

In questo caso parameters, query e fragment sono mancanti nell'URL di origine. Il nuovo URL non sembra uguale all'originale, ma, in base agli standard, è equivalente.

$ python3 urllib_parse_urlunparseextra.py

ORIGINALE : http://netloc/path;?#
ASSEMBLATO: <class 'urllib.parse.ParseResult'> ParseResult(scheme='http', netloc='netloc', path='/path', params='', query='', fragment='')
TUPLA     : <class 'tuple'> ('http', 'netloc', '/path', '', '', '')
NUOVO     : http://netloc/path

Unire

Oltre alla separazione di URL, urllib.urlparse comprende urljoin() per costruire URL assoluti da frammenti relativi.

# 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'))

Nell'esempio la porzione relativa del percorso ("../"), viene presa in considerazione quando viene calcolato il secondo URL.

$ python3 urllib_parse_urljoin.py

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

Percorsi non relativi sono gestiti allo stesso modo di 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'))

Se il percorso che deve essere unito nell'URL inizia con una barra (/), reimposta il percorso dell'URL al livello superiore, altrimenti viene aggiunto alla fine del percorso dell'URL.

$ python3 urllib_parse_urljoin_with_path.py .

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

Codificare gli Argomenti in Query

Prima di essere aggiunti all'URL, gli argomenti devono essere codificati.

# urllib_parse_urlencode.py

from urllib.parse import urlencode

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

La codifica sostituisce i caratteri speciali come gli spazi per assicurarsi che vengano passati al server usando un formato che sia conforme allo standard.

$ python3 urllib_parse_urlencode.py

Codificati: foo=bar&q=query+string

Per passare una sequenza di valori usando diverse occorrenze della variabile nella query string, si imposti doseq a True quando si chiama urlencode() .

urllib_parse_urlencode_doseq.py
from urllib.parse import urlencode

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

Il risultato è una query string con diversi valori associati allo stesso nome.

$ python3 urllib_parse_urlencode_doseq.py

Singola : foo=%5B%27foo1%27%2C+%27foo2%27%5D
Sequenza: foo=foo1&foo=foo2

Per decodificare una query string si usi parse_qs(), oppure 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))

Il valore di ritorno da parse_qs() è un dizionario che mappa nomi con valori, mentre parse_qsl() ritorna una lista di tuple che contengono un nome e un valore.

$ python3 urllib_parse_parse_qs.py

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

I caratteri speciali all'interno degli argomenti della query che potrebbero causare problemi di elaborazione con l'URL lato server sono racchiusi tra virgolette quando passati ad urlencode(). Per eseguire la stessa operazione localmente per generare versioni più sicure delle stringhe si usi quote() o quote_plus() direttamente.

# 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))

L'implementazione in quote_plus() è più aggressiva verso i caratteri che deve sostituire.

$ 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

Per invertire le operazioni fatte con quote() e quote_plus() si usi rispettivamente unquote() e unquote_plus().

# 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'
))

Il valore codificato viene riconvertito come normale stringa URL.

$ python3 urllib_parse_unquote.py

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

Vedere anche:

urllib.parse
La documentazione della libreria standard per questo modulo
urllib.request
Recupera il contenuto di una risorsa identificata da un URL
RFC 1738
Sintassi per Uniform Resource Locator (URL)
RFC 1808
URL relativi
RFC 2396
Sintassi generica per Uniform Resource Identifier (URI)
RFC 3986
Sintassi per Uniform Resource Identifier (URI)