Mengambil Data dari API

Permintaan HTTP dengan requests, pagination, dan penghormatan rate limit.

Daftar isi

API menyediakan data dalam keadaan segar dan terstruktur, tetapi mengambilnya adalah keterampilan mengelola permintaan, bukan sekadar satu pemanggilan fungsi. Topik ini membedah anatomi satu permintaan dengan pustaka requests, pola pagination yang menarik seluruh halaman data, serta penghormatan pada batas laju penyedia. Setelah membacanya, pengambilan data ribuan baris dari layanan seperti API kualitas udara OpenAQ dapat dirancang dengan pola yang aman dan sopan.

Anatomi satu permintaan#

Satu permintaan HTTP terdiri atas URL dasar, parameter yang disebut query parameter, kepala permintaan yang disebut header, dan badan jawaban yang pada API modern berformat JSON. Pustaka requests membungkus semuanya dalam satu fungsi requests.get, dengan params untuk parameter dan headers untuk kepala permintaan seperti kunci API.

python Contoh: satu permintaan dengan parameter dan pemeriksaan status
# tidak-dijalankan
jawab = requests.get(
    "https://api.openaq.org/v3/locations",
    params={"limit": 100},
    headers={"X-API-Key": "ISI-KUNCI"},
    timeout=30,
)
jawab.raise_for_status()             # pangkatkan error bila status bukan 2xx
isi = jawab.json()

Dua baris pada contoh tersebut adalah kebiasaan yang wajib. Argumen timeout mencegah program menggantung selamanya apabila layanan tidak menjawab, dan raise_for_status() mengubah status HTTP gagal menjadi error Python yang terlihat, alih-alih membiarkan kode melanjutkan dengan data yang kosong. Kode status 200 berarti berhasil, 4 berarti kesalahan pada sisi permintaan, misalnya 401 tanpa kunci dan 429 karena melewati batas laju, sedangkan 5 berarti kesalahan di sisi penyedia.

Jawaban JSON diubah menjadi kamus Python dengan jawab.json(), dan struktur lapisannya ditentukan penyedia, umumnya berupa kamus berisi daftar results beserta informasi pagination.

Pagination: menarik seluruh halaman#

Penyedia membatasi jumlah baris per jawaban, sehingga data yang besar harus diambil halaman demi halaman. Polanya tetap pada semua penyedia, yaitu mengulang permintaan dengan nomor halaman yang bertambah, menambahkan isi ke hasil, dan berhenti pada tanda habis yang diberikan penyedia, umumnya halaman yang tidak penuh atau daftar yang kosong.

Simulasi berikut memperagakan pola itu tanpa jaringan, dengan ukuran halaman penuh tiga baris dan satu halaman akhir yang tidak penuh.

python pagination_simulasi.py
"""Simulasi pagination: pola pengulangan permintaan sampai halaman habis."""

def ambil_semua(halaman, ukuran_penuh=3):
    hasil = []
    for nomor, isi in enumerate(halaman, start=1):
        print(f"mengambil halaman {nomor}: {len(isi)} baris")
        hasil.extend(isi)
        if len(isi) < ukuran_penuh:      # halaman yang kurang dari penuh = terakhir
            break
    return hasil

halaman = [
    [{"id": f"m{i:03d}"} for i in range(1, 4)],
    [{"id": f"m{i:03d}"} for i in range(4, 7)],
    [{"id": "m007"}],                    # halaman terakhir, tidak penuh
]

data = ambil_semua(halaman)
print("total baris :", len(data))
print("id terakhir :", data[-1]["id"])
text Keluaran
mengambil halaman 1: 3 baris
mengambil halaman 2: 3 baris
mengambil halaman 3: 1 baris
total baris : 7
id terakhir : m007

Syarat berhenti pada contoh itu adalah halaman yang berisi kurang dari ukuran penuh. Sebagian penyedia memakai tanda lain, misalnya kolom meta.total atau tautan next, dan menyesuaikan syarat itu dengan dokumentasi penyedia adalah langkah pertama sebelum menulis pengulangan. Kesalahan yang paling mahal adalah syarat berhenti yang tidak pernah terpenuhi, karena program akan terus meminta halaman baru sampai kuota habis.

Batas laju dan sopan santun#

