Kursus Python / Mengekstrak data HTML dengan Beautiful Soup

Untuk mengambil data dari HTML, gunakan parser untuk memilih elemen dan membaca teks atau atributnya. Periksa juga jumlah elemen serta kolom wajib, karena perubahan struktur dokumen dapat membuat ekstraksi gagal tanpa terlihat jelas. Latihan ini memakai HTML buatan sendiri dan tidak mengirim permintaan ke situs mana pun.

Memulai dari dokumen statis yang terkendali

HTML memiliki struktur elemen bertingkat. Parser mengubahnya menjadi pohon yang dapat ditelusuri, sehingga Anda dapat memilih daftar tertentu lalu membaca nama dan jumlah pada tiap item. Ini berbeda dari mencari pola teks dengan regular expression: hubungan elemen dan atribut lebih tepat diperlakukan sebagai struktur dokumen.

Beautiful Soup merupakan pustaka pihak ketiga. Nama paket pemasangannya adalah beautifulsoup4, sedangkan nama impornya bs4. Aktifkan lingkungan virtual proyek dan pasang dengan interpreter yang akan menjalankan contoh:

python -m pip install beautifulsoup4

Contoh menggunakan backend html.parser yang tersedia pada pustaka standar Python. Memilih parser secara eksplisit membantu menjaga konteks pengujian. Dokumen tidak valid dapat menghasilkan pohon berbeda pada parser berbeda, sehingga jangan menganggap seluruh parser selalu menafsirkan markup rusak dengan cara yang sama.

Jika kelak menambahkan pengambilan halaman luar, pisahkan tahap HTTP dari ekstraksi. Tetapkan akses yang sesuai, syarat layanan, frekuensi permintaan, dan penanganan data pribadi menurut sumbernya. Latihan lokal ini cukup untuk memahami pemilihan elemen tanpa bergantung pada jaringan atau halaman yang berubah.

Menjalankan ekstraksi lengkap

Buat folder latihan baru dan simpan kode berikut sebagai parse_html.py dengan UTF-8. Gunakan Python 3.12 atau lebih baru dan jalankan python parse_html.py pada terminal lingkungan proyek. Jika nama interpreter Anda adalah python3, gunakan nama itu secara konsisten.

from bs4 import BeautifulSoup

html = """<ul id="expenses">
<li data-amount="3500"><span class="name">coffee</span></li>
<li data-amount="8000"><span class="name">lunch</span></li>
</ul>"""
soup = BeautifulSoup(html, "html.parser")
rows = soup.select("#expenses > li")
if len(rows) != 2:
    raise ValueError("unexpected row count")
total = 0
for row in rows:
    name = row.select_one(".name")
    amount_text = row.get("data-amount")
    if name is None or not amount_text or not amount_text.isascii() or not amount_text.isdecimal():
        raise ValueError("missing or invalid field")
    amount = int(amount_text)
    print(name.get_text(" ", strip=True), amount)
    total += amount
print("total:", total)

Hasil yang diharapkan:

coffee 3500
lunch 8000
total: 11500

Dokumen berisi dua item langsung di bawah daftar dengan ID expenses. Setiap item memiliki satu elemen nama dan atribut jumlah. Setelah kedua item dibaca, totalnya adalah 3500 + 8000 = 11500. Teks coffee dan lunch dipertahankan sebagai data contoh agar hasil mudah dibandingkan.

Nama diambil dari teks elemen, sedangkan jumlah diambil dari atribut data-amount. Mengubah tampilan warna atau ukuran teks tidak otomatis mengubah kedua nilai tersebut. Ekstraksi ini membaca HTML yang diberikan ke parser, bukan hasil tangkapan layar.

Membaca selector, kecocokan opsional, dan teks bertingkat

#expenses > li memilih elemen li yang menjadi anak langsung elemen ber-ID expenses. Tanda > berbeda dari spasi selector keturunan: li yang ditempatkan di dalam pembungkus tambahan tidak lagi menjadi anak langsung. Pembatasan ini membantu menghindari item dari daftar lain atau daftar bersarang.

.name memilih kelas name. select menghasilkan kumpulan kecocokan, sedangkan select_one menghasilkan elemen pertama atau None ketika tidak ditemukan. Program memeriksa None sebelum memanggil metode teks agar struktur yang berubah menghasilkan kegagalan yang dapat dikenali.

get_text(" ", strip=True) menggabungkan bagian teks bertingkat dengan spasi dan membuang spasi tepi setiap bagian. Jika nama menjadi <span class="name"><b>iced</b> coffee</span>, hasilnya dapat dibaca sebagai iced coffee, bukan penyambungan tanpa pemisah.

Jumlah harus memiliki setidaknya satu karakter, hanya ASCII, dan seluruhnya digit desimal. Kombinasi isascii() dan isdecimal() menolak tanda minus, pecahan, serta digit dari sistem tulisan lain. Ini adalah kebijakan format latihan, bukan sifat umum semua angka yang dapat dikonversi oleh Python.

Parser tidak menjalankan JavaScript. Data yang terlihat pada browser mungkin dibuat setelah respons HTML diterima dan tidak ada pada teks yang diberikan ke Beautiful Soup. Bandingkan sumber respons dengan halaman hasil render sebelum menyimpulkan selector salah. Dokumentasi Beautiful Soup menjelaskan pilihan parser dan pencarian elemen.

Mendeteksi perubahan dan menetapkan kebijakan gagal

  1. Ubah kelas name pada salah satu elemen HTML menjadi nama lain. Jalankan ulang dan pastikan muncul missing or invalid field.
  2. Ganti jumlah 3500 dengan -1, lalu abc pada percobaan terpisah. Keduanya harus ditolak.
  3. Tambahkan tag bertingkat di dalam nama dan periksa spasi teks yang dihasilkan. Setelah itu, pulihkan markup awal.
  4. Ubah ID daftar sehingga selector tidak menemukan dua item. Pemeriksaan jumlah harus gagal, bukan mencetak total nol sebagai laporan sukses.

Program mengharapkan tepat dua item karena dokumen latihannya diketahui. Sistem nyata memerlukan kebijakan yang masuk akal untuk jumlah, rentang, kolom wajib, dan konteks pengambilan. Memperlakukan semua hasil kosong sebagai sukses dapat menyembunyikan perubahan halaman, sedangkan mewajibkan jumlah tetap tanpa alasan juga dapat menolak perubahan data yang sah.

Validasi contoh belum memeriksa seluruh aturan bisnis. Elemen nama yang ada tetapi kosong masih diterima sebagai string kosong, dan beberapa elemen name dalam satu item membuat select_one memilih yang pertama. Jika kontrak Anda mewajibkan nama tidak kosong atau tepat satu elemen, tambahkan pemeriksaan tersebut dan uji perilakunya.

Keluaran dicetak saat setiap item diproses. Jika item kedua salah, baris pertama mungkin sudah tercetak sebelum program berhenti. Jangan menganggap stdout sebagian sebagai laporan lengkap; periksa status keluar. Untuk hasil yang harus diterbitkan hanya setelah seluruh dokumen valid, kumpulkan dan validasi record terlebih dahulu, kemudian tampilkan hasil akhirnya.