Come fare l'URL encoding in Python (guida completa a urllib.parse)
In Python l'URL encoding si effettua con urllib.parse.quote() per il percent-encoding delle stringhe e con urllib.parse.urlencode() per codificare i dizionari in query string. Questa guida illustra quote(), unquote(), urlencode() e parse_qs() con esempi pratici.
URL encoding con quote()
La funzione urllib.parse.quote() è lo strumento principale di Python per il percent-encoding delle stringhe. Converte i caratteri non sicuri per l'uso negli URL nei loro equivalenti percent-encoded. Per impostazione predefinita considera le barre (/) come caratteri sicuri, ma è possibile personalizzare questo comportamento.
from urllib.parse import quote
# Codifica di base
print(quote('hello world'))
# Output: hello%20world
# Codifica dei caratteri speciali
print(quote('price=10&qty=2'))
# Output: price%3D10%26qty%3D2
# Per impostazione predefinita, / non viene codificato
print(quote('path/to/file'))
# Output: path/to/file
# Per codificare anche le barre, imposta safe=''
print(quote('path/to/file', safe=''))
# Output: path%2Fto%2Ffile
# Codifica dei caratteri Unicode
print(quote('cafe'))
# Output: caf%C3%A9
# Specifica di caratteri sicuri aggiuntivi
print(quote('key=value&foo=bar', safe='=&'))
# Output: key=value&foo=bar
Il parametro safe è la chiave per controllare ciò che viene codificato. Per impostazione predefinita, safe='/'. Se vuoi codificare tutto tranne i caratteri alfanumerici e _.-~, imposta safe=''. Questo equivale alla funzione encodeURIComponent() di JavaScript.
Esiste anche quote_plus(), che funziona come quote() ma codifica gli spazi come + invece di %20. Questo è il formato usato nei dati dei form HTML (application/x-www-form-urlencoded).
from urllib.parse import quote_plus
print(quote_plus('hello world'))
# Output: hello+world
print(quote_plus('key=value&name=John Doe'))
# Output: key%3Dvalue%26name%3DJohn+Doe
URL decoding con unquote()
La funzione urllib.parse.unquote() inverte il percent-encoding, riconvertendo le sequenze %XX nei caratteri originali. Esiste anche unquote_plus(), che in aggiunta converte i segni + in spazi.
from urllib.parse import unquote, unquote_plus
# Decodifica di base
print(unquote('hello%20world'))
# Output: hello world
print(unquote('caf%C3%A9'))
# Output: cafe (con accento)
# unquote NON converte + in spazio
print(unquote('hello+world'))
# Output: hello+world
# unquote_plus converte + in spazio
print(unquote_plus('hello+world'))
# Output: hello world
# Decodifica di un URL completo
url = 'https://example.com/search?q=C%2B%2B%20programming'
print(unquote(url))
# Output: https://example.com/search?q=C++ programming
Usa sempre unquote_plus() quando decodifichi i dati di un form, poiché i form HTML codificano gli spazi come +. Usa unquote() per la decodifica generica degli URL, dove gli spazi sono codificati come %20.
Codifica delle query string con urlencode()
La funzione urllib.parse.urlencode() accetta un dizionario o una lista di tuple e la converte in una query string correttamente formattata. È il modo più comodo per costruire query string in Python.
from urllib.parse import urlencode
# Da dizionario a query string
params = {
'q': 'python programming',
'page': 1,
'lang': 'en'
}
print(urlencode(params))
# Output: q=python+programming&page=1&lang=en
# Lista di tuple (conserva l'ordine, consente chiavi duplicate)
params = [
('tag', 'python'),
('tag', 'web'),
('sort', 'date')
]
print(urlencode(params))
# Output: tag=python&tag=web&sort=date
# Uso di doseq=True per valori di tipo lista
params = {
'tag': ['python', 'web', 'api'],
'sort': 'date'
}
print(urlencode(params, doseq=True))
# Output: tag=python&tag=web&tag=api&sort=date
# Uso di quote_via per controllare la codifica degli spazi
from urllib.parse import quote
params = {'q': 'hello world'}
print(urlencode(params, quote_via=quote))
# Output: q=hello%20world (usa %20 invece di +)
Per impostazione predefinita, urlencode() usa internamente quote_plus(), il che significa che gli spazi diventano +. Se hai bisogno di %20 per gli spazi, passa quote_via=quote come mostrato sopra.
Parsing delle query string con parse_qs()
La funzione urllib.parse.parse_qs() esegue il parsing di una query string riconvertendola in un dizionario. Ogni valore del dizionario è una lista, poiché i parametri di query possono avere più valori. Esiste anche parse_qsl(), che restituisce una lista di tuple.
from urllib.parse import parse_qs, parse_qsl
# Parsing di una query string in un dizionario
qs = 'q=python+programming&page=1&lang=en'
result = parse_qs(qs)
print(result)
# Output: {'q': ['python programming'], 'page': ['1'], 'lang': ['en']}
# Nota: i valori sono sempre liste
print(result['q'][0]) # 'python programming'
# Gestione di più valori per la stessa chiave
qs = 'tag=python&tag=web&tag=api'
result = parse_qs(qs)
print(result)
# Output: {'tag': ['python', 'web', 'api']}
# parse_qsl restituisce una lista di tuple
result = parse_qsl(qs)
print(result)
# Output: [('tag', 'python'), ('tag', 'web'), ('tag', 'api')]
# Mantieni i valori vuoti (per impostazione predefinita vengono omessi)
qs = 'name=John&email=&age=30'
print(parse_qs(qs, keep_blank_values=True))
# Output: {'name': ['John'], 'email': [''], 'age': ['30']}
Codifica di URL completi con urlparse
Quando si lavora con URL completi, le funzioni urlparse() e urlunparse() di Python permettono di scomporre e ricostruire gli URL in modo sicuro. Questo è particolarmente utile quando è necessario modificare parti specifiche di un URL senza comprometterne la struttura.
from urllib.parse import urlparse, urlunparse, urlencode, quote
# Parsing di un URL nei suoi componenti
url = 'https://example.com/search?q=hello&page=1#results'
parsed = urlparse(url)
print(parsed.scheme) # 'https'
print(parsed.netloc) # 'example.com'
print(parsed.path) # '/search'
print(parsed.query) # 'q=hello&page=1'
print(parsed.fragment) # 'results'
# Costruzione di un URL dai suoi componenti
from urllib.parse import ParseResult
new_url = urlunparse(ParseResult(
scheme='https',
netloc='api.example.com',
path='/v2/search',
params='',
query=urlencode({'q': 'python & java', 'limit': 10}),
fragment=''
))
print(new_url)
# Output: https://api.example.com/v2/search?q=python+%26+java&limit=10
# Aggiunta sicura di un segmento di percorso con caratteri speciali
base = 'https://example.com/files/'
filename = 'my report (final).pdf'
safe_url = base + quote(filename, safe='')
print(safe_url)
# Output: https://example.com/files/my%20report%20%28final%29.pdf
Per il codice Python moderno, valuta l'uso della libreria requests, che gestisce automaticamente l'URL encoding quando passi i parametri come dizionario. Anche la libreria httpx offre analoghe funzionalità di codifica automatica.
import requests
# requests gestisce la codifica automaticamente
response = requests.get(
'https://api.example.com/search',
params={
'q': 'python & java',
'page': 1,
'sort': 'relevance'
}
)
print(response.url)
# https://api.example.com/search?q=python+%26+java&page=1&sort=relevance