Python: Join multiple components to build a URL

Viewed 17679

I am trying to build a URL by joining some dynamic components. I thought of using something like os.path.join() BUT for URLs in my case. From research I found urlparse.urljoin() does the same thing. However, it looks like it only take two arguments at one time.

I have the following so far which works but looks repetitive:

    a = urlparse.urljoin(environment, schedule_uri)
    b = urlparse.urljoin(a, str(events_to_hours))
    c = urlparse.urljoin(b, str(events_from_date))
    d = urlparse.urljoin(c, str(api_version))
    e = urlparse.urljoin(d, str(id))
    url = e + '.json'

Output = http://example.com/schedule/12/20160322/v1/1.json

The above works and I tried to make it shorter this way:

url_join_items = [environment, schedule_uri, str(events_to_hours),
                  str(events_from_date), str(api_version), str(id), ".json"]
new_url = ""
for url_items in url_join_items:
    new_url = urlparse.urljoin(new_url, url_items)

Output: http://example.com/schedule/.json

But the second implementation does not work. Please suggest me how to fix this or the better way of doing it.

EDIT 1: The output from the reduce solution looks like this (unfortunately): Output: http://example.com/schedule/.json

4 Answers

I also needed something similar and came up with this solution:

from urllib.parse import urljoin, quote_plus

def multi_urljoin(*parts):
    return urljoin(parts[0], "/".join(quote_plus(part.strip("/"), safe="/") for part in parts[1:]))

print(multi_urljoin("https://server.com", "path/to/some/dir/", "2019", "4", "17", "some_random_string", "image.jpg"))

This prints 'https://server.com/path/to/some/dir/2019/4/17/some_random_string/image.jpg'

Here's a bit silly but workable solution, given that parts is a list of URL parts in order

my_url = '/'.join(parts).replace('//', '/').replace(':/', '://')

I wish replace would have a from option but it does not hence the second one is to recover https:// double slash

Nice thing is you don't have to worry about parts already having (or not having) any slashes

Simple solution will be:

def url_join(*parts: str) -> str:
    import re

    line = '/'.join(parts)
    line = re.sub('/{2,}', '/', line)
    return re.sub(':/', '://', line)
Related