Penyaring yang Paling Banyak Membocorkan Boilerplate Ternyata Tidak Kehilangan Konten Apa Pun dalam Set Fixture Ini

Terakhir diperbarui pada August 14, 2026
Penyaring yang Paling Banyak Membocorkan Boilerplate Ternyata Tidak Kehilangan Konten Apa Pun dalam Set Fixture Ini
Ringkasan AI
Enam library, satu set fixture beranotasi, satu scorer. Dalam 22 fixture sintetis ini, library dengan kebocoran boilerplate tertinggi — Mozilla's Readability, sebesar 23,5% — juga menjadi satu-satunya yang berhasil mengambil kembali semua unit artikel yang diberi label. Itulah komprominya dalam satu kalimat, dan kebanyakan tulisan tentang topik ini tidak pernah menampilkannya, karena biasanya mereka berhenti setelah menghitung precision. Memberi makan model dan membayar per token? newspaper4k atau goose3. Keduanya tidak membocorkan unit boilerplate dan tidak menghasilkan token yang mencemari. newspaper4k jika Anda ingin ada jawaban di setiap halaman; goose3 jika Anda lebih memilih diam daripada tebakan, dan halaman Anda berbentuk paragraf.

Enam library, satu set fixture beranotasi, satu scorer. Di 22 fixture sintetis ini, library dengan kebocoran boilerplate paling tinggi — Mozilla Readability, sebesar 23,5% — justru jadi satu-satunya yang berhasil mengambil kembali semua unit artikel yang diberi label.

Itulah komprominya dalam satu kalimat, dan kebanyakan tulisan soal topik ini jarang menampilkannya, karena biasanya berhenti setelah menghitung precision.

Apa yang sebenarnya diukur

Setiap fixture dalam set ini punya ground truth per unit. Setiap blok halaman — paragraf artikel, navigasi, iklan, sidebar, thread komentar, promo — diberi label article atau boilerplate dan ditandai dengan token sentinel unik. Jadi, “apakah extractor berhasil mengambil unit ini” benar-benar diuji lewat keberadaan substring yang persis sama, bukan lewat skor kemiripan. Sebuah sentinel entah tetap ada di output atau hilang sepenuhnya.

Dua puluh dua fixture, 91 unit. Enam extractor: Mozilla Readability 0.6.0 (melalui jsdom 30.0.1), trafilatura 2.2.0, resiliparse 1.0.9, newspaper4k 0.9.6, goose3 3.1.22, dan jusText 3.0.2. Python 3.14.2 dan Node 22 dijalankan di mesin yang sama. Artikel ini tidak mencantumkan OS/CPU, detail pemanggilan yang persis, jumlah pengulangan, atau kebijakan warm-up, jadi kolom waktu di sini adalah hasil observasi lokal, bukan benchmark yang bisa langsung dipindahkan begitu saja.

Ada dua aturan yang saya pakai sebelum menjalankan apa pun. Setiap library Python diinstal ke virtualenv kosongnya sendiri, supaya jejak dependensinya memang milik dia sendiri dan bukan warisan library lain. Dan tidak ada runner yang menghitung metrik — semuanya cuma mengeluarkan teks hasil ekstraksi mentah, lalu satu scorer tunggal menghasilkan seluruh angka, sehingga keenam tools dibandingkan dengan aritmetika yang sama persis, bukan enam definisi “precision” yang cuma mirip-mirip.

Tabel utama

LibraryRecall artikel (semua 22)Kebocoran boilerplatePrecision token kontenFixture yang terjawabToken yang mencemari
Readability1.00000.23530.910911/1135
trafilatura0.98650.05880.941111/114
newspaper4k0.98650.00000.945211/110
resiliparse0.90540.05880.938111/117
jusText0.83780.47060.876010/1174
goose30.82430.00001.000010/110

Recall diakumulasi dari seluruh 22 fixture. Tingkat kebocoran, precision token konten agregat, dan kontaminasi memakai 11 fixture yang berisi unit artikel sekaligus boilerplate; “terjawab” menunjukkan berapa banyak dari fixture itu yang menghasilkan output. Angka lengkap per fixture ada di sixway-scores.json.

