Build a Scalable URL Shortener Service in Python
Create a mini-project to develop a URL shortener service that generates unique short URLs, supports custom aliases, and includes analytics features such as click tracking and expiration dates.
Challenge prompt
Create a Python-based URL shortener service that supports the following features: 1) Generating unique shortened URLs from original long URLs; 2) Allow users to specify custom aliases for the shortened URLs where possible; 3) Store all URL mappings and track the number of times each short URL is accessed; 4) Implement optional expiration dates after which the shortened URLs become inactive; 5) Provide functions to resolve shortened URLs back to their original URLs and retrieve click statistics. Your implementation should optimize for scalability and handle potential collision in alias generation gracefully.
Guidance
- • Use hashing or base encoding techniques to generate compact and unique short codes.
- • Maintain a persistent mapping (in-memory dictionary or file/db-based for persistence) between short codes and original URLs, along with metadata such as click counts and expiration.
- • Ensure thread-safe operations if you simulate concurrent accesses or updates.
- • Implement clean error handling for cases such as alias conflicts, expired links, or non-existent short URLs.
Hints
- • Consider using base62 encoding (alphanumeric characters) for compact URL code generation.
- • For custom aliases, check if the alias already exists before creating a new short URL.
- • Store timestamps to manage expiration and periodically clean up expired URLs from storage.
Starter code
import time
class URLShortener:
def __init__(self):
self.url_map = {}
self.clicks = {}
self.expirations = {}
self.counter = 1
self.alphabet = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
def _encode(self, num):
# encode integer to base62 string
if num == 0:
return self.alphabet[0]
arr = []
base = len(self.alphabet)
while num:
num, rem = divmod(num, base)
arr.append(self.alphabet[rem])
arr.reverse()
return ''.join(arr)
def shorten(self, original_url, custom_alias=None, expire_in_seconds=None):
pass # Implement this
def resolve(self, short_url):
pass # Implement this
def get_clicks(self, short_url):
pass # Implement this
def _is_expired(self, short_url):
pass # Implement thisExpected output
short_url = shortener.shorten('https://www.example.com/some/long/path') print(short_url) # e.g. "b9" print(shortener.resolve(short_url)) # 'https://www.example.com/some/long/path' print(shortener.get_clicks(short_url)) # 1 after resolve call custom_short = shortener.shorten('https://www.example.org', custom_alias='exOrg') print(custom_short) # 'exOrg' print(shortener.resolve('exOrg')) # 'https://www.example.org'
Core concepts
Challenge a Friend
Send this duel to someone else and see if they can solve it.