# Create an opener with authentication handler
Source: https://speakeasy.com/blog/python-http-clients-requests-vs-httpx-vs-aiohttp

import { Table } from "@/mdx/components";

Anyone who's been using Python for more than a minute has come across the `Requests` library. It is so ubiquitous, some may have thought it was part of the standard library. Requests is so intuitive that writing `r = requests.get` has become muscle memory. In contrast, any script using Python's built-in [`urllib`](https://docs.python.org/3/library/urllib.html) starts with a trip to the Python docs.

But Python has evolved, and simply defaulting to Requests is no longer an option. While `Requests` remains a solid choice for short synchronous scripts, newer libraries like `HTTPX` and `AIOHTTP` are better suited for modern Python, especially when it comes to asynchronous programming.

Let's compare these three popular HTTP clients for Python: [`Requests`](https://github.com/psf/requests), [`HTTPX`](https://github.com/projectdiscovery/httpx), and [`AIOHTTP`](https://docs.aiohttp.org/en/stable/). We'll explore their strengths, weaknesses, and ideal use cases to help you choose the right tool for your next project.

## In The Beginning, Guido Created Urllib

Before we dive into our comparison of modern HTTP libraries, it's worth taking a brief look at where it all began: Python's built-in `urllib` module.

`urllib` has been part of Python's standard library since the early days. It was designed to be a comprehensive toolkit for URL handling and network operations. However, its API is notoriously complex and unintuitive, often requiring multiple steps to perform even simple HTTP requests.

Here's a basic example of making a GET request with `urllib`:

```python filename="urllib_basic.py"
from urllib.request import urlopen

with urlopen('https://api.github.com') as response:
    body = response.read()
    print(body)
```

While this might seem straightforward for a simple GET request, things quickly become more complicated when dealing with headers, POST requests, or authentication. For instance, here's how you might make a request with authentication:

```python filename="urllib_example.py"
import urllib.request
import json

url = 'http://httpbin.org/basic-auth/user/passwd'
username = 'user'
password = 'passwd'


password_mgr = urllib.request.HTTPPasswordMgrWithDefaultRealm()
password_mgr.add_password(None, url, username, password)
auth_handler = urllib.request.HTTPBasicAuthHandler(password_mgr)
opener = urllib.request.build_opener(auth_handler)