Satu baris di tabel itu bukan default. extract_plain_text milik resiliparse memakai main_content=False sebagai default, dan saya memanggilnya dengan main_content=True. Perbedaannya besar: pada default, ia membocorkan 17 dari 17 unit boilerplate di seluruh set — setiap nav, iklan, sidebar, thread komentar, dan promo — dibanding 1 dari 17 saat flag itu dinyalakan. Semua library lain di atas dipanggil dengan default masing-masing. Jadi tingkat kebocoran 0,0588 untuk resiliparse adalah perilaku saat Anda meminta konten utama, sedangkan extract_plain_text(html) begitu saja adalah produk yang berbeda (default-vs-main-content.json).

Baca kolom pertama dan kedua bersama-sama, karena kalau hanya satu yang dibaca, Anda bisa salah memilih tool.

Readability tidak pernah meleset. Recall sempurna di semua 22 fixture, dan cuma itu yang mencapai hasil tersebut. Harganya: 4 dari 17 unit boilerplate ikut lolos, 35 token mencemari output, empat kali tingkat kebocoran trafilatura. Tiga dari empat kebocorannya polanya sama — blok promo berkelas netral yang berdiri sebagai saudara artikel, lalu terseret oleh heuristik sibling-append miliknya. Kalau output itu Anda berikan ke model, Anda sedang membayar token-token itu, dan model akan membacanya sebagai bagian dari artikel.

newspaper4k adalah yang paling seimbang. Nol kebocoran, nol token mencemari, recall 0,9865, dan menghasilkan output pada semua 22 fixture. Kalau saya harus memilih satu tanpa tahu bebannya seperti apa, ini yang saya pilih, dan ini bukan pilihan yang paling sering diambil orang.

goose3 punya precision sempurna dan recall terburuk di pengujian ini. Setiap kata konten yang ia keluarkan memang konten artikel. Tapi ia juga tidak mengambil apa pun pada dua fixture dan pada dua fixture yang sama ia tidak menghasilkan output sama sekali. Precision sempurna memang murah kalau Anda boleh menolak menjawab.

Angka precision yang membuat dua library terlihat terlalu bagus

Poin terakhir ini penting untuk dibuat konkret, karena saya hampir menerbitkan versi yang menyesatkan.

Precision dan F1 di sini bersifat kondisional terhadap adanya output. Library yang mengembalikan string kosong pada sebuah fixture tidak menambah apa pun ke pembilang maupun penyebut — jadi tidak menjawab itu gratis, dan precision extractor yang konservatif akan terlihat lebih baik daripada extractor yang lebih lengkap, bukan karena kualitasnya, melainkan karena diam.

goose3 punya precision agregat 1,0000 pada 10 fixture yang dinilai ketika ia mengeluarkan output. jusText 0,8760 pada 10 dari 11. Readability, trafilatura, resiliparse, dan newspaper4k menjawab 11 dari 11. Tabel sekarang menampilkan penyebut di samping precision agar abstain tidak hilang di balik rasio yang tampak bagus.

Ada versi yang lebih buruk dari ini. Scorer pertama saya merata-ratakan recall artikel pada set fidelitas konten yang sama, yaitu 11 fixture yang tidak memiliki boilerplate — dan itu memang benar untuk mengukur kebocoran. Hasilnya menampilkan resiliparse dengan recall 1.0000. Padahal kalau dihitung di semua 22 fixture, resiliparse cuma 0,9054, karena pada fixture yang artikelnya seluruhnya berada di dalam elemen <li> tanpa satu pun <p>, ia mengembalikan output tetapi mengambil kembali 0 dari 6 unit artikel. Fixture itu tidak punya boilerplate, jadi tidak masuk ke rata-rata, dan kegagalan nyata tersembunyi di balik skor sempurna.

Di mana masing-masing benar-benar gagal

FixtureApa yang diujiSiapa yang tidak mengambil apa pun
Artikel seluruhnya di <li>, tanpa <p>asumsi strukturresiliparse (0/6), goose3 (tidak ada output)
Satu unit artikel 129 karakterambang konten pendekjusText
Sepuluh paragraf pendek, tanpa satu paragraf panjangambang konten pendekjusText
Dokumen hampir kosongbatas null yang sebenarnyagoose3, jusText

