Skip to content

GraphQL dari Bundle Situs: 7 Permintaan Ganti 533 Halaman

Adityo Guni Waluyo

Menemukan endpoint GraphQL situs dari bundle JS publik: dataset 5330 baris cukup 7 permintaan, bukan 533 halaman HTML.

Ringkasan

Awalnya penulis kira harus scraping 533 halaman HTML tiap hari, ternyata dugaan itu meleset total. Setelah intip file JavaScript situsnya, ketemu API GraphQL GetBlacklists yang bisa ambil data tanpa login sama sekali. Cuma butuh 7 request sekitar 10 detik buat dapetin 5330 baris, parsing HTML pun langsung dipensiunkan deh.

1. Judul (H1) Saat merancang sistem pemantauan daftar hitam penyedia, saya membayangkan harus mengurai 533 halaman HTML yang dipaginasi setiap hari. Rencana awal adalah mengambil cuplikan HTML halaman pertama yang dirender di server, dengan asumsi halaman yang lebih dalam digerakkan oleh JavaScript dan sulit diakses secara terprogram. Dugaan awal saya keliru. Saya mengasumsikan data di halaman kedua dan seterusnya dikunci di balik mekanisme otentikasi, CAPTCHA, atau rendering sisi klien yang kompleks sehingga memerlukan otomatisasi peramban yang berat. Fakta yang muncul justru berbeda. Dengan memeriksa berkas JavaScript publik yang dimuat oleh portal, saya menemukan bahwa tabel yang terlihat di antarmuka sebenarnya diisi oleh API GraphQL milik situs itu sendiri. Proses menemukan graphql dari bundle situs ini mengubah seluruh pendekatan pengambilan data. Kueri bernama GetBlacklists mengarah ke titik akhir /graphql tanpa memerlukan otentikasi, CAPTCHA, atau kuki apa pun. Petunjuk terbaiknya malah datang dari tombol ekspor XLSX di situs itu. Fiturnya memanggil perPage: 800, bentuk permintaan yang jelas disetujui server karena situsnya sendiri yang memakainya.

Mekanisme di Balik Layar

Next.js secara default merender layout dan halaman sebagai Server Components, namun komponen klien tetap digunakan untuk penanganan keadaan dan API khusus peramban [8]. Dalam kasus portal daftar-hitam.example.id, seluruh operasi data dipusatkan pada satu titik akhir HTTP tunggal [10]. Fitur introspeksi GraphQL memungkinkan klien untuk mempelajari jenis, bidang, dan deskripsi skema secara langsung dari server [11]. Meskipun paginasi berbasis offset memiliki kelemahan performa dan keamanan pada dataset yang sangat besar [9], struktur ini justru menjadi celah efisiensi untuk dataset berukuran menengah seperti ini. Dalam probe saya, server tidak memprotes tujuh permintaan berurutan yang dijeda satu detik.

Isi Dataset Setelah Dihitung Ulang

Satu permintaan dengan parameter perPage: 800 mengembalikan halaman 1 dari 7, dengan nilai totalData sebesar 5330. Penyapuan penuh hanya memerlukan tujuh permintaan dalam waktu sekitar 10 detik. Saya menerapkan agen pengguna bot dengan batas kesopanan 1 permintaan per detik. Seluruh dataset dapat diambil melalui satu perintah terprogram, bukan 533 halaman HTML yang harus diurai satu per satu. Dataset ini jauh lebih kaya daripada tabel web yang ditampilkan. Data mentah mencakup nomor NPWP wajib pajak yang disamarkan, alamat penyedia, nilai kontrak pagu atau HPS, teks peraturan pelanggaran, asal LPSE, KLDI, atau satker, nomor SK, serta stempel waktu publikasi dan kedaluwarsa. Dari berkas JSON 7,7 MB yang saya hitung ulang sendiri, isinya 5330 baris, campuran status terdiri dari 348 berstatus PUBLISHED, 4916 EXPIRED, 63 CANCELED, dan 3 CANCELED_TEMPORARY. NPWP yang disamarkan hadir pada 4169 baris. Nilai pagu atau HPS tender tercatat pada 3006 baris, atau sekitar 56 persen dari total. Tanggal mulai membentang dari tahun 1905 yang merupakan satu baris data anomali, hingga tahun 2026. Terdapat 27 baris yang dimulai pada periode September hingga Oktober 2026, yang menjadi jendela sinyal segar yang dibutuhkan oleh sistem ringkasan harian. Total terdapat 4260 nama penyedia yang unik.

Dampak pada Arsitektur Pengambilan Data

Penemuan ini sepenuhnya menggantikan keputusan awal untuk mengambil cuplikan HTML halaman pertama setiap hari. Strategi mengambil graphql dari bundle situs terbukti jauh lebih andal dan efisien. Proses ingest sekarang menggunakan klien HTTPS GraphQL biasa. Sistem melakukan sinkronisasi dataset penuh harian sebanyak 7 permintaan, lalu melakukan deduplikasi berdasarkan pengenal baris. Hasil dump 5330 baris ini menjadi basis historis hari pertama. Data lama tidak pernah diambil ulang untuk menghemat sumber daya. Polanya pendek saja:
import requests

url = "https://daftar-hitam.example.id/graphql"
headers = {
    "User-Agent": "DataBot/1.0 (Research Purpose)",
    "Content-Type": "application/json"
}

payload = {
    "query": """
    query GetBlacklists($page: Int, $perPage: Int) {
        blacklists(page: $page, perPage: $perPage) {
            data {
                id
                providerName
                status
                npwp
                paguHps
                startDate
                endDate
            }
            pageInfo {
                currentPage
                totalPages
                totalData
            }
        }
    }
    """,
    "variables": {"page": 1, "perPage": 800}
}

response = requests.post(url, json=payload, headers=headers)
print(response.json())
Jalur penguraian HTML pun pensiun. Berkas JSON halaman pertama hanya disimpan sebagai bukti asal usul jalur tersebut, bukan sebagai sumber data utama. Keputusan ini menghilangkan kerumitan pemeliharaan pengurai HTML yang rapuh dan menggantinya dengan kontrak data yang terstruktur dan dapat diandalkan.

Sumber

  1. Next.js Docs: Server and Client Components
  2. GraphQL Learn: Pagination
  3. GraphQL Learn: Serving over HTTP
  4. GraphQL Learn: Introspection

Artikel terkait