BeautifulSoup adalah library yang hampir selalu dipakai orang pertama kali saat mereka mulai melakukan web scraping di Python, dan itu memang bukan tanpa alasan: ini parser HTML serius yang paling lambat. Dua hal itu sama-sama benar, dan itu bukan berarti jelek. Yang menarik adalah bahwa label βpaling lambatβ ternyata bukan cuma kesan umum β melainkan angka yang bisa diukur dan benar-benar perlu dipertimbangkan.
Saya menguji bs4 (yakni beautifulsoup4, versi 4.15.0, rilis Juni 2026, berlisensi MIT) dengan gabungan tes kemampuan terbaru dan data timing yang diambil ulang dari rig benchmark yang sama. Hasilnya konsisten: kamu menukar penalti kecepatan sekitar satu orde magnitudo demi API yang paling ramah dan toleransi error terkuat di kelasnya. Apakah pertukaran itu masuk akal sangat bergantung pada workload kamu, jadi ulasan ini membahas kedua sisinya secara seimbang.
Apa Sebenarnya BeautifulSoup Itu β dan Bukan Apa
Kebanyakan tutorial melewatkan bagian yang paling penting: BeautifulSoup tidak mem-parse HTML. Ia adalah pembungkus. Di balik layar, ia menyerahkan dokumen kamu ke salah satu dari tiga parser asli β html.parser bawaan Python, lxml, atau html5lib β lalu membungkus tree yang dihasilkan menjadi satu API navigasi dan pencarian yang sangat ramah. Tugas bs4 bukan mem-parse. Tugasnya adalah membuat hasil parse itu enak dijelajahi.
Penulisnya sendiri menyebutnya sebagai "screen-scraping library", dan tawarannya sejak awal memang begitu: arahkan ke HTML yang begitu berantakan sampai browser pun ikut mengernyit, dan ia tetap akan mengambil data yang kamu minta. Reputasi itu memang pantas, dengan satu catatan yang akan kita bahas nanti.
Beberapa fakta dasar yang perlu dikunci dulu:
| Field | Value |
|---|---|
| Package | beautifulsoup4 (import sebagai bs4) |
| Versi yang diuji | 4.15.0 (diunggah 2026-06-07) |
| Kebutuhan Python | >=3.7.0 |
| Lisensi | MIT |
| Beranda resmi | crummy.com/software/BeautifulSoup |
| Source + bug tracker | Launchpad β bukan GitHub |
| Pemeliharaan | Aktif (4.15.0 pada Juni 2026, enam rilis dalam setahun terakhir) |
Bagian "bukan GitHub" itu lebih penting daripada kelihatannya. bs4 adalah library berumur 20 tahun yang hidup di crummy.com dan Launchpad, jadi kebiasaan menilainya dari jumlah bintang GitHub tidak relevan. Ukuran kesehatan yang lebih masuk akal adalah ritme rilis; dari ukuran itu, kondisinya jelas masih hidup dan sehat.
Satu detail soal lisensi yang penting buat tim compliance: wrapper-nya memang MIT, tetapi apa yang benar-benar ikut masuk ke dependency tree saat kamu memakai bs4 bergantung pada backend yang dipilih. html.parser adalah bagian dari standard library Python (lisensi PSF, tanpa dependency tambahan). lxml berlisensi BSD, tetapi bergantung pada libxml2/libxslt β dependency C eksternal yang harus kamu kompilasi sendiri atau ambil sebagai wheel prebuilt. html5lib murni Python dan MIT. Kalau kamu menginginkan footprint dependency paling bersih, parser bawaan html.parser memberikannya β yang kebetulan juga menjadi backend dengan satu masalah terbesar. Nanti kita bahas.
Pajak Kecepatan, Dibuat Kuantitatif
Mari langsung taruh angkanya di depan, karena ini inti beritanya dan menyembunyikannya bukan hal yang jujur. Pada tugas parse lalu ekstrak yang realistis β parse string, ambil setiap <h3 class="title"> dan setiap <a href> β BeautifulSoup adalah parser paling lambat dalam perbandingan ini, dan jaraknya jauh.