Semua ini adalah perilaku spesifik yang bisa direproduksi, bukan sekadar klaim umum “lebih buruk dalam ekstraksi”:

  • resiliparse dan goose3 sama-sama mengasumsikan paragraf. Arahkan salah satunya ke halaman yang tubuhnya berupa daftar — changelog, spesifikasi, FAQ, resep — dan resiliparse akan mengembalikan teks tanpa isi daftar, sementara goose3 sama sekali tidak mengembalikan apa pun. resiliparse justru lebih berbahaya di sini, karena mengembalikan sesuatu terlihat seperti sukses.
  • jusText punya jurang panjang, dan jurang itu tajam. Lebih lanjut di bawah.
  • Dokumen yang hampir kosong adalah satu-satunya kasus ketika tidak mengembalikan apa pun bisa dibilang benar, jadi saya tidak akan menyalahkan library mana pun untuk itu.

jusText: jurang, bukan lereng

jusText menghasilkan output pada 19 dari 22 fixture dan membocorkan 47% boilerplate — yang tertinggi di pengujian ini, berlawanan dengan reputasinya. Tapi angka yang paling menarik adalah angka yang membuat saya menjalankan ulang semuanya.

jusText mengklasifikasikan setiap blok berdasarkan kepadatan stopword terhadap stoplist bahasa, lalu menjalankan pass yang peka konteks untuk menaikkan blok neargood menjadi good hanya jika ia berdampingan dengan blok good yang sudah ada. Sebuah blok bisa menjadi good sendiri hanya jika panjangnya melampaui length_high, yang default-nya 200 karakter. Pada dokumen di mana tidak ada satu pun yang melewati garis itu, tidak ada yang menjadi pemicu promosi, dan seluruh halaman turun menjadi boilerplate.

Saya mengujinya pada dokumen yang paragraf terpanjangnya 151 karakter:

length_highParagraf goodKarakter yang dikembalikan
200 (default)00
1508832
1208832
1008832
808832

Dari nol ke 832 karakter ketika tepat satu paragraf melewati ambang batas, lalu tidak ada yang berubah meskipun ambang terus dilonggarkan. Satu paragraf yang melewati garis itu membuka seluruh dokumen.

Sebelum menyimpulkan itu, saya menyapu length_low pada empat nilai dan max_link_density pada dua nilai — delapan kombinasi, semuanya menghasilkan nol. Aturan proyek ini adalah klaim kemampuan negatif harus diuji dengan setidaknya tiga bentuk parameter, atau dengan nama field error dari vendor sendiri; satu parameter yang tidak produktif bukan temuan tentang library. Angkanya ada di justext-length-threshold.json.

Semua ini tidak berarti jusText mengekstrak dengan buruk. Pada halaman bahasa alami yang nyata dengan default, ia mengembalikan 1.190 karakter teks artikel yang bersih. Artinya, jusText punya knob terdokumentasi yang berperilaku seperti sakelar, dan posisi default sakelar itu tidak cocok untuk dokumen dengan paragraf pendek.

Apa yang harus diinstal, dan berapa biaya import-nya

Measured results chart: Install footprint vs cold import

Fixture sama, mesin sama, setiap library di virtualenv kosongnya masing-masing.

LibraryPaketsite-packagesCold importExtraction p50
resiliparse521.0 MiB0.015 s0.06 ms
jusText322.4 MiB0.777 s0.56 ms
goose31644.3 MiB2.181 s1.85 ms
newspaper4k2247.5 MiB2.812 s2.69 ms
trafilatura1769.9 MiB1.584 s0.51 ms
Readability + jsdom32 (npm)26 MiB0.473 s6.37 ms

Pada pengujian ini, resiliparse punya cold-import dan median extraction terendah: 15 ms dan 0,06 ms. Rasio lintas-runtime yang presisi akan melebih-lebihkan apa yang bisa didukung oleh protokol yang belum lengkap, terutama karena satu ekstraksi terburuknya mencapai 1.098 ms. Distribusi startup terpisah, call pertama, dan steady-state dibutuhkan sebelum angka ini dipakai untuk sizing serverless.

