Di seluruh fixture tempat kebocoran memang bisa didefinisikan, goose3 mencatat precision token konten 1.0000 dan nol token pengotor. Tidak satu pun kata dari navigasi, iklan, sidebar, komentar, atau promosi ikut terbawa ke output-nya. Tidak ada library lain dalam perbandingan enam alat yang bisa menyaingi hasil itu.
Namun, goose3 juga punya recall artikel terburuk di set ini — 0.8243 pada total 22 fixture, dibanding 1.0000 milik Mozilla Readability — karena pada dua fixture ia mengembalikan string kosong.
Kedua metrik ini saling terhubung oleh aturan penilaian: hasil kosong tidak menyumbang apa pun pada precision kondisional, sementara recall justru mencatat kegagalannya.
Apa itu goose3
goose3 adalah lanjutan Python 3 dari garis keturunan yang dimulai dari Goose milik Gravity Labs di Scala lalu berlanjut ke python-goose. Ini adalah article extractor dengan metadata, bukan sekadar pengambil teks: buat Goose, panggil extract(), lalu Anda akan mendapat objek Article dengan sekitar dua puluh delapan field yang bisa diakses — teks yang sudah dibersihkan, judul, penulis, tanggal publikasi, gambar utama, meta description, tag, tautan, tweet, dan lainnya.
Referensi resmi: repositori resmi goose3.

Versi yang diuji: 3.1.22, berlisensi Apache, 912 bintang GitHub, terakhir di-push 2026-07-23 — masih aktif dipelihara saat pengujian. Python 3.14.2.
API-nya hanya dua pemanggilan dan satu kewajiban:
from goose3 import Goose
g = Goose()
try:
article = g.extract(raw_html=html)
text = article.cleaned_text
finally:
g.close() # tutup secara eksplisit setelah dipakai
close() ini memang perlu digarisbawahi karena sangat gampang terlupa, dan tidak ada peringatan apa pun. Review ini tidak menjalankan probe loop untuk mengukur sesi, koneksi, atau memori yang tertahan saat penutupan diabaikan, jadi menyebutnya sebagai “resource leak” akan lebih kuat daripada bukti yang tersedia. Anggap penutupan eksplisit sebagai syarat siklus hidup yang ditunjukkan oleh penggunaan API di sini.
Trade-off yang terukur
Enam extractor, satu set fixture beranotasi, satu scorer. Setiap blok pada setiap fixture diberi label article atau boilerplate dan membawa token penanda unik, jadi “apakah alat ini berhasil mengambil unit ini” diukur lewat keanggotaan substring yang tepat, bukan skor kemiripan.
| Library | Article recall (all 22) | Boilerplate leak | Content-token precision | Contaminating tokens | Produced output |
|---|---|---|---|---|---|
| Readability | 1.0000 | 0.2353 | 0.9109 | 35 | 22/22 |
| trafilatura | 0.9865 | 0.0588 | 0.9411 | 4 | 22/22 |
| newspaper4k | 0.9865 | 0.0000 | 0.9452 | 0 | 22/22 |
| resiliparse | 0.9054 | 0.0588 | 0.9381 | 7 | 22/22 |
| jusText | 0.8378 | 0.4706 | 0.8760 | 74 | 19/22 |
| goose3 | 0.8243 | 0.0000 | 1.0000 | 0 | 20/22 |
sixway-scores.json. Recall dihitung di seluruh 22 fixture; leak rate dan precision dihitung pada 11 fixture yang berisi unit artikel sekaligus boilerplate.
Baca kolom precision bersama kolom paling kanan, selalu. Precision di sini bersyarat pada keluarnya output — library yang mengembalikan string kosong pada suatu fixture tidak menyumbang apa pun ke pembilang maupun penyebut, jadi output kosong itu tidak dianggap “gratis” dalam rata-rata ini. Pembilang adalah irisan multiset antara token non-stopword yang diekstrak dan token artikel yang dilabeli; penyebut adalah semua token non-stopword hasil ekstraksi. “Contaminating tokens” lebih sempit: irisan dengan token boilerplate yang dilabeli saja. Token ekstra yang cocok dengan artikel maupun boilerplate bisa menurunkan precision tanpa menambah hitungan kontaminasi itu; token berulang yang melampaui multiset artikel juga bisa melakukan hal yang sama. Itu sebabnya newspaper4k bisa menunjukkan 0 contaminating tokens tetapi precision di bawah 1.0000. Nilai 1.0000 milik goose3 dihitung pada 10 dari 11 fixture; Readability, trafilatura, newspaper4k, dan resiliparse dihitung pada 11 dari 11.
Nilai 1.0000 tetap berguna dalam set sintetis ini. Di antara sepuluh fixture yang terhitung, goose3 tidak mengeluarkan satu pun token boilerplate yang dilabeli; Readability mengeluarkan 35 token pada halaman yang sama. Jika sebuah model mengonsumsi output itu, artinya tidak ada token yang terbuang untuk label boilerplate pada sepuluh fixture tersebut. Ini tidak berarti nol pemborosan pada halaman nyata, dan hasil kosong bisa menambah biaya fallback atau retry di bagian pipeline lain.
Dua kali diam, dan artinya apa
goose3 tidak mengeluarkan apa pun pada tepat dua fixture. Satu masih bisa dibenarkan, satu lagi memang batasan nyata.
Dokumen yang nyaris kosong. Satu halaman dengan unit artikel sepanjang 32 karakter. goose3 menolak. jusText juga. Dalam set fixture ini, Readability menghasilkan output pada semua 22 halaman, jadi hasilnya tidak mendukung keheningan goose3 di sini. Apakah menolak dokumen kecil seperti ini dapat diterima atau tidak tergantung pada kontrak minimum konten dari pemanggil.