Timing ini diambil ulang dari rig benchmark selectolax (mesin yang sama, metodologi 3 kali run yang sama, per 2026-07-13); ulasan ini tidak menjalankan benchmark timing baru untuk menghindari kontensi CPU dan duplikasi kerja. Median latensi p50, dalam milidetik:
| Ukuran halaman | bs4 (html.parser) | bs4 (lxml) | selectolax-Lexbor | lxml | bs4-hp lebih lambat | bs4-lxml lebih lambat |
|---|---|---|---|---|---|---|
| 1 KB | 0.323 | 0.281 | 0.027 | 0.036 | 12.0x | 10.5x |
| 10 KB | 2.049 | 1.705 | 0.160 | 0.166 | 12.8x | 10.7x |
| 100 KB | 20.599 | 16.540 | 1.464 | 1.423 | 14.1x | 11.3x |
| 1 MB | 232.558 | 181.855 | 14.901 | 14.177 | 15.6x | 12.2x |
| 10 MB | 2788.746 | 2261.557 | 159.935 | 172.933 | 17.4x | 14.1x |
Jadi bs4(html.parser) berjalan sekitar 12β17x lebih lambat daripada parser C seperti selectolax-Lexbor, dan berpindah ke backend lxml hanya memangkasnya menjadi 10.5β14x β tetap tertinggal satu orde magnitudo penuh. Penyebabnya struktural, bukan bug: apa pun backend yang melakukan parse, bs4 membangun objek Python lengkap (Tag atau NavigableString) untuk setiap node. Lapisan materialisasi objek itu adalah pajak yang tidak dibayar oleh parser C.
Perhatikan bahwa pengali ini naik saat halaman membesar β 12.0x di 1 KB, 17.4x di 10 MB. Itu menunjukkan ini bukan overhead startup tetap yang bisa βdiamortisasiβ. Ini adalah pajak per-node yang naik linear seiring jumlah node yang dibangun.
Sekarang sudut pandangnya: "10x lebih lambat" terdengar lebih seram daripada biasanya. Pada halaman 1 MB, angkanya 232 ms versus 15 ms. Kalau pekerjaan kamu adalah "scrape beberapa ratus sampai beberapa ribu halaman, masing-masing beberapa ratus KB," perbedaan absolut itu nyaris tak terasa β kamu tidak akan merasakannya, dan mengoptimalkannya tidak memberi manfaat berarti. Tapi kalau pekerjaan kamu adalah pipeline satu juta halaman, rasio yang sama menjadi pembeda antara job yang selesai dan job yang gagal. Angkanya sama, kesimpulannya bisa berlawanan. Nilailah berdasarkan volume nyata, bukan semata benchmark.
Tidak, Ganti Backend Tidak Menyelesaikan Masalah Ini
Ada mitos yang terus beredar bahwa kalau bs4 diberi backend lxml, maka kecepatan lxml akan ikut didapat. Itu tidak benar, dan penting untuk dipahami alasannya. Pada query CSS batch 100.000 node (pilih semua <a> dan baca href-nya, tree sudah terbentuk), perbedaan throughput-nya sangat jelas:
| Parser | Query p50 | Nodes/detik |
|---|---|---|
| lxml | 33.30 ms | 3,002,646 |
| selectolax-Modest | 34.19 ms | 2,924,550 |
| selectolax-Lexbor | 39.46 ms | 2,534,027 |
| bs4 (lxml) | 250.56 ms | 399,111 |
bs4(lxml) hanya mencapai sekitar 399.000 node/detik β kira-kira 6.3β7.5x lebih lambat daripada tiga engine C, meskipun backend-nya sendiri memang lxml. Backend mempercepat fase pembentukan tree. Namun query dan traversal tetap melewati soupsieve ke objek bs4 Tag, dan setiap node yang cocok tetap dibungkus ulang dalam Python. Jadi model mental "kasih bs4 lxml, lalu ia akan secepat lxml" itu keliru: backend hanya mempercepat satu tahap, sementara tahap paling lambat bukan tahap itu.
Memori dan cold start menambah biaya totalnya. Pada dokumen 10 MB, bs4 memakai sekitar 1.5β1.75x resident memory dibanding selectolax atau lxml (218β226 MB versus 129β145 MB) β akar masalahnya sama, yaitu satu objek Python per node. Dan impor bs4 memakan sekitar 33.4 ms versus 14.1 ms untuk lxml.html, jadi 2.36x lebih lambat saat import. Poin terakhir ini bukan masalah besar untuk proses yang berjalan lama, tetapi untuk CLI tool atau serverless function yang sering cold start, ini adalah biaya kecil yang nyata dan layak diketahui.
Kenapa Menambah Thread Tidak Akan Menolong
Kalau insting kamu saat menghadapi tugas CPU-bound yang lambat adalah "lempar saja thread ke sini," bs4 akan menghukum insting itu. Pada halaman 1 MB yang diparse 48 kali, perbandingan single-thread versus empat thread seperti ini:
| Parser | 1 thread | 4 thread | Speedup |
|---|---|---|---|
| selectolax-Lexbor | 0.563 s | 0.159 s | 3.54x |
| lxml | 0.459 s | 0.378 s | 1.21x |
| bs4 (lxml) | 6.945 s | 26.842 s | 0.26x |
Baca baris terbawah itu dua kali. Empat thread justru membuat bs4 sekitar 3.9x lebih lambat, bukan lebih cepat. Sinyal empirisnya adalah "kemungkinan besar masih memegang GIL": pembuatan tree bs4 murni Python, jadi semuanya terserialisasi di bawah Global Interpreter Lock, dan menumpuk thread hanya menambah overhead penjadwalan untuk pekerjaan yang sebenarnya tidak bisa berjalan paralel. selectolax mendapat speedup sekitar 3.5x karena core C-nya melepaskan lock; bs4 tidak punya ruang seperti itu.
Untuk era free-threading, kesimpulan praktisnya: kalau kamu perlu mem-parallel-kan BeautifulSoup, pilih multiprocessing (ProcessPoolExecutor), bukan threads. selectolax dan lxml bisa diskalakan dengan threads; bs4 tidak. Satu catatan kehati-hatian: ini adalah satu observasi pada satu jumlah thread (4) dan satu ukuran halaman (1 MB), dan mekanisme "memegang GIL" adalah hipotesis yang disimpulkan dari perilaku wall-clock, bukan sesuatu yang saya verifikasi dengan instrumentasi kode untuk melihat jalur mana yang menahan lock. Arah kesimpulannya jelas; mekanisme pastinya masih bersifat sementara.
Backend Default Itu Jebakan. Baca Ini Dulu.
Kalau kamu hanya mengambil satu pelajaran dari ulasan ini, ambil ini. BeautifulSoup(html) tanpa argumen kedua memakai html.parser, dan html.parser tidak mengimplementasikan aturan optional-end-tag milik HTML5. Kedengarannya akademis, sampai diam-diam merusak data kamu.