trafilatura dan resiliparse praktis imbang dari sisi kualitas — 0,9697 lawan 0,9681 untuk content-token F1, dengan tingkat kebocoran 0,0588 yang sama — dan saya tidak akan menunjuk pemenang dari selisih sekecil itu. Namun dari sisi footprint, keduanya jauh berbeda: 21,0 MiB versus 69,9 MiB, 5 paket versus 17. Kompromi yang benar-benar Anda buat adalah antara kebutaan resiliparse terhadap daftar dan tiga dependensi tambahan milik trafilatura.

Dua bug di testbed saya sendiri, ditemukan sebelum publikasi

Perbandingan di atas hampir tidak jadi terbit, dan alasannya lebih berharga daripada satu baris apa pun di dalamnya.

Set fixture awal tidak bisa melihat dua dari enam library. Fixture asli menulis setiap unit sebagai rangkaian token nonsense unik — zzart01vf64 zzart01v56i — yang justru membuat recall bisa dihitung tepat. Tapi itu juga berarti fixture tidak mengandung kata fungsi bahasa Inggris sama sekali. Readability, trafilatura, dan resiliparse memutuskan secara struktural dari DOM, jadi tidak terpengaruh. goose3 dan jusText memutuskan secara leksikal, lewat hitungan stopword, dan tidak ada yang bisa dihitung: keduanya mengembalikan string kosong pada semua 22 fixture.

Tabel dengan dua library bernilai nol akan terlihat meyakinkan tetapi tidak bermakna. Saya cek dulu sebelum menuliskannya, memakai halaman nyata: goose3 mengembalikan 1.017 karakter dan jusText 1.190. Masalahnya ada di testbed, bukan di library.

Jadi fixture dibangun ulang dengan prosa bahasa Inggris yang membawa sentinel — struktur sama, kelas sama, posisi DOM sama, batas unit sama, sentinel sama, 1.568 token ditukar satu per satu. goose3 berubah dari 0 menjadi 20 dari 22.

Lalu rebuild itu merusak dua hal lain, dan keduanya kesalahan saya. Satu kata bahasa Inggris kira-kira enam karakter; zzart01vf64 sekitar dua belas. Menukar satu per satu memotong semua unit menjadi setengah — 21.646 karakter teks unit turun menjadi 10.986, dan unit terpanjang turun dari 1.513 menjadi 622. Itu diam-diam menulis ulang fixture yang tujuan utamanya menguji panjang. jusText, yang perilakunya berupa jurang panjang, turun dari 19 dari 22 menjadi 6 dari 22 hanya karena hal itu. Kalau saya memublikasikan versi yang dipotong dua itu, angka jusText akan salah tiga kali lipat ke arah yang membuatnya tampak lebih buruk.

Yang kedua: menarik semua unit dari satu korpus bersama memulihkan densitas stopword tetapi menghancurkan sifat yang dibutuhkan scoring tingkat token. Kosakata artikel dan boilerplate harus saling lepas, atau “token yang diekstrak yang merupakan token boilerplate” akan menghitung kata the. Sepuluh dari 22 fixture akhirnya memiliki kosakata yang tumpang tindih, padahal pada versi asli nol. Solusinya adalah menambahkan sufiks pada kata konten per unit dan membiarkan kata fungsi tetap polos — stopword asli untuk dihitung library leksikal, kosakata konten yang saling lepas untuk scorer.

Itu juga alasan kolom tingkat token di sini diberi nama content_token_* dan tidak memakai ulang angka dari hasil Readability-versus-trafilatura yang sudah diterbitkan. Itu adalah kuantitas yang berbeda, diukur hanya atas kata konten, dan menyebut satu sebagai yang lain akan keliru.

Saat rebuild, satu hal lagi muncul yang bukan kesalahan saya: tiga fixture link-density menempatkan </a> di dalam sebuah kata<a href="/x">zzsibp015qlhf zzsi</a>bp015qbht — karena anchor diposisikan berdasarkan offset karakter untuk mencapai rasio yang tepat. Teks yang dirender tidak berubah, jadi scoring asli tidak pernah menyadarinya, tetapi extractor yang bekerja per elemen, bukan per aliran teks, melihat dua fragmen sementara yang lain melihat satu kata. Sudah diperbaiki, dengan delta karakter pada link dicatat alih-alih diam-diam diserap.

Siapa sebaiknya memakai apa