Artikel yang seluruhnya terdiri dari elemen <li>. Enam unit artikel, semuanya bukan di dalam tag <p>. goose3 mengembalikan string kosong.
Bagian kedua ini sempat membingungkan saya, karena konfigurasi bawaan goose3 sendiri membaca parse_lists=True. Jadi saya uji — tiga konfigurasi dibandingkan dengan control yang berfungsi, karena satu kali percobaan yang gagal bukanlah kesimpulan tentang sebuah library:
| Configuration | List-only page | <p> control |
|---|---|---|
| defaults | 0 chars | 937 chars |
strict=False | 0 chars | 937 chars |
parse_lists=True (explicit) | 0 chars | 937 chars |
Nol pada ketiganya, sementara control mengembalikan 937 karakter pada semuanya. Jadi parse_lists=True mengatur apakah daftar dipertahankan di dalam artikel yang sudah lebih dulu ditemukan — bukan membuat scorer kandidat memperlakukan daftar sebagai artikel. Penilaian node goose3 membutuhkan blok berbentuk paragraf untuk menemukan tubuh artikel sama sekali, dan halaman yang tubuhnya berupa daftar memang tidak memilikinya.
Kesimpulan yang bisa didukung lebih sempit: tubuh seperti fixture sintetis ini — enam unit artikel, semuanya <li>, tanpa kandidat paragraf — menghasilkan string kosong. Changelog, referensi API, resep, halaman FAQ, dan posting perbandingan adalah sampel risiko yang masuk akal untuk replay halaman nyata karena mungkin banyak daftar, tetapi satu fixture ini tidak membuktikan bahwa kategori halaman tersebut pasti gagal secara umum.
String kosong hanya bisa dideteksi mesin jika pemanggil memvalidasi bahwa output tidak kosong. Itu lebih mudah dicegah daripada teks yang tampak meyakinkan tetapi sebenarnya tidak memuat artikel sama sekali, tetapi tetap saja ini kegagalan diam-diam jika monitoring hanya mengandalkan exception. Pemanggil di produksi perlu mengecek output minimum dan fallback atau pencatatan halaman gagal yang eksplisit.
Realitas setup
pip install goose3 menarik 16 paket dan 44.3 MiB dalam waktu sekitar 6 sampai 9 detik. Cold import yang diukur pada subprocess baru: 2.181 s.
Referensi resmi: goose3 di PyPI.
| Library | Packages | site-packages | Cold import | Extraction p50 |
|---|---|---|---|---|
| resiliparse | 5 | 21.0 MiB | 0.015 s | 0.06 ms |
| jusText | 3 | 22.4 MiB | 0.777 s | 0.56 ms |
| goose3 | 16 | 44.3 MiB | 2.181 s | 1.85 ms |
| newspaper4k | 22 | 47.5 MiB | 2.812 s | 2.69 ms |
| trafilatura | 17 | 69.9 MiB | 1.584 s | 0.51 ms |
install-and-import.json. Masing-masing library dijalankan di virtualenv kosong tersendiri, jadi tidak ada jejak yang diwarisi dari library lain.
Beratnya di tengah, kecepatannya juga di tengah. Ia terpasang dan terimpor dengan lancar di Python 3.14.2, yang tidak universal di kategori ini.
Tiga default yang perlu diketahui sebelum deploy

