Ada situasi di mana memasang library tambahan bukan pilihan: server produksi yang terkunci kebijakan, skrip sekali jalan di mesin orang lain, atau lingkungan minimal di dalam container. Untuk kebutuhan tersebut Python sudah membawa klien HTTP bawaan bernama urllib di dalam standard library — tidak ada pip install, tidak ada dependency. Harganya memang API yang lebih bertele-tele dibanding library modern; pada Chapter ini kita pelajari keduanya: cara pakainya sekaligus bentuk verbositasnya.

Server Lokal untuk Latihan #

Semua contoh pada Chapter ini mengarah ke server lokal, jadi hasilnya bisa kita reproduksi persis. Simpan kode berikut sebagai server_lokal.py, lalu jalankan dengan python3 server_lokal.py di terminal terpisah:

python
from http.server import BaseHTTPRequestHandler, HTTPServer
from urllib.parse import parse_qs


class Handler(BaseHTTPRequestHandler):
    def do_GET(self):
        if self.path == "/":
            body = b"Selamat datang di server lokal!"
            status = 200
        elif self.path.startswith("/cari"):
            params = parse_qs(self.path.split("?")[1])
            kota = params.get("kota", ["-"])[0]
            body = f"Hasil pencarian di {kota}".encode("utf-8")
            status = 200
        else:
            body = b"Halaman tidak ditemukan"
            status = 404
        self.send_response(status)
        self.send_header("Content-Type", "text/plain; charset=utf-8")
        self.send_header("Content-Length", str(len(body)))
        self.end_headers()
        self.wfile.write(body)

    def log_message(self, format, *args):
        pass


HTTPServer(("127.0.0.1", 8123), Handler).serve_forever()

Server sederhana ini menjawab tiga rute: halaman utama, pencarian dengan parameter kota, dan error 404 untuk path lain — cukup untuk semua skenario yang kita butuhkan.

Permintaan Pertama dengan urlopen #

Function urlopen() adalah pintu masuk utama urllib.request:

python
from urllib.request import urlopen

with urlopen("http://127.0.0.1:8123/", timeout=3) as respons:
    print("Status :", respons.status)
    print("Tipe   :", respons.headers["Content-Type"])
    print("Isi    :", respons.read().decode("utf-8"))

Output yang Dihasilkan:

text
Status : 200
Tipe   : text/plain; charset=utf-8
Isi    : Selamat datang di server lokal!

Mari kita telaah baris-baris kode di atas:

  1. Baris 1: Kita memuat urlopen() dari package urllib.request — satu-satunya import yang diperlukan untuk permintaan dasar.
  2. Baris 3 (urlopen(...)): Function tersebut membuka URL, menjalankan seluruh siklus HTTP (DNS, koneksi TCP, kirim request), lalu mengembalikan object respons mirip file yang bisa dibaca; parameter timeout=3 membatasi lamanya operasi jaringan.
  3. Baris 4-5: Attribute .status berisi kode status, sedangkan .headers bisa diakses seperti dictionary untuk membaca header response.
  4. Baris 6 (.read().decode(...)): Method .read() mengembalikan bytes isi body, sehingga perlu .decode("utf-8") sebelum dicetak sebagai teks.

Request dengan Header dan Query String #

Kebutuhan nyata biasanya lebih dari sekadar membuka URL: kita perlu query string dan header khusus. Function urlencode() merapikan parameter, sedangkan class Request membungkus URL beserta header:

python
from urllib.parse import urlencode
from urllib.request import Request, urlopen

query = urlencode({"kota": "Bandung", "kategori": "kuliner"})
url = f"http://127.0.0.1:8123/cari?{query}"
print("URL final :", url)

permintaan = Request(url, headers={"User-Agent": "belajar-python/1.0"})
with urlopen(permintaan, timeout=3) as respons:
    print("Respons    :", respons.read().decode("utf-8"))

Output yang Dihasilkan:

text
URL final : http://127.0.0.1:8123/cari?kota=Bandung&kategori=kuliner
Respons    : Hasil pencarian di Bandung

Function urlencode() mengubah dictionary menjadi query string yang sah — karakter khusus otomatis di-escape, misalnya spasi menjadi %20. Object Request menerima URL di argumen pertama; urlopen() kemudian menerima object tersebut alih-alih string, dan header User-Agent ikut terkirim bersama request.

Ketika Server Menjawab Error #

Perilaku urlopen() yang paling mengejutkan pemula: kode status 4xx dan 5xx tidak dikembalikan sebagai data, melainkan langsung dijadikan exception HTTPError:

python
from urllib.error import HTTPError
from urllib.request import urlopen

try:
    urlopen("http://127.0.0.1:8123/tidak-ada", timeout=3)
except HTTPError as err:
    print("Kode status :", err.code)
    print("Alasan       :", err.reason)
    print("Isi body     :", err.read().decode("utf-8"))

Output yang Dihasilkan:

text
Kode status : 404
Alasan       : Not Found
Isi body     : Halaman tidak ditemukan

Object HTTPError sekaligus berperan sebagai response — attribute .code berisi status, .reason berisi alasan, dan .read() masih bisa membaca body error dari server, misalnya pesan JSON penjelasan dari API publik.

Kapan Memilih urllib #

Aspekurllibrequests / httpx
InstalasiBawaan PythonPerlu pip install
Kode untuk permintaanAgak panjangRingkas
Session dan penggunaan koneksi ulangManualBawaan
Retry dan connection poolTulis sendiriBawaan / adapter

Aturan praktisnya sederhana: untuk skrip kecil sekali jalan di lingkungan yang tidak boleh menambah dependency, urllib memadai. Untuk aplikasi yang rutin berbicara dengan API — dengan session, retry, dan penanganan error terstruktur — library modern akan membalas waktu yang kita investasikan.