Memberi makan model dan membayar per token? newspaper4k atau goose3. Keduanya tidak membocorkan unit boilerplate dan tidak menghasilkan token yang mencemari. newspaper4k kalau Anda ingin ada jawaban di setiap halaman; goose3 kalau Anda lebih suka diam daripada tebakan, dan halaman Anda berbentuk paragraf.

Mengoptimalkan jalur Python yang sensitif terhadap latensi? Masukkan resiliparse ke dalam perbandingan. Di sini ia punya import terendah dan median extraction terendah, serta sangat dekat dengan trafilatura dari sisi kualitas — tapi hanya dengan main_content=True, yang bukan default. Cek dulu layout yang banyak daftar, dan jangan ubah timing lokal ini jadi rasio kecepatan lintas-runtime yang terlalu presisi.

Untuk pengarsipan, atau apa pun di mana kehilangan konten lebih buruk daripada konten tambahan? Readability. Ia satu-satunya yang mengambil kembali setiap unit artikel di setiap fixture, dan 35 token nyasar adalah harga yang murah kalau alternatifnya kehilangan satu paragraf.

Pekerjaan multibahasa? jusText layak dipertimbangkan karena ia menyertakan stoplist bahasa. Studi ini tidak menguji ekstraksi multibahasa, jadi fitur itu adalah alasan untuk mengevaluasinya, bukan bukti bahwa ia menang. Uji length_high terhadap panjang paragraf yang mewakili data Anda.

Apa pun yang bukan artikel? Tidak satu pun dari ini. Semuanya dibangun di atas asumsi bahwa sebuah halaman punya satu badan teks utama, dan listing produk, halaman hasil pencarian, atau dashboard akan merusak asumsi itu dengan cara yang tidak bisa diperbaiki oleh parameter apa pun.

Di mana API terkelola masuk

Semua yang dibahas di atas adalah library yang Anda jalankan sendiri: Anda memberi HTML dan menerima teks. Mode kegagalannya beda-beda tergantung bentuk halaman, jadi validasi default yang Anda pilih terhadap korpus Anda. Ekstraksi field terstruktur dan pengambilan/rendering berada di luar perbandingan ini.

Catatan penulis: Thunderbit adalah layanan terkelola kami untuk workflow URL-in dan output terstruktur. Ia tidak dijalankan melalui fixture ini, jadi tidak ada implikasi perbandingan kualitas. Batas keputusan yang relevan adalah apakah Anda sudah memegang HTML dan ingin extractor teks lokal, atau Anda ingin pengambilan/rendering dan operasionalnya ditangani layanan.

Framing yang jujur: kalau Anda sudah punya HTML dan ingin teks, salah satu dari enam ini gratis dan bagus, dan tabel ini menunjukkan mana yang cocok. Kalau Anda mengambil halaman dalam skala besar, atau Anda ingin baris data alih-alih prosa, itu pembelian yang berbeda.

Kalau Anda justru memilih di antara hosted fetcher, ringkasan kami tentang web scraping API membahas area itu, dan perbandingan biaya SEO dan data API membahas biayanya. Untuk sisi self-hosted, pilar scraper open-source memberi pandangan yang lebih luas, dan kalau yang Anda butuhkan sebenarnya Markdown, bukan plain text, mengonversi HTML ke Markdown di Python adalah tempat kehilangan paling banyak terjadi.

Coba Thunderbit untuk Ekstraksi Data Web

Putusan akhir

Tidak ada pemenang, dan tabel yang menyebut satu pemenang akan membohongi kompromi yang nyata.

Bangun korpus penerimaan kecil sebelum memilih: sertakan artikel yang hanya berisi daftar, paragraf pendek, sibling promo, halaman yang hampir kosong, dan contoh di mana tidak menjawab lebih baik daripada mencemari hasil. Nilai recovery artikel, kebocoran boilerplate, dan abstain secara terpisah. Pada fixture ini, Readability unggul di recall, newspaper4k menghasilkan baris paling seimbang, dan resiliparse menjadi kandidat latensi dengan titik buta pada konten daftar; label-label itu tidak boleh dibawa ke bentuk halaman yang belum diuji tanpa validasi.