Membaca objek Configuration bawaan, bukan dokumennya, akan memperlihatkan sembilan belas pengaturan. Tiga di antaranya bisa mengejutkan banyak orang.
Ia memperkenalkan dirinya sendiri. browser_user_agent default-nya Goose/3.1.22. Jika Anda membiarkan goose3 mengambil halaman sendiri, setiap server yang Anda sentuh akan mencatat nama library beserta versi pastinya. Itu jujur, tetapi juga sidik jari. Atur secara sengaja atau ambil HTML sendiri lalu kirim raw_html.
Ia menunjuk ke binary MacPorts. imagemagick_convert_path default-nya /opt/local/bin/convert dan imagemagick_identify_path ke /opt/local/bin/identify. Di mesin saya, keduanya tidak ada — /opt/local adalah MacPorts, yang tidak dimiliki kebanyakan orang; Homebrew menaruh binary di /opt/homebrew. Default ini tidak berpengaruh jika Anda tidak mengaktifkan pengambilan gambar (enable_image_fetching default-nya False, dan itu masuk akal), tetapi jika Anda mengaktifkannya sambil berharap ekstraksi gambar utama berjalan, di sinilah ia diam-diam tidak akan jalan.
Ia mengasumsikan bahasa Inggris. target_language default-nya en dengan use_meta_language=True, jadi ia akan mengikuti deklarasi bahasa di halaman jika ada, dan kembali ke Inggris jika tidak ada. Cocok untuk pekerjaan berbahasa Inggris, tetapi sebaiknya diatur secara eksplisit untuk bahasa lain.
Sisanya masuk akal: parser_class adalah lxml, http_timeout 30 detik, strict aktif, log_level ERROR, parse_headers dan keep_footnotes aktif, images_min_bytes 4.000.
Memori, dan apa yang dilakukan HTML rusak terhadapnya
Dua hal yang di setiap review dalam batch ini sebelumnya ditandai belum diuji, kini diukur.
Konteks stress-test yang lebih luas ada di perbandingan memori library dan HTML rusak untuk sepuluh library.
Peak resident memory, melalui /usr/bin/time -l, satu proses baru per sel — floor import adalah biaya library saat dimuat dan idle, sementara puncak sudah termasuk dokumen.
| Library | Runtime | Import floor | 226 KB peak | 10 MB peak |
|---|---|---|---|---|
| html2text | python3.14 | 18.7 | 19.9 | 71.2 |
| pyquery | python3.14 | 30.3 | 33.9 | 172.5 |
| resiliparse | python3.14 | 20.5 | 25.1 | 225.1 |
| markdownify | python3.14 | 23.9 | 28.9 | 278.5 |
| goose3 | python3.14 | 44.1 | 52.4 | 398.5 |
| cheerio | node22 | 66.8 | 76.5 | 398.5 |
| justext | python3.14 | 30.3 | 36.6 | 431.2 |
| newspaper4k | python3.14 | 52.6 | 61.8 | 668.5 |
| trafilatura | python3.14 | 52.5 | 64.8 | 927.1 |
| turndown | node22 | 47.8 | 68.4 | 2947.1 |
memory-results.json. Baseline Python dan Node tidak bisa dibandingkan langsung satu sama lain; interpreter ada di keduanya.
goose3 memiliki floor 44.1 MiB dan mencapai puncak 398.5 MiB pada dokumen 10 MB. Floor import-nya adalah yang ketiga tertinggi di antara library Python yang ditampilkan, dan ini penting untuk deployment yang sensitif terhadap cold start.
HTML rusak. Dua belas dokumen, masing-masing merusak tepat satu hal — tag yang tidak ditutup, inline element yang salah nesting, atribut tanpa tanda kutip dengan spasi, penutup yang nyasar, tanpa <html> sama sekali, atribut duplikat, dokumen terpotong di tengah tag, entity yang salah, <script> yang tidak ditutup, deklarasi charset yang menyesatkan, komentar yang berisi markup, dan nesting 600 level — plus dua control yang well-formed dengan ukuran yang sama, karena “ia tidak mengembalikan apa pun” hanya berarti sesuatu tentang malformedness jika library tersebut juga tidak diam pada dokumen bersih dengan ukuran yang sama.
goose3 melempar error pada 0 dari 14 dan mengembalikan kosong pada 10, dengan memulihkan 2/33 sentinel di seluruh fixture rusak (malformed-results.json). Satu fixture dikecualikan dari hitungan itu: menurut HTML5, semua yang ada setelah <script> yang tidak ditutup memang adalah konten script, jadi kehilangan itu justru benar dan memulihkannya adalah deviasi.
Kelebihan dan kekurangan
Kelebihan. Nol token boilerplate terlabel pada sepuluh fixture fidelitas konten tempat ia menghasilkan output. Sekitar dua puluh delapan field artikel tersedia, meski akurasinya dicatat inventarisnya, bukan dinilai. Default pengambilan gambar masuk akal (nonaktif). Instalasi bersih di Python 3.14. Aktif dipelihara. Apache-2.0. Hasil kosong mudah disaring jika pemanggil memeriksanya secara eksplisit.
Kekurangan. Recall artikel terendah di set ini, yaitu 0.8243, sepenuhnya disebabkan oleh tidak menghasilkan apa pun, bukan karena menghasilkan hal yang salah. Halaman yang artikelnya berupa daftar akan menghasilkan string kosong, apa pun konfigurasinya. 44.3 MiB dan cold import 2,2 detik tergolong berat dibanding 21.0 MiB dan 15 ms milik resiliparse. Memerlukan close(). Dua default menunjuk ke hal-hal yang keliru di sebagian besar mesin.
Siapa yang cocok memakainya, dan siapa yang sebaiknya tidak
Gunakan goose3 sebagai kandidat ketika teks hasil ekstraksi masuk ke model atau database yang mahal terhadap boilerplate terlabel dan halaman Anda berupa artikel konvensional dengan paragraf. Pada set fixture ini, ia tidak mengeluarkan token boilerplate terlabel ketika menjawab. Permukaan metadata memang tersedia, tetapi belum divalidasi di sini; akurasi judul, penulis, tanggal, dan gambar perlu fixture ground truth terpisah sebelum menjadi keunggulan seleksi.
Lewati jika korpus Anda banyak berbentuk daftar — Anda akan mendapat string kosong tanpa penjelasan. Lewati jika biaya cold start penting, karena resiliparse mengimpor 145× lebih cepat. Dan lewati jika Anda butuh jawaban di setiap halaman, karena “tidak ada jawaban” adalah hasil yang nyata di sini: dua dari 22 fixture, dan keduanya diam-diam, dalam arti string kosong bukan exception.
Pasangan yang layak diuji: goose3 sebagai utama dengan fallback saat cleaned_text kosong atau di bawah aturan konten minimum Anda. Readability berhasil memulihkan seluruh unit artikel pada set 22 fixture ini, termasuk dua kasus kosong milik goose3. Hasil sintetis ini mendukung pola arsitektur, bukan janji bahwa fallback tidak akan pernah meleset di halaman nyata.
Di mana API terkelola cocok
Benchmark ini menguji jalur ekstraksi raw_html milik goose3: HTML sudah didapat lebih dulu sebelum goose3 melihatnya. goose3 juga punya jalur fetch jaringan sendiri, seperti yang ditunjukkan pengaturan User-Agent-nya, tetapi jalur itu tidak diuji di sini. Rendering JavaScript dan perilaku anti-bot juga tidak diuji.
Untuk fixture yang sama di keenam extractor, lihat perbandingan ekstraksi enam library.
Layanan managed fetch/render/extraction, termasuk Thunderbit milik kami, berada di batas tanggung jawab yang berbeda. Thunderbit tidak dibenchmark dalam pengujian ini. Perbedaan yang relevan adalah ekstraksi artikel dari HTML yang disuplai versus layanan hosted yang mengambil dan memproses URL; artikel ini tidak memberikan perbandingan performa atau kualitas dengan metrik yang sama.
Perbandingan yang adil: set field goose3 bersifat tetap dan berbentuk artikel, yang tepat ketika halaman Anda adalah artikel dan salah ketika halaman Anda adalah daftar produk. Jika Anda sudah memegang HTML-nya dan halaman Anda berupa artikel, goose3 gratis dan sangat bersih.
Untuk sisi hosted, ringkasan API web scraping kami memberi gambaran yang lebih luas; untuk alternatif self-hosted, lihat pilar open-source scraper. Jika teksnya akan dipakai oleh model, mengonversi HTML ke Markdown di Python menjelaskan di mana fidelitas benar-benar hilang.
Coba Thunderbit untuk Ekstraksi Data Web
Jadi, apakah Anda harus memakai goose3?
Ya, jika artikel berbentuk paragraf sesuai dengan beban kerja Anda dan pemanggil memperlakukan output kosong sebagai ekstraksi gagal, bukan sukses.
Pada set fixture ini, goose3 tidak mengeluarkan token boilerplate terlabel ketika menjawab dan mengembalikan dua string kosong. Satu adalah halaman yang nyaris kosong dan satu lagi adalah body sintetis yang hanya terdiri dari daftar. Itu adalah trade-off precision/coverage, bukan bukti temperamen yang berlaku untuk seluruh produk.
Jika recall lebih penting, uji fallback dengan guard output minimum yang eksplisit. Readability memulihkan setiap unit artikel dalam 22 fixture ini; newspaper4k menunjukkan nol kebocoran unit boilerplate terlabel dan recall 0.9865 sambil tetap menghasilkan output pada semua 22 halaman. Hasil ini menempatkan set sintetis ini di level default, bukan pada workload produksi yang belum diketahui.
goose3 layak dipilih ketika biaya satu kata yang salah lebih tinggi daripada biaya satu halaman yang hilang.
Coba Thunderbit untuk Ekstraksi Data Web Get Started Free
FAQ
Apakah precision sempurna milik goose3 itu nyata, atau sekadar efek karena ia menolak memproses? Keduanya, dan keduanya bisa dipisahkan. Ia dihitung pada 10 dari 11 fixture yang mengandung boilerplate, jadi satu fixture memang tidak masuk rata-rata — bagian itu adalah artefak dari penolakan. Tapi pada sepuluh fixture itu, ia mengembalikan nol token pengotor terhadap boilerplate yang sengaja dibuat adversarial, sementara Readability bocor 35. Precision itu nyata untuk halaman yang dijawabnya; kolom recall-lah yang menunjukkan efek penolakannya.
Kenapa goose3 tidak mengembalikan apa pun pada halaman yang artikelnya berupa daftar?
Scoring kandidatnya membutuhkan blok berbentuk paragraf untuk menemukan tubuh artikel, dan halaman yang dibangun dari <li> tidak memilikinya. Default parse_lists=True tidak mengubah hal ini — saya mencoba secara eksplisit, plus strict=False, dan hasilnya nol karakter pada ketiga konfigurasi, sementara control berbasis <p> menghasilkan 937 pada ketiganya. parse_lists hanya menentukan apakah daftar dipertahankan di dalam artikel yang sudah ditemukan.
Apakah saya harus memanggil close()?
Tutup secara eksplisit dengan try/finally, seperti contoh di atas. Review ini tidak mengukur apa yang menumpuk jika penutupan diabaikan, jadi tidak mengklaim adanya leak loop yang terkuantifikasi; tetapi review ini memang menunjukkan bahwa Goose punya siklus hidup yang harus dikelola pemanggil.
Apa User-Agent yang dikirim goose3?
Goose/3.1.22 secara default — nama library dan versi persisnya. Itu hanya berlaku jika Anda membiarkannya mengambil halaman sendiri, dan jika Anda mengirim raw_html, hal itu tidak berlaku sama sekali. Jika Anda membiarkannya fetch, atur User-Agent secara sengaja; default-nya memberi tahu setiap server yang Anda sentuh dengan tepat apa yang memanggil.
Apa yang tidak diuji oleh review ini?
Halaman dunia nyata sama sekali — ini semua fixture terkontrol. Ekstraksi multibahasa, meski target_language adalah pengaturan kelas utama. Field metadata (judul, penulis, tanggal, gambar utama) dicatat inventarisnya tetapi tidak dinilai akurasinya. Pengambilan gambar, yang default-nya nonaktif dan path ImageMagick-nya menunjuk ke package manager yang tidak dimiliki kebanyakan mesin. Perilaku memori pada beban simultan atau beban berkelanjutan, plus throughput di bawah beban; tabel memori hanya mengukur satu proses baru yang menangani satu dokumen.