Saya menjalankan 15 sampel HTML yang sengaja dirusak melalui ketiga backend, dengan assertion struktural agnostik-backend yang sudah ditetapkan sebelum pengujian dimulai (jadi tidak ada yang bisa memilih pemenang setelah hasil keluar). Skornya:
| Backend | Memenuhi ekspektasi / 15 |
|---|---|
| lxml | 15 |
| html5lib | 15 |
| html.parser | 12 |
Tiga kegagalan itu punya akar masalah yang sama. Ambil contoh tabel yang tidak ditutup: <table><tr><td>a<td>b<tr><td>c<td>d</table>. Pada html.parser, teks sel yang diekstrak menjadi ['abcd','bcd','cd','d'] β setiap <td> menelan semua isi setelahnya karena parser menanamkan sel-sel itu alih-alih menutupnya. lxml dan html5lib sama-sama mengembalikan ['a','b','c','d'] dengan benar. Item list yang berdiri sendiri juga sama: <li>a<li>b<li>c menghasilkan struktur bertingkat ['abc','bc','c'] di html.parser, dan ['a','b','c'] yang bersih pada dua backend lainnya. Atribut duplikat juga berbeda β <div id="first" id="second"> mempertahankan "second" di html.parser tetapi "first" di lxml/html5lib, padahal spesifikasi HTML5 meminta atribut pertama yang dipertahankan.
Mengapa ini berbahaya, bukan sekadar menjengkelkan? Karena semua itu terjadi tanpa memunculkan error. Scraper yang asal memanggil BeautifulSoup(html) lalu menemui tabel atau list yang tidak ditutup β sesuatu yang sangat umum pada situs lama, HTML buatan tangan, dan template yang lupa menutup tag β akan mencampur teks sel yang bersebelahan menjadi satu field, mengirim data kotor, dan sama sekali tidak protes. Solusinya cuma satu argumen: BeautifulSoup(html, "lxml") atau BeautifulSoup(html, "html5lib").
Kalau kita adil pada html.parser, 12 dari 15 sampel rusak lainnya memang menghasilkan output yang sama di semua backend: tag yang salah susun seperti <b><i></b></i>, skeleton html/body yang hilang, atribut tanpa tanda kutip, closing tag yang yatim, komentar yang tidak ditutup, nested form, campuran huruf besar-kecil, dan lain-lain. Jadi toleransi bs4 memang kuat secara umum; perbedaannya hampir seluruhnya terkonsentrasi pada keluarga optional-end-tag. Dan ini bukan temuan baru β dokumentasi bs4 sendiri di bagian "Differences between parsers" sudah bilang bahwa html.parser "less lenient". Nilai tambah dari matriks HTML rusak di sini adalah contoh konkret dan bisa direproduksi di mana "kurang toleran" berubah menjadi output yang salah.
Apa yang Tidak Kamu Korbankan: API dan CSS Justru Bagian Terbaiknya
Jadi bs4 itu lambat, single-threaded, dan punya jebakan backend default. Tapi orang tetap memakainya karena sisi "ramah" dari trade-off ini memang nyata β dan hasil pengujian juga membuktikannya.

