Python

How to split a URL into parts in Python 3.

Python’s standard library does this without any dependency: urllib.parse breaks a URL into its components and parses the query string. Here’s urlparse, parse_qs, and the decoding details that matter.

urlparse: the six components

urlparse splits a URL into scheme, netloc (host), path, params, query, and fragment:

from urllib.parse import urlparse

u = urlparse('https://user@example.com:8443/api/items?q=hello&page=2#top')
u.scheme     # 'https'
u.netloc     # 'user@example.com:8443'
u.hostname   # 'example.com'
u.port       # 8443
u.path       # '/api/items'
u.query      # 'q=hello&page=2'
u.fragment   # 'top'
u.username   # 'user'

Parse the query string with parse_qs

u.query is still a raw string. Use parse_qs to turn it into a dict — note every value is a list, because a key can repeat:

from urllib.parse import urlparse, parse_qs

q = urlparse('https://x.com/?tag=js&tag=url&page=2').query
parse_qs(q)
# {'tag': ['js', 'url'], 'page': ['2']}   ← values are lists

# If you know keys are unique and want single values:
from urllib.parse import parse_qsl
dict(parse_qsl(q))
# {'tag': 'url', 'page': '2'}   ← parse_qsl keeps last for dupes

Decoding happens automatically

parse_qs and parse_qsl percent-decode values for you, and treat + as a space (query-string convention):

parse_qs('q=hello%20world')   # {'q': ['hello world']}
parse_qs('q=a+b')             # {'q': ['a b']}  ← + becomes space

To decode a path segment yourself (where + is a literal plus, not a space), use unquote, not unquote_plus:

from urllib.parse import unquote, unquote_plus
unquote('/caf%C3%A9/menu')      # '/café/menu'   (path — + stays +)
unquote_plus('a+b%20c')         # 'a b c'        (form — + becomes space)

Put a URL back together

urlunparse reverses urlparse, and urlencode builds a query string from a dict (encoding each value once):

from urllib.parse import urlencode
urlencode({'q': 'hello world', 'page': 2})
# 'q=hello+world&page=2'

urlencode({'tags': ['a', 'b']}, doseq=True)
# 'tags=a&tags=b'   ← doseq handles list values

A note on urlsplit vs urlparse

urlsplit is the same as urlparse but omits the rarely-used params field (the legacy ; path parameters). For modern URLs, urlsplit is often the cleaner choice.

Prefer to see it visually first? Paste a URL into the URL parser to see every component split and decoded, matching what urlparse returns.

About this page

Written and maintained by the urlencodedecode.com team. Every technical claim on this page is verified against primary sources — the RFCs (3986, 3629, 4648, 7578), the WHATWG URL Standard, and official vendor or language documentation — rather than second-hand summaries. When a source contradicts a common assumption, we follow the source and note the discrepancy. Corrections: contactus@urlencodedecode.com.

Common questions

Frequently asked.

Use urllib.parse.urlparse(url), which returns a named tuple with .scheme, .netloc, .hostname, .port, .path, .query, and .fragment. It's in the standard library, so no installation is needed.

Pass the .query string to urllib.parse.parse_qs, which returns a dict where each value is a list (because keys can repeat): {'tag': ['js','url']}. Use parse_qsl for a list of pairs, or dict(parse_qsl(q)) if keys are unique.

Because a query string can repeat a key — ?tag=a&tag=b is two values for tag. parse_qs preserves all of them as a list so no data is lost. Use parse_qsl if you'd rather have pairs.

unquote decodes percent-encoding but leaves + as a literal plus — correct for path segments. unquote_plus also converts + to a space — correct for form-encoded query values. Match the function to where the string came from.

urlsplit is the same as urlparse but omits the legacy params attribute (the semicolon path parameters), so it returns five components instead of six. For modern URLs, urlsplit is usually the cleaner choice.

Related

Keep exploring.