Penyedia membatasi laju permintaan dengan rate limit, yaitu jumlah permintaan maksimum dalam rentang waktu tertentu, dan melampinya dengan status 429. Menghormatinya adalah persyaratan teknis dan etis sekaligus, karena pengambilan yang serakah membebani layanan dan dapat menggugurkan akun.

Tiga kebiasaan menjaga batas tersebut. Pertama, jeda antar permintaan dengan time.sleep, sekecil satu detik, yang membuat pengambilan ribuan halaman tetap wajar. Kedua, backoff pada jawaban 429 atau 5xx, yaitu menunggu lebih lama pada setiap percobaan ulang alih-alih menyerang ulang seketika. Ketiga, cache lokal, yaitu menyimpan jawaban ke berkas agar pengambilan ulang pada besok tidak meminta ulang halaman yang lama.

Kebiasaan ketiga tersebut paling jarang dilakukan dan paling menghemat. Data historis tidak berubah, sehingga cukup diambil sekali, sedangkan pengambilan berikutnya hanya menambah halaman terbaru. Pola ini dipakai ulang pada modul reproduktibilitas ketika dataset latihan diperbarui secara terkendali.

Pola lengkap pada penyedia nyata#

Skrip berikut menggabungkan seluruh pola menjadi satu fungsi pengambilan pada API kualitas udara OpenAQ, lengkap dengan parameter, kunci, pagination, dan jeda. Ia ditandai tidak-dijalankan karena menuntut kunci API dan jaringan, dan yang dipelajari adalah bentuk polanya.

python ambil_api.py
# tidak-dijalankan
"""Pola permintaan API dengan requests: pagination dan hormat pada batas laju.

Ditandai tidak-dijalankan karena membutuhkan kunci API dan jaringan; bentuk
polanya tetap yang dipelajari.
"""
import time

import requests

def ambil_semua():
    url = "https://api.openaq.org/v3/measurements"
    hasil, halaman = [], 1
    while True:
        jawab = requests.get(
            url,
            params={"limit": 1000, "page": halaman},
            headers={"X-API-Key": "ISI-KUNCI"},
            timeout=30,
        )
        jawab.raise_for_status()
        isi = jawab.json()["results"]
        if not isi:
            return hasil
        hasil.extend(isi)
        halaman += 1
        time.sleep(1)                    # hormati batas laju penyedia

Hasil kembalian hasil berupa daftar kamus yang langsung dapat dimasukkan ke pd.DataFrame, dan pekerjaan selanjutnya kembali ke wilayah pandas, yaitu memeriksa tipe, meratakan kolom bersarang, dan menyimpan potongan ke berkas sebelum halaman berikutnya diambil agar kegagalan jaringan tidak membuang seluruh kemajuan.

Kesalahan umum#

Kesalahan pertama adalah memanggil jawab.json() tanpa memeriksa status. Badan jawaban gagal berisi pesan error, bukan data, sehingga error KeyError yang membingungkan muncul jauh dari sebabnya. raise_for_status() setelah setiap permintaan membuat kesalahan muncul pada tempatnya.

Kesalahan kedua adalah syarat berhenti pagination yang salah, umumnya karena menganggap halaman penuh selalu penuh. Halaman terakhir yang kebetulan penuh membuat pengulangan meminta satu halaman lagi yang kosong, dan pola yang benar menangani kedua tanda tersebut, yaitu daftar kosong maupun halaman yang tidak penuh.

Kesalahan ketiga adalah menyimpan seluruh hasil di memori untuk dataset yang jutaan baris. Menulis per halaman ke berkas atau tabel, lalu menggabungkannya di akhir, membuat pengambilan tahan terhadap kegagalan di tengah jalan.

Ringkasan#

  • Satu permintaan API terdiri atas URL, parameter, header, dan badan JSON, dengan timeout dan raise_for_status() sebagai kebiasaan wajib.
  • Pagination adalah pengulangan permintaan yang berhenti pada tanda habis dari penyedia, dan syarat berhentinya menentukan kebenaran seluruh hasil.
  • Batas laju dihormati dengan jeda, backoff, dan cache lokal, bukan dengan permintaan yang dipaksa.
  • Hasil pengambilan disimpan per halaman agar kegagalan jaringan tidak membuang kemajuan yang sudah diperoleh.

Referensi#