Apa yang sebenarnya akan saya katakan kepada Anda jauh lebih sempit dari itu: jalankan fixture terhadap bentuk halaman Anda sendiri sebelum memilih. Dua dari enam tidak bisa melihat testbed awal saya, dan salah satunya mencetak recall sempurna yang menutupi kegagalan total. Tabel perbandingan adalah titik awal untuk itu, bukan pengganti.

Coba Thunderbit untuk Ekstraksi Data Web Get Started Free

FAQ

Apakah angka-angka ini bisa dibandingkan dengan benchmark yang dipublikasikan untuk library-library ini? Tidak, dan saya tidak akan mengutipnya seperti itu. Ini adalah fixture terkontrol dengan unit sintetis berlabel, jadi keenamnya melihat byte yang sama dan perbandingan di antara mereka adil. Angka yang dipublikasikan seperti benchmark article extraction di scrapinghub memakai korpus dunia nyata, yang mengukur hal berbeda dan lebih sulit. Pakai tabel ini untuk membandingkan keenamnya satu sama lain, bukan dengan angka dari paper.

Mengapa tingkat kebocoran Readability jauh lebih tinggi daripada trafilatura padahal keduanya berbasis DOM? Karena batasnya ditarik di tempat yang berbeda. Tiga dari empat kebocoran Readability adalah blok promo berkelas netral yang berdiri sebagai saudara artikel, dan heuristik sibling-append-nya menariknya masuk dengan asumsi bahwa konten panjang ber-link rendah yang berdampingan kemungkinan bagian dari cerita. Sering kali memang begitu. Pada fixture ini, itu adalah promo. trafilatura lebih ketat soal apa yang ia tambahkan dan membocorkan satu unit yang sama.

Haruskah saya mempercayai angka precision untuk goose3 dan jusText? Hanya kalau disertai jumlah sampel. Keduanya dinilai pada 10 dari 11 fixture yang berisi artikel dan boilerplate, karena mereka tidak mengembalikan output pada salah satunya, dan fixture tanpa output tidak menyumbang ke sisi mana pun dari rasio. Precision 1,0000 milik goose3 memang nyata untuk halaman yang ia jawab; recall 0,8243-nya di seluruh 22 fixture adalah sisi lain dari fakta yang sama.

Apakah ambang panjang jusText berpengaruh pada halaman nyata? Sangat tergantung pada panjang paragraf Anda. Artikel berita dengan paragraf 300 karakter akan melewati length_high pada paragraf pertama dan berperilaku normal — itulah sebabnya jusText mengembalikan 1.190 karakter bersih pada halaman nyata dengan default. Halaman dengan paragraf pendek, item daftar, atau deskripsi produk mungkin tidak pernah melewatinya, lalu jusText mengembalikan string kosong alih-alih jawaban sebagian. Setel secara eksplisit, jangan tunggu ketahuan di produksi.

Apa yang tidak diuji di sini? Halaman dunia nyata, sama sekali tidak. Ekstraksi multibahasa, meskipun stoplist jusText adalah selling point utamanya. Memory saat beban. Apa pun yang bukan artikel — tidak ada listing produk, tidak ada hasil pencarian, tidak ada dashboard. Kasus tepi encoding. Dan dua ekosistem Node serta Python dibandingkan dari perilaku library, bukan dari performa runtime, jadi angka milidetik di lintas batas itu sebaiknya dibaca sebagai orde besaran, bukan rasio presisi.

Ke
Ke
CTO di Thunderbit | Senior Data Scientist & Expert ML Dengan pengalaman hampir satu dekade di bidang machine learning dan data science, Ke Shen adalah lulusan Columbia University dan mantan Senior Data Scientist di Walmart Labs. Dengan keahlian mendalam yang diakui sejawat dalam Python, R, Java, dan Statistik, ia membagikan wawasan teruji lapangan tentang bagaimana membawa algoritma AI yang kompleks dari teori ke arsitektur siap produksi.
Daftar Isi
Thunderbit · Agen data web AI

Ekstrak data dari halaman apa pun dalam 1 klik

Dipercaya oleh 250.000+ pengguna
tersedia paket gratis
Dari halaman web ke spreadsheet
Jelaskan apa yang kamu butuhkan — AI Agent Thunderbit akan men-scrape dan mengekspornya ke Excel, Google Sheets, Airtable, atau Notion. Gratis untuk memulai.
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week