Saya menjalankan 29 probe API yang mencakup search, CSS, navigasi tree, ekstraksi teks, dan modifikasi DOM. Semua 29 lolos, dan setiap hasilnya dihitung dengan membandingkan return aktual terhadap nilai yang diharapkan, bukan dinilai sekilas. Dua kemampuan ini sangat ergonomis dan memang tidak ditawarkan oleh parser C:
- Predikat fungsi di
find/find_all. Kamu bisa menulissoup.find(lambda t: t.name == "a" and "btn" in t.get("class", []))dan mengekspresikan kondisi yang rumit dalam satu baris Python β tanpa perlu langkah dua tahap "select semua, lalu filter". - Navigasi tree bernama dan dua arah.
.parent,.next_sibling,.find_parent,.stripped_strings,.descendantsβ traversal-nya terbaca seperti bahasa alami dan bisa bergerak ke dua arah. selectolax membutuhkan beberapa langkah untuk sebagian fungsi ini, atau bahkan tidak menyediakannya sama sekali.
Itulah sisi "menghemat waktu developer" yang benar-benar terasa. Ini bukan marketing; ini 29 cek hijau.
Ada dua jebakan yang perlu dicatat, karena ulasan yang adil harus menyebutkan keduanya. Pertama, boolean attribute: <input disabled> mengembalikan string kosong "" untuk disabled di bs4 (selectolax mengembalikan None). Keduanya bernilai falsy, jadi if node.get("disabled") diam-diam gagal mendeteksi boolean attribute yang sebenarnya ada di kedua library. Tes aman adalah "disabled" in tag.attrs. Kedua, get_text(strip=True) menggabungkan teks node tanpa pemisah setelah stripping, jadi "...with " + "link1" menjadi "withlink1". Gunakan separator=" " ketika kamu butuh batas kata. Kedua jebakan ini bukan khusus bs4; keduanya adalah masalah lintas library.
Dan sekarang bagian yang sering mengejutkan orang: memilih bs4 tidak membuat kamu kehilangan coverage CSS. Mesin CSS-nya, soupsieve, adalah implementasi paling lengkap dalam seluruh perbandingan ini. Pada matriks dasar 41 kasus (diambil ulang dari rig selectolax), soupsieve mencetak 41/41 β satu-satunya skor sempurna di lapangan, melampaui selectolax-Lexbor yang 39/41 dan cssselect (lxml/parsel) yang 37/41. Lalu saya menjalankan 20 kasus tambahan yang didokumentasikan sebagai dukungan extended, dan hasilnya 20/20, termasuk selector yang ditolak mentah-mentah oleh Lexbor: :lang(en), selector khusus soupsieve :-soup-contains('featured'), :is(), :where(), dan :has(> a). Satu-satunya kekurangan yang benar-benar terasa adalah XPath (soupsieve hanya CSS) dan pseudo-element parsel ::text / ::attr(), yang merupakan ekstensi Scrapy. Kalau kamu hidup di dunia XPath, migrasi itu akan terasa berat.
Kesimpulan untuk bagian ini jelas: yang kamu korbankan dengan memilih BeautifulSoup adalah kecepatan. Bukan ergonomi API, dan jelas bukan coverage CSS.
Dua Jebakan Produksi yang Layak Kamu Anggarkan
Selain backend default, ada dua perilaku yang bisa menggigit kamu, khususnya dalam workload jangka panjang atau yang non-UTF-8.
Siklus Referensi: Panggil decompose() di Loop Panjang
Setiap Tag bs4 menyimpan referensi ke parent dan ke child-nya, sehingga terbentuk reference cycle. Reference counting di CPython tidak bisa membebaskan siklus sendirian β itu tugas generational garbage collector. Untuk melihat seberapa besar pengaruhnya, saya membangun lalu menghapus tree sebanyak 300 kali dengan GC dimatikan, lalu menghitung objek Tag yang masih bertahan di memori:

| Skenario | Tags yang masih tertahan setelah del |
|---|---|
| GC off | 120,900 (300 siklus, tidak ada yang dibebaskan) |
| GC on | 26,598 (generational GC berjalan di tengah loop) |
Setelah gc.collect() dipaksa | 0 (semua dibebaskan) |
| Kontrol tanpa siklus (list string, GC off) | delta 0 |
Saat GC dimatikan, del soup tidak membebaskan apa pun β semua 120.900 objek tetap resident, karena siklus referensi mengalahkan reference counting. Satu kali gc.collect() membersihkan semuanya. Grup kontrol tanpa siklus (list string biasa, yang sudah diketahui tidak punya cycle) menunjukkan delta nol, membuktikan bahwa penumpukan itu berasal dari siklus bs4, bukan noise pengukuran. Dokumentasi bs4 sendiri menyebut objek-objek ini "densely interconnected ... exactly the sort a garbage collector would have trouble with", jadi ini adalah perilaku yang terdokumentasi; pengujian ini menambah jumlah objek yang tertahan dan bukti bahwa collect() mengembalikannya ke nol.
Aturan praktisnya: dalam pipeline yang mem-parse banyak halaman besar di loop ketat, kalau kode kamu (atau konfigurasi throughput tinggi) mematikan GC atau tidak cukup sering memicunya, tree bs4 akan bertahan lebih lama dan memori naik. Panggil soup.decompose() setelah tiap halaman β bs4 menyediakannya justru untuk memutus siklus dan membebaskan memori lebih awal. Tree C dari selectolax dan lxml tidak punya masalah ini sama sekali.
Encoding: UnicodeDammit adalah Keunggulan Tersenyap bs4
bs4 membawa komponen yang tidak dimiliki parser cepat: UnicodeDammit, yang menebak encoding dokumen dan mengonversinya ke Unicode secara otomatis. Saya memberinya matriks 8 kasus "deklarasi vs charset aktual":

| Kasus | Encoding asli | Tebakan UnicodeDammit | Pulih? |
|---|---|---|---|
| utf8_no_decl | utf-8 | utf-8 | Ya |
| utf16_bom | utf-16 | utf-16le | Ya |
| gbk_chinese | gbk | gb18030 | Ya (superset) |
| shiftjis | shift_jis | cp932 | Ya (superset) |
| latin1_declared_utf8 | latin-1 (dideklarasikan utf-8) | iso-8859-1 | Ya (mengabaikan kebohongan) |
| latin1_no_decl | latin-1 | cp720 | Tidak |
| cp1252_no_decl | cp1252 | cp862 | Tidak |
| utf8_declared_latin1 | utf-8 (dideklarasikan latin-1) | iso-8859-1 | Tidak (mengikuti deklarasi yang salah) |
Lima dari delapan berhasil dipulihkan. UTF-8, UTF-16 dengan BOM, GBK, Shift-JIS, dan bahkan latin-1 yang salah label semuanya kembali dengan benar, dan tebakan berbentuk superset (GBKβgb18030, Shift-JISβcp932) tetap bisa didekode. Dua mode kegagalan perlu diketahui: sampel byte latin-1/cp1252 yang pendek sering salah ditebak sebagai code page DOS, karena detektor statistik tidak andal pada input pendek dan karakter box-drawing DOS bertumpang tindih dengan titik kode Latin-1; dan ketika deklarasi <meta charset> memang salah, UnicodeDammit mempercayai deklarasi itu. Dokumentasi bs4 sudah menandai keduanya β sampel bisa saja "so short that Unicode, Dammit can't get a lock on it," dan semakin banyak data, semakin baik tebakan yang dihasilkan.
Dibanding selectolax, yang diam-diam merusak byte non-UTF-8 dan mengharuskan kamu mendekode sendiri, ini adalah keunggulan nyata: bs4 setidaknya mencoba menebak dan sering berhasil. Tetapi ini bukan jaminan. Jika encoding sudah diketahui, jangan menebak β nyatakan eksplisit: BeautifulSoup(bytes, from_encoding="...").
Apakah Backend Benar-Benar Bisa Berbeda di Halaman Nyata?
Matriks malformed HTML menunjukkan backend bisa berbeda pada input yang sengaja rusak. Pertanyaan berikutnya yang wajar adalah apakah itu relevan di dunia nyata, jadi saya menjalankan ketiga backend pada 11 halaman nyata hasil fetch β BBC, Wikipedia, Craigslist, MDN, old.reddit, Python docs, Hacker News, Books to Scrape, webscraper.io, whitehouse.gov, dan halaman kutipan yang dirender JavaScript β lalu membandingkan jumlah link, heading, dan image.
Semua tiga backend sepakat pada semua 11 halaman. Nol divergensi. Artinya perbedaan backend pada bagian jebakan tadi hanya muncul pada HTML yang sengaja rusak; ketika situs produksi modern cukup rapi strukturnya β bahkan yang terasa "berantakan" sekalipun β pilihan backend tidak mengubah apa yang kamu ekstrak. Bacaan praktisnya: untuk situs mainstream yang well-formed, html.parser sudah sangat memadai dan menghemat dependency. Hanya saat kamu meng-scrape HTML yang jelas tidak standar, buatan tangan, atau sangat tua, pilihan backend mulai mengubah hasil, dan di situlah kamu berpindah ke lxml atau html5lib.
Satu catatan dari pengujian itu, karena ini benar-benar edge case. Halaman MDN berisi elemen <template>, dan semua backend bs4 mengembalikan 508 link β artinya bs4 memipihkan isi <template> ke tree utama. Itu menempatkan bs4 di sisi yang sama dengan lxml, dan berlawanan dengan selectolax-Lexbor, yang mengikuti spesifikasi HTML5 secara ketat (<template> adalah DocumentFragment inert) dan hanya mengembalikan 497, dengan diam-diam mengabaikan 11 link di dalam template. Jadi bs4 akan menangkap data di dalam <template> β berguna, tetapi juga bisa membuat kamu mengambil konten "phantom" yang tidak pernah dirender browser. Tidak ada yang salah; keduanya hanya interpretasi spesifikasi yang berbeda, dan kamu perlu tahu mana yang kamu dapatkan.
Posisi BeautifulSoup β dan Posisi yang Bukan Miliknya
Daripada memampatkan semuanya menjadi satu skor 0β100 (yang justru akan menyembunyikan trade-off penting), berikut skor per dimensi, dengan catatan kehati-hatian di tiap baris:
| Dimensi | Temuan tes | Catatan pembaca |
|---|---|---|
| Instalasi / first run | Hanya pembungkus, tanpa browser/setup; html.parser tanpa dependency; semua wheel prebuilt | Backend lxml butuh dependency C |
| Kecepatan vs parser C | 12β17x lebih lambat (html.parser) / 10.5β14x (backend lxml), semua ukuran | Satu rig; data selectolax dipakai ulang |
| Throughput query CSS | ~6β7.5x lebih lambat pada 100k node; backend lxml tidak menyelamatkan | Dipakai ulang; membayar pajak Python Tag |
| Memori | 1.5β1.75x selectolax/lxml; paling berat | Dipakai ulang; diukur dengan RSS |
| Import cold start | 2.36x lebih lambat (33.4 vs 14.1 ms) | Dipakai ulang; item kecil |
| Skalasi thread | bs4-lxml ~3.9x lebih lambat pada 4 thread (memegang GIL) | Satu observasi; gunakan multiprocessing |
| Ergonomi API | 29/29 probe; find berbasis predikat fungsi + navigasi dua arah | Jebakan boolean-attr string kosong dan batas kata strip |
| Coverage CSS | soupsieve paling lengkap: 41/41 dasar + 20/20 extended; dukung :lang | Tanpa XPath, tanpa ::text |
| Toleransi 3 backend | lxml/html5lib 15/15; html.parser 12/15 | Divergensi hanya pada HTML rusak |
| Konsistensi halaman nyata | 3 backend sepakat 11/11; semuanya memipihkan <template> (508) | Untuk situs well-formed, backend tidak masalah |
| GC siklus referensi | Tree adalah cycle; 300 loop menahan 120,900 objek, collect() mengosongkannya | Loop panjang perlu decompose() |
| Encoding | UnicodeDammit memulihkan 5/8; salah menebak sampel pendek, mengikuti deklarasi yang salah | Satu observasi |
| Pemeliharaan | Aktif (4.15.0, Juni 2026); MIT | Beranda di crummy/Launchpad, bukan GitHub |
Jadi BeautifulSoup cocok untuk siapa? Siapa pun yang lebih mementingkan API yang mudah dibaca dan parsing yang toleran daripada throughput mentah, pada volume moderat β prototipe, scraping sekali jalan, alat internal, tim yang biaya waktu developer-nya lebih mahal daripada runtime. Siapa yang sebaiknya mencari opsi lain? Pipeline jutaan halaman yang pajak kecepatannya berubah menjadi biaya nyata, workload yang membutuhkan paralelisme tingkat thread, dan siapa pun yang sudah terikat pada XPath.
Satu catatan tentang posisinya di stack scraping nyata, dan di mana tool kami sendiri masuk. BeautifulSoup mengasumsikan kamu sudah memiliki HTML-nya. Ia tidak mengambil halaman, tidak merender JavaScript, dan tidak menangani anti-bot atau CAPTCHA β itu pekerjaan terpisah, dan memang benar-benar sulit di web modern. Di sinilah AI scraping API berada di lapisan berbeda: stack developer Thunderbit β REST API, server MCP, dan CLI β menangani fetch, rendering JS, dan masalah anti-bot, lalu mengembalikan Markdown bersih (POST /distill) atau JSON terstruktur yang sesuai skema (POST /extract) tanpa kamu perlu menulis selector sama sekali. Keduanya bukan pesaing; mereka saling melengkapi. bs4 mem-parse HTML yang sudah kamu pegang; API, MCP, dan CLI Thunderbit membantu kamu mendapatkan HTML yang sebelumnya sulit dijangkau. Kalau bottleneck kamu adalah parsing, bs4 adalah jawaban yang baik. Kalau bottleneck kamu adalah akuisisi, itu lapisan yang lain.
Coba Thunderbit untuk Ekstraksi Data Web
Kesimpulan
BeautifulSoup memberi kamu API paling ramah, toleransi HTML rusak paling kuat, dan mesin CSS paling lengkap dalam perbandingan ini β dengan biaya kecepatan sekitar satu orde magnitudo dan footprint memori terbesar. Itulah trade-off utamanya, dijelaskan apa adanya. Backend default html.parser adalah satu-satunya jebakan nyata: ia diam-diam merusak tabel dan list yang tidak ditutup, jadi gunakan "lxml" atau "html5lib" setiap kali input kamu mungkin berantakan. Threads tidak akan mempercepatnya β multiprocessing yang akan membantu. Dan pada loop jangka panjang, panggil decompose() untuk setiap halaman agar siklus referensi tidak menumpuk.
Dua batasan terakhir. Semua hasil di sini diukur pada satu platform (macOS arm64, Python 3.14, wheel prebuilt), dan pengali timing diambil ulang dari rig selectolax (bench yang sama, per 2026-07-13) alih-alih dijalankan ulang β jadi hasilnya mewarisi keterbatasan platform tunggal itu, dan setup Linux x86_64 atau kompilasi dari source bisa menggeser angka pastinya. Dan tidak ada satu pun hasil di sini yang merupakan penemuan baru: bs4 adalah library berusia 20 tahun, sehingga setiap perilaku yang diuji sudah terdokumentasi atau pernah dicatat secara publik. Nilai utamanya bukan scooping. Nilainya adalah memberi angka nyata pada trade-off yang selama ini cuma dijelaskan secara kualitatif oleh dokumentasi.
Pertanyaan yang Sering Diajukan
Apakah BeautifulSoup lambat?
Ya, dan itu bisa diukur. Pada tugas parse-plus-extract, ia berjalan sekitar 12β17x lebih lambat daripada parser C seperti selectolax-Lexbor dengan backend default html.parser, dan 10.5β14x lebih lambat dengan backend lxml, karena ia membuat objek Python untuk setiap node. Seberapa penting itu tergantung skala: pada halaman 1 MB, angkanya 232 ms versus 15 ms β hampir tak terasa untuk beberapa ribu halaman, tetapi sangat menentukan untuk pipeline satu juta halaman.
Parser BeautifulSoup mana yang sebaiknya saya pakai β html.parser, lxml, atau html5lib?
Untuk situs mainstream yang well-formed, html.parser bawaan sudah cukup dan tidak menambah dependency. Tetapi ia tidak mengimplementasikan optional-end-tags HTML5, jadi pada tabel atau list yang tidak ditutup, ia mencampur teks di sekitarnya tanpa error. Kalau input kamu mungkin rusak, buatan tangan, atau tua, gunakan "lxml" atau "html5lib" secara eksplisit β keduanya mencetak 15/15 pada matriks HTML rusak, sementara html.parser hanya 12/15.
Bisakah BeautifulSoup diproses paralel dengan threads?
Tidak. Pembuatan tree bs4 murni Python dan memegang GIL, jadi menambah thread justru memperlambat, bukan mempercepat β pada pengujian, empat thread untuk parse 1 MB berjalan sekitar 3.9x lebih lambat daripada satu thread. Untuk mem-parallel-kan bs4, gunakan multiprocessing (ProcessPoolExecutor). Library dengan core C, seperti selectolax dan lxml, justru yang mendapat manfaat dari parallelisme tingkat thread.
Apakah BeautifulSoup bagus dalam menangani HTML yang rusak?
Secara umum ya β pada beragam sampel HTML rusak (tag bertingkat salah, skeleton hilang, atribut tanpa tanda kutip, dan lain-lain), ketiga backend memulihkan struktur dengan baik. Titik lemahnya adalah html.parser default dan optional-end-tags: <td>/<li> yang tidak ditutup akan ditanamkan, bukan ditutup, sehingga teks yang diekstrak jadi rusak. Pindah ke backend lxml atau html5lib dan kelas masalah ini hilang.
BeautifulSoup vs lxml β mana yang lebih baik?
Keduanya alat yang berbeda. lxml jauh lebih cepat dalam membangun tree maupun query, dan mendukung XPath. BeautifulSoup membungkus lxml (di antara backend lain) dalam API yang jauh lebih ramah dan bahkan punya coverage CSS yang lebih luas lewat soupsieve. Hanya jangan berharap backend lxml membuat bs4 secepat lxml β backend hanya mempercepat parsing, sementara query dan traversal tetap membayar biaya objek Python per node dari bs4, sehingga performanya masih sekitar 6β7.5x lebih lambat pada seleksi batch besar.
Coba Thunderbit untuk Ekstraksi Data Web Get Started Free


