Mozilla Readability İncelemesi: Kusursuz Kayıt, Kardeş Sızıntısı ve Tahmin Edici Yanlış Negatifler

Son güncelleme: August 17, 2026
Mozilla Readability İncelemesi: Kusursuz Kayıt, Kardeş Sızıntısı ve Tahmin Edici Yanlış Negatifler
AI Özeti
Mozilla's Readability, Firefox'un Reader View özelliğinin arkasındaki çıkarıcının bağımsız JavaScript portudur. Apache-2.0 paketi @mozilla/readability olarak yayımlanır; canlı bir DOM üzerinden makale içeriğini seçer. Bu nedenle Node altında jsdom gibi bir DOM uygulaması gerekir. Sayfa çekmez, JavaScript çalıştırmaz ve şema çıkarımı yapmaz. Test edilen sürüm npm latest üzerindeki 0.6.0’dır; 3 Mart 2025’te yayımlanmıştır ve 27 Temmuz 2026’da depoda 11.361 yıldız vardı (son push 9 Temmuz 2026; yani main, yayımlanan paketin oldukça önündeydi).

Mozilla's Readability, Firefox's Reader View arkasındaki çıkarıcının bağımsız JavaScript portudur. Apache-2.0 paketi @mozilla/readability olarak yayımlanan bu araç, canlı bir DOM üzerinden makale içeriğini seçer. Bu yüzden Node altında jsdom gibi bir DOM uygulamasına ihtiyaç duyar. Sayfayı çekmez, JavaScript çalıştırmaz ve şema çıkarımı yapmaz.

Test ettiğim sürüm npm latest olarak 0.6.0’dır; 3 Mart 2025’te yayımlanmıştır. 27 Temmuz 2026’da kontrol ettiğimde depoda 11.361 yıldız vardı (son push 9 Temmuz 2026; yani main, yayımlanan paketin epey önündeydi). Bunu jsdom 29.1.1 ve Node v22.22.3 üzerinde, macOS arm64 ortamında, özel olarak hazırlanmış 22 etiketli HTML fixture’a karşı çalıştırdım; buradaki tüm ölçümler o kurulumdan geliyor. Pratikte bu kategori içindeki en az uğraştıran araçlardan biri: kurulum iki dakika, ikili dosya yok, cache içinde bekleyen tarayıcı yok, her çalıştırmada aynı çıktı. İlginç olan şey onu kullanmak değil; hataların kaynak koddaki birkaç sabitten öngörülebilir olması ve bu sabitlerden birinin, dokümanların ima ettiğinden daha fazlasını belirlemesi.

Kontrollü 22 sentetik fixture boyunca Readability, etiketlenmiş 74 makale bloğunun tamamını geri getirdi. Ancak bu sınırlı sonuç, makale metnini asla kaybetmediği anlamına gelmiyor: herkese açık gerçek sayfa benchmark’ında geri çağırma 0,982 olarak raporlanıyor ve burada bilinen hata türleri yeniden üretilemedi. Fixture düzeyinde en görünür hata, kaynak koddaki 0,25 eşik değerine bağlı komşu içerik sızıntısıydı. Görülen etki, test ortamına göre değişiyor.

readability.js aslında nedir ve üç şey neden değildir

Readability, bir DOM üzerinde kural tabanlı skorlamadır. Aday öğeleri dolaşır, her birine içerik puanı verir, bu puanları üst öğelere taşır, en yüksek puanlı alt ağacı seçer ve ardından sayfa dekoru gibi görünenleri temizlemek için birkaç son geçiş uygular. Tüm makale çıkarma stratejisi budur — model yok, eğitim verisi yok, siteye özel kural yok. Bu tasarım, daha önce hiç görmediği bir sayfada çalışmasının sebebi; aynı zamanda kaynak koddan okunabilir şekilde hata vermesinin de sebebi. İşin eğlenceli kısmı burada.

Ama üç şey değildir ve insanlar en çok bu üçünde yanılır:

  • Fetcher değildir. URL değil, document alır. Sayfayı çekmek, yeniden denemek, bot korumasını aşmak ve başlıklar sizin sorumluluğunuzdadır.
  • Renderer değildir. JavaScript çalıştırmaz. Elinizdeki DOM ne içeriyorsa sadece onu görür.
  • Yapısal çıkarıcı değildir. Size title, byline, excerpt, content (HTML), textContent, length, siteName verir. Şema, tiplenmiş satırlar, {name, price} yoktur.

İşin çoğunu dört sabit yapıyor

System diagram: Four constants do most of the work

node_modules içindeki Readability.js dosyasını okumak, davranış hakkında doküman sayfalarından daha fazla şey söyler. Kütüphanenin yaptığı işin çoğunu dört mekanizma açıklar:

  • Skorlanan paragraf başına içerik puanı: 1 + (commaCount + 1) + min(floor(len / 100), 3). 25 karakterin altındaki paragraflar hiç sayılmaz. Puanlar üst öğelere bölünerek taşınır — ebeveyn tam puan, büyükanne/büyükbaba yarım puan, daha derin atalar level · 3.
  • DEFAULT_CHAR_THRESHOLD = 500 — “başarılı” bir ayrıştırma için asgari makale uzunluğu. Bunun altındaysa, temizlik bayrakları azaltılarak alma işlemi yeniden denenir.
  • unlikelyCandidates regex’icomment, footer, menu, related, sidebar, social, sponsor gibi class ve id parçalarını yakalar. Eşleşen düğümler skorlamadan önce elenir.
  • grabArticle içindeki kardeş ekleme kapısı — en iyi aday seçildikten sonra onun kardeşleri dahil edilmeyi değerlendirilir. Bir kardeş, kendi puanı eşiği aşarsa veya nodeLength > 80 && linkDensity < 0.25 ise veya nodeLength < 80 && nodeLength > 0 && linkDensity === 0 && içinde nokta varsa eklenir.

Link yoğunluğu Σ(linkText.length · coef) / textLength şeklindedir; coef çıplak # href’ler için 0.3, diğerleri için 1’dir. Buradaki kardeş sızıntılarını açıklayan şey bu kapıdır; çıkarma algoritmasının tamamı değildir.

Kurulum ve vaat metninde olmayan bağımlılık

npm install @mozilla/readability jsdom yeterlidir, çalıştırırsınız. İki dakika, ikili dosya yok, kurulum sonrası indirme yok, cache içinde bekleyen tarayıcı yok. Kurulum açısından neredeyse ideal.

Ama “sıfır bağımlılık” ifadesi algoritma içindir, çalışma zamanı için değil. Readability canlı bir document üzerinde çalışır; Node tarafında ise DOM uygulamasını siz sağlarsınız — burada 29.1.1 sürümüyle jsdom kullandım. jsdom küçük değildir ve çoğu iş akışında döngüdeki asıl maliyet çıkarma değil, DOM kurulumudur. Buna bütçe ayırın.

Bana ikinci bir çalıştırma kaybettiren bir footgun daha var: Readability.parse() verilen DOM’u değiştirir. Aynı jsdom belgesini iki kez parse ederseniz, ikinci çağrı ilk çağrının zaten darmadağın ettiği bir belge görür. Benim test ortamımda her parse için yeni bir jsdom oluşturuldu. Sayfaları döngüyle işlerken zaman kazanmak için belge nesnesini yeniden kullanıyorsanız, size hata olarak geri dönecek şey bu.

Nasıl test ettim

Bunu canlı haber sitelerine bağlamadım. Canlı sayfalar size bir skor verir ama nedenini görmenizi sağlamaz — halbuki bir sezgisel yöntemde “neden” bütün değerdir. Bunun yerine 91 etiketli bloğa (74 makale, 17 iskelet içerik) sahip 22 HTML fixture ürettim; her bloktaki her kelime, o bloğa özgü bir sentinel dizesiyle öne eklenmiş durumda. Bloklar arasında sözlükler ayrık olduğu için çıkarılan her token tam olarak tek bir bloğa eşleşiyor; böylece “geri kazanıldı” ya da “sızdı” kararı bulanık değil, tam üyelik testi oluyor.

Çıkarma adımı ile skorlamayı bilerek ayırdım. Node çalıştırıcısı yalnızca ham çıkarılmış metni, isProbablyReaderable boolean’larını ve ölçülmüş link yoğunluklarını döker. Tüm precision ve recall, bu ham metinlerden sonra ayrı bir script ile etiketlere göre hesaplanır. Ortamda elle yazılmış tek bir metrik sabiti bile yok; kendi sayılarımda güvenebildiğim tek yol bu.

Sonra aynı bytes dizisini trafilatura 2.1.0’a da verdim ve aynı test ortamında bir karşılaştırma aldım. Her fixture üç kez parse edildi; 22’si de her çalıştırmada byte-identical metin verdi.

Her aşamayı özet tabloya güvenmek zorunda kalmadan inceleyebilirsiniz. tests/build_fixtures.mjs açıklamalı HTML’i ve ground truth’u üretir; tests/run_readability.mjs çıkarım ve tahmin edici çıktısını kaydeder; tests/metrics.py ise bu kayıtları sonradan puanlar. Ham Readability çıktısı, hesaplanan metrikler ve aynı girdi karşılaştırması artifacts/raw/ içinde tutuluyor. Bir sonuç şüpheli göründüğünde bu ayrım önemlidir: ayrıştırıcının beklenmedik metin döndürüp döndürmediğini, etiket kümesinin yanlış olup olmadığını veya puanlama kodunun yanlış sınıflandırıp sınıflandırmadığını anlayabilirsiniz. Bu paket üzerindeki yeniden üretim, burada iddia edilenleri denetler; ancak bir dağıtımdaki gerçek sayfa karışımının aynı hata dağılımına sahip olduğunu kanıtlamaz.

Kapsam sınırı gerçek ve önemlidir: bunlar gerçek dünya korpusu değil, sentetik kontrollü sayfalardır. Yetkili gerçek sayfa rakamları, herkese açık article-extraction-benchmark kaynağından gelir; bu kaynak readability_js 0.6.0 sürümünü — burada test edilen tam sürüm — yaklaşık 181 gerçek sayfa üzerinde word-F1 0,947 ± 0,005 (precision 0,914 ± 0,008, recall 0,982 ± 0,003) olarak puanlıyor. Bunu aktarıyorum; yeniden üretmedim.

Burada mevcut sürüm benchmark satırları kullanılmıştır; geçerliliğini yitirmiş tarihsel satırlar hariç tutulmuştur. Kontrollü fixture’lar, kamuya açık gerçek sayfa korpusunun yerine geçmektense, hangi içerik biçiminin hangi kuralı tetiklediğini blok bazında gösterir.

Sentetik fixture paketinde geri çağırma kusursuzdu

74’ün 74’ü. 22 fixture’ın tamamında Readability etiketli tek bir makale bloğunu bile düşürmedi — ve makale ile iskelet içeriği karıştıran on bir temiz sentetik fixture’da mikro ortalama token recall 1.000 çıktı. Tek bir makale cümlesi bile kaybolmadı.

Buna iki not düşülmeli:

Bunlar temiz, tek sütunlu sentetik sayfalar. Gerçek makaleler daha derine gömülür, ortada reklamlar girer ve bazen ilk paragrafını bir skor artefaktı yüzünden kaybeder — bu tür kaçırmalar izleyicide raporlanmıştır (#437, #901 ve bir tablodan önceki içeriğin düşmesi için #922). Benim fixture’larım bunların hiçbirini tetiklemedi; dolayısıyla bunların düzeltildiğini iddia etmiyorum — sadece testimin o noktalara ulaşmadığını söylüyorum. Gerçek sayfalarda bu sürüm için benchmark geri çağırması 1.000 değil, 0,982’dir.

Yine de sonucun yönü önemli. Readability’nin sorunu makalenizi tamamen atması değil. Yanında neyi getirdiğidir.

Precision sayısı ve neden üç etikete ihtiyaç duyduğu

Measured results chart: Three scopes behind the precision story

Tek bir sayı söylemesi kolay, savunması zordur. On bir karışık fixture’da Readability 17 iskelet bloğunun 5’ini tuttu — sızıntı oranı 0,294.

Bu, gerçek dünya sızıntı oranı değildir. Üç farklı kurulum üç farklı şeyi ölçer ve bunlardan yalnızca biri sıradan sayfaları anlatır:

Sayının neyi ölçtüğüSonuç
Saldırıya dayanıklı ağırlıklı fixture seti — karışık 11 sayfanın 6’sı kardeş kapısını yenmek için özel olarak tasarlandı17 iskelet bloğunun 5’i tutuldu (0,294)
Tek gerçekçi sayfa<article> gövdesi; etrafında nav, reklam bandı, sidebar, yorumlar ve footer ve nötr sınıflı bir promo bloğu6 chrome bloğunun 5’i temizlendi; 1’i kaldı
~181 gerçek sayfa, herkese açık benchmark (benim çalıştırmam değil)precision 0,914, recall 0,982, word-F1 0,947readability_js 0.6.0

İlk satırı bir stres testi olarak okuyun, tahmin olarak değil. Readability, vahşi ortamda iskelet içeriğin %29’unu sızdırmaz. Gerçekçi sayfada unlikelyCandidates regex’inin eşleştiği sınıf taşıyan her şey — nav-menu, ad-banner, sidebar, comments, site-footer — temizce atıldı; beşi de gitti. Sağ kalan tek blok, o regex’ten kaçacak şekilde tasarladığım bloktu.

0,25 kapısı: iskelet temizliğinin durduğu yer

Kardeş ekleme kuralı kaynak kodda belgelenmiştir. Bildiğim kadarıyla kimsenin ölçmediği şey, tam olarak nerede devreye girdiğidir. Bu yüzden bir gradyan kurdum: <article> dışında duran, nötr sınıflı bir <p class="teaser-block">, top candidate olarak kazanması garanti dört paragraflık belirgin bir makale ve yalnızca promo bloğunun uzunluğu ile link yoğunluğu değişiyor. Yoğunluklar, Readability’nin kendi formülüyle, varsayım yerine çalışma anında ölçülüyor:

Promo bloğuİç metin uzunluğu80 karakter üstü müÖlçülen link yoğunluğuSonuç
Hiç link yok126evet0,000tutuldu
Tek kısa link126evet0,143tutuldu
Tek daha uzun link126evet0,278düştü
Metnin yarısı linkli126evet0,476düştü
Tek cümle, nokta ile bitiyor60hayır0,000tutuldu
Aynı metin, nokta yok59hayır0,000düştü

Kaynak koşulu 0,25 eşiğini kullanıyor; ölçülen örnekler bunu iki taraftan sardı: 0,143 tutuldu, 0,278 düştü. Ayrı bir dal, noktayla biten 60 karakterlik cümleyi tuttu; noktasız 59 karakterlik sürüm düştü. Makale geri çağırması her dalda 4/4 kaldı, yani bu örnekler yalnızca precision etkisini izole ediyor.

Test ortamı dışında bu kapı fiilen şunu söylüyor: makalenin yanında duran uzun, düşük-linkli, nötr düz yazı da makaledir. Bu da makale olmayan birçok şeyi kapsar — paragraf gibi yazılmış bir “İlgili okuma” notu, bir bülten tanıtımı, pazarlama ekibinin tam cümlelerle yazdığı ve takip amacıyla linkleri kaldırılmış sponsorlu bir teaser.

Bir RAG indeksinde düşük-linkli bir promo paragrafı çıkarılmış bir parça hâline gelebilir ve retrieval ya da generation’ın onu makale içeriği sanmasına yol açabilir. Kaynak kuralı bu hata modunu mümkün kılar; bu inceleme uçtan uca retrieval ya da model alıntısı değerlendirmesi yapmadı.

Siteye özgü sert bir filtre için, bilinen kaynak-DOM konteynerlerini önceden filtreleyin, serileştirmeden önce karşılaştırma için kaynak düğüm ata-soyunu koruyun ya da sonradan dikkatle doğrulanmış metin desen filtrelemesi uygulayın. Dönen HTML artık bir düğümün başlangıçta birincil konteynerin dışında olup olmadığını korumayabilir. Kardeş kapısı, herkese açık seçeneklerle ayarlanamaz.

Fixture’ların çürüttüğü üç varsayım

Fixture’lar üç varsayımı çürüttü: charThreshold’ün kısa makaleleri reddettiği, semantik etiketlerin gerekli olduğu ve kısa nesir-dışı içeriğin atıldığı varsayımları. Aşağıdaki kanıtlar ilgili kısımdır; ön kayıt iddiasına gerek yoktur.

charThreshold = 500 bir uçurum değil

Halk arasındaki yorum, 500 karakter altındaki bir makalenin null döneceği yönündedir. Dönmüyor. Gövde uzunluğunu 120 ile 1500 karakter arasında, charThreshold değerlerini 200, 500 ve 1000 olacak şekilde taradım:

Gövde uzunluğuHer eşikte parse başarılı mı?Çıkarılan uzunluk
120evet161
300evet342
460evet509
520evet569
800evet841
1500evet1555

Düz. Her gövde boyutunda, üç eşik ayarında da aynı çıkarılan uzunluk. Eşik, dönüş değerini kapatmıyor — temizleme bayrakları kaldırılarak alma işleminin yeniden çalıştırılıp çalıştırılmayacağına karar veriyor; temiz bir sayfada kaldırılacak bir şey olmadığı için, sonuç iki durumda da aynı. Gerçek null sınırı “hiç çıkarılabilir metin olmaması”dır.

Burada asıl hata da bu olur ve sahte bir null’dan daha tatsızdır. Neredeyse boş bir sayfa verdim — bir nav bar ve dört kelimelik bir blurb. Başarıyla döndü ve döndürdüğü “makale” nav bar’ı da içeriyordu. Gerçek bir makale yoksa Readability, iskelet içeriği makale olarak verir. Ölçekli taramada non-null sonucu “bu sayfada içerik vardı” diye yorumluyorsanız, bu varsayım yanlıştır.

Semantik etiketler işi yapmıyor

İskeleti kaldırdığımda geri çağırmanın düşmesini bekliyordum. Aynı makale metni, iki görünüm: biri <main><article><h1> ve açıklayıcı class adlarıyla, diğeri <div class="x1"> ve düz <div> paragraflarıyla. Sonuç: her iki durumda da 4/4 makale bloğu kurtarıldı, iskelet sızıntısı 0. Makale sayfadaki açık ara en yoğun metin bloğu olduğunda, uzunluk ve virgül skorlaması semantik yardıma gerek duymadan onu buluyor. “Readability’nin <article> etiketlerine ihtiyacı var” halk söylemidir.

Bu iddianın dürüst sınırı şu: Benim sayfamda tek ve bariz bir içerik bloğu vardı. Semantiğin gerçekten fark yaratabileceği yer, iki rekabet eden yoğun alt ağacın bulunduğu bir sayfadır; ben o beraberlik bozmayı test etmedim.

Nesir dışı içerik olduğu gibi kalıyor

“25 karakterin altındaki paragraflar sayılmaz” kuralı, tablolar ve altyazılarda kayıp beklememe neden olmuştu. Yanlış çıktı — bu kural aday skorlamasını, korumayı etkilemiyor. Konteyner kazandığında içindeki her şey de birlikte gelir:

Makale içindeki içerik türüReadabilitytrafilatura
Nesir paragrafları (×2)tutuldututuldu
Veri tablosu hücreleri (×2)tutuldututuldu
<pre> kod bloğututuldututuldu
25 karakter altı tek satırlık <p> (×2)tutuldututuldu
<figcaption>tutuldudüştü
Toplam8/87/8

Bu, daha sert temizleyicinin kaybettiği bir eksendir. Sayfalarınız dokümantasyon, öğretici içerik ya da kod blokları ve altyazılı görseller içeriyorsa, Readability’nin kazanan alt ağacın tamamını koruma davranışı bir özelliktir.

isProbablyReaderable, parse() evet derken hayır diyor

System diagram: isProbablyReaderable says no when parse() says yes

README, tam parse’a geçmeden önce ucuz bir ön kontrol olarak isProbablyReaderable(doc) çağrılmasını önerir. Testlerimde bu kapı, parse() sonrasında gayet iyi çalışan üç ayrı sayfa biçimini reddetti:

Sayfa biçimiTahmin edici kararıparse()Hangi ayar düzeltir
İçerik yalnızca <li> öğelerindefalsebaşarılıhiçbiriminScore 1–80 ve minContentLength 40–200 boyunca da false
Her biri 140 karakterin altında on paragraffalsebaşarılıminContentLength ≤ 100 (minScore hiçbir şey yapmaz)
408 karakterlik tek paragraffalsebaşarılıminScore ≤ 10 (skor ≈16,4)
Normal makale (kontrol)truebaşarılı

Üç başarısızlığın üç ayrı nedeni var ve yalnızca ikisi ayarlanabilir. <li> durumu yapısaldır: tahmin edici yalnızca p, pre ve article düğümlerini (ve div > br ebeveynlerini) puanlar; içeriği liste öğelerinde yaşayan bir sayfa hiçbir şeyle eşleşmez, sıfır puan alır ve eşik ayarıyla kurtarılamaz — bu biçim zaten #662 numaralı issue’da raporlanmıştır. Çok kısa-paragraf durumu bir minContentLength kapısıdır; her paragrafı skorlamadan önce atladığı için on sağlam paragraf sıfıra toplanır; bu değeri düşürmek düzeltir, minScore ayarlamak düzeltmez. Tek-paragraf durumu ise aritmetiktir: skor sqrt(408 − 140) ≈ 16,4 olur; bu da varsayılan minScore 20’nin altındadır — tek başına bir paragrafın eşiği geçebilmesi için 140 + 20² = 540 karakter gerekir.

README tahmin edicinin yanlış negatif üretebileceğini söylüyor. Benim ekleyeceğim pratik kural şu: onu tek kapınız olarak kullanmayın. Bir sayfa önemliyse, parse edin ve sonuç uzunluğunu kontrol edin. Zaten ödediğiniz jsdom kurulumuna kıyasla parse o kadar pahalı değildir.

Aynı bytes, iki çıkarıcı

Aynı fixture’lar üzerinde trafilatura 2.1.0 çalıştırmak, iki farklı test ortamında ölçülmüş iki sayıyı yan yana koymaktan daha temiz bir okuma sağlar; çünkü girdi byte-byte aynıdır:

Ölçü (11 karışık fixture)@mozilla/readabilitytrafilatura
Makale bloğu recall1,0001,000
Tutulan iskelet blokları5/17 (0,294)1/17 (0,059)
Token F1 (mikro)0,9480,969
Nesir-dışı recall8/87/8
Çok kısa makale (120 karakter), token F10,8000,571

Bu fixture’larda ikisinden biri açık ara üstün değil. Trafilatura daha az kardeş blok tuttu; Readability ise daha fazla kısa ve nesir-dışı içeriği korudu. Her iki aracın mutlak token precision’ı, etiketlenmemiş başlık metinleri nedeniyle aşağı çekiliyor; bu yüzden blok düzeyindeki sızıntı sayısı daha temiz ve doğrudan bir sinyal. Kamuya açık gerçek sayfa benchmark’ı bunları word-F1 açısından benzer sıralasa da, korpuslar ve metrikler farklıdır; bu, test ortamları arası doğrulama değildir.

Kısaca sağlamlık: Kasıtlı olarak bozulmuş bir ikiz sayfa da çalıştırdım (kapanmamış <p>, yanlış iç içe geçmiş <b>/<i>, fazladan bir </div>); geri çağırma 3/3 ve sıfır sızıntı oldu, düzgün sürümle aynı. Bunun kredisi, Readability onu görmeden önce karmaşayı düzelten jsdom HTML5 ağaç oluşturucusuna aittir. Hiçbir fixture ayrıştırıcıyı çökertmedi.

Artılar ve eksiler

Artılar

  • Makale geri çağırması güçlü tarafıdır: 22 sentetik fixture boyunca 74/74 etiketli blok geri alındı; karışık sette token recall 1,000.
  • Regex ile işaretlenen sayfa dekoru güvenilir biçimde temizlenir — gerçekçi sayfada nav, reklam bandı, sidebar, yorumlar ve footer’ın hepsi gitti (6’nın 5’i).
  • Nesir dışı içerik eksiksiz korunur: tablolar, <pre> kodu, görsel altyazıları ve 25 karakter altı satırlar olduğu gibi kaldı (8/8); trafilatura ise bir altyazıyı düşürdü.
  • Semantik işaretlemeye bağımlı değildir — nötrleştirilmiş bir <div> makalesi, <article>/<main> sürümüyle aynı puanı aldı.
  • Kısa makaleleri yanlış reddetmez: temiz içerik 120 karaktere kadar kurtarıldı ve charThreshold 200/500/1000 arasında aynı kaldı.
  • Tamamen deterministik: 22 fixture’ın tamamı üç çalıştırmada da aynı metni verdi.
  • Kurulum iki dakika, Apache-2.0 ve npm’deki sürüm test ettiğim sürümle aynı (0.6.0); yani burada bayat bir şey yok.

Eksiler

  • Kardeş ekleme kapısı suistimal edilebilir: uzun, düşük-linkli, nötr class’lı promo nesri makale metninden ayırt edilemez ve linkDensity < 0.25 iken birlikte gelir.
  • İçerik fakiri sayfalarda null yerine iskelet içeriği makale olarak döndürür — neredeyse boş fixture, gövde olarak nav bar’ıyla geldi.
  • isProbablyReaderable, üç ayrı sayfa biçiminde yanlış negatif üretir; bunlardan biri hiçbir ayarla düzeltilemez.
  • Çalışma anında tam DOM ister — “bağımlılık yok” ifadesi jsdom maliyetini gizler; döngüdeki asıl yük odur.
  • parse(), giriş belgesini değiştirir; bu yüzden her sayfa için DOM’u yeniden oluşturmanız gerekir.
  • Çekme yok, JavaScript render yok, yapısal çıktı yok. Bu bir pipeline’ın tek aşamasıdır, pipeline’ın kendisi değil.
  • İzleyicide raporlanan gerçek sayfa kaçırmaları (ilk paragraf ve tablodan önceki içerik düşmeleri) benim fixture’larımda yeniden oluşmadı; dolayısıyla bunların nadir mi yoksa benim sayfalarımın o koşullara hiç ulaşmadığı mı belli değil.

Kim kullanmalı, kim kaçınmalı

Elinizde zaten HTML varsa ve onu saf JavaScript ile, Python bağımlılığı eklemenin zahmetli olacağı bir Node servisinde makaleye dönüştürmek istiyorsanız Readability iyi bir tercihtir. (Zamanlamayı düzgün bir dağılım olarak toplamadım, bu yüzden “jsdom kurulumunun döngüde çıkarımdan daha ağır bastığı” dışında hız iddiasında bulunmuyorum.) Reader-mode özellikleri, çevrimdışı makale arşivleme, e-posta bültenleri, “temiz görünüm” düğmeleri, tarayıcı eklentileri, kod blokları ve altyazılar içeren dokümantasyon akışları — bu alan onun alanıdır ve geri çağırma sayıları bunun iyi bir alan olduğunu söylüyor. Davranışı kaynak koddan doğrudan okunabildiği için, belirli bir bloğun neden geçtiğini ekip arkadaşınıza açıklamanız gerektiğinde değeri beklenenden fazladır.

İskelet temizleme precision’ı not aldığınız metrikse ve özellikle de bir LLM indeksine beslediğiniz yerde tek bir promo paragrafının geri getirilebilir bir parçaya dönüşmesi riskse ondan kaçının. Sayfalarınız istemci tarafında render ediliyorsa da kaçının; çünkü size verdiğiniz DOM’u okur, JavaScript çalıştırmaz. İhtiyacınız {title, price, sku} gibi yapılandırılmış alanlarsa, bunu içerik çıkarıcıya çevirecek bir ayar yoktur. Ve “bu sayfada gerçekten makale var mıydı?” sorusunun gerçek olduğu durumlarda, non-null dönüşü cevabınız olarak güvenmeyin.

Alternatifler ve Thunderbit yığınının yeri

Buradaki hiçbir şey, Mozilla tarafından sürdürülen ücretsiz Apache-2.0 kütüphaneye bir eleştiri değildir — Readability bir altyapıdır, Firefox içinde yıllardır çalışır ve reader-mode çıkarımı için referans uygulama olmasının bir nedeni vardır. Daha geniş alanı görmek isterseniz, open-source scrapers roundup içinde canlı bir karşılaştırma ve the best web scraping tools içinde daha kapsamlı bir tarama tutuyorum.

Tüm altı çıkarıcı için aynı fixture’lar üzerindeki karşılaştırmayı six-library extraction comparison yazısında görebilirsiniz.

Yazar notu: Thunderbit URL’den render etme ve çıkarım için yönetilen seçeneğimizdir. Bu fixture’lar üzerinde çalıştırılmadı; dolayısıyla eşleşen kalite iddiası ima edilmez. Buradaki önemli sınır, elinizde zaten bir DOM olup yerel makale çıkarımı mı istediğiniz, yoksa çekme/render etme ve yapılandırılmış çıktının servis olarak işletilmesini mi istediğinizdir. Self-hosting, bir satıcı kullanım ücretinden kaçınır ama yine de altyapı ve bakım maliyeti taşır.

Dürüst takas şu: Readability ücretsizdir, şeffaftır ve kontrol sizdedir — çıktınızı hangi kapının belirlediğini tam olarak okuyabilirsiniz; yönetilen API’lerde bu olmaz. Yönetilen bir yığın para ister ve mekanizmayı gizler, ama sizin tek tek kurmanız gereken çekme-render-yapı aşamalarını kapsar. Bu spektrumun AI destekli ucunu istiyorsanız, başka yazılarımda AI ile herhangi bir web sitesini nasıl scrape edeceğinizi ve AI crawler’ları anlattım. Hangi aşamaları gerçekten sahiplenmek istediğinize göre seçim yapın.

Web Veri Çıkarma için Thunderbit’i Deneyin

Sonuç

Readability, elinizde zaten bir DOM varsa, JavaScript/Node kullanıyorsanız ve agresif atlamaya kıyasla ara sıra gelen fazladan kardeş içeriğini tercih ediyorsanız güçlü bir adaydır. Bu fixture paketinde 74 etiketli makale bloğunun tamamını kurtardı ve tabloları, kodu ve altyazıları korudu. Bu sonuç sentetik tek sütunlu sayfalarla sınırlıdır; kamuya açık gerçek sayfa recall’ı 0,982’dir, bilinen ilk paragraf/tablo yanı kaçırmaları yeniden oluşmadı ve içerik fakiri sayfalar iskelet içeriği makale olarak döndürebilir.

Zayıflığını doğru boyutlandırın. Bu fixture’larda ana hata yüzeyi precision’dır ve kaynak kodda açıkça belgelenmiş belirli bir satırda yer alır: 80 karakteri aşan ve link yoğunluğu 0,25’in altında olan bir kardeş, ister makalenin parçası olsun ister olmasın, makalenize eklenir. Bunu aynı metinde 0,143 ile 0,278 arasında bir geçiş olarak gördüm. Gerçek sayfa benchmark’ı precision 0,914 ve recall 0,982 rapor ediyor. Çıkarılmış metni daha sonra modelin alıntılayacağı bir indekse aktarıyorsanız, ne tutulan iskelet içeriği ne de atlanan gövde metnini varsaymayın; ikisini de kontrol edin.

Web Veri Çıkarma için Thunderbit’i Deneyin Get Started Free

SSS

Mozilla Readability tüm iskelet içeriği kaldırır mı? Hayır; üstelik sayı, neyi ölçtüğünüze çok bağlıdır. Kamuya açık gerçek sayfa benchmark’ında readability_js 0.6.0, precision 0,914 alır — yani döndürdüğünün yaklaşık %8,6’sı gövde içeriği değildir. Benim gerçekçi test sayfamda 6 chrome bloğunun 5’ini temizledi (nav, reklam, sidebar, yorumlar, footer gitti), geriye yalnızca nötr sınıflı bir promo paragrafı kaldı. Kasıtlı olarak sezgisel kurala karşı tasarladığım bloklarla ağırlıklandırılmış fixture setimde ise 17 bloğun 5’ini tuttu — son rakam bir stres testidir, gerçek dünya oranı değil.

Node içinde readability.js kullanmak için jsdom gerekiyor mu? Evet, ya da başka bir DOM uygulaması gerekir. Readability saf JavaScript’tir ama canlı bir document nesnesi üzerinde çalışır; Node altında DOM’u sizin sağlamanız gerekir — benim kurulumumda jsdom 29.1.1. “Bağımlılık yok” ifadesi algoritma içindir, çalışma zamanı için değil. Ayrıca parse() verilen belgeyi değiştirdiğinden, her sayfa için aynı nesneyi yeniden kullanmak yerine yeni bir DOM oluşturun.

charThreshold seçeneği gerçekte ne yapar? Çoğu kişinin sandığı şeyi yapmaz. Kısa makaleleri null döndürerek engellemez — 120 karaktere kadar temiz makaleleri geri aldım ve charThreshold 200, 500 ve 1000 iken çıkarılan uzunluk aynı kaldı. Eşik, ayrıştırıcının temizleme bayrakları kapatılmış şekilde alma işlemini yeniden çalıştırıp çalıştırmayacağını kontrol eder; temiz bir sayfada kaldırılacak bir şey olmadığı için çıktı iki durumda da aynıdır. Gerçek null durumu, hiç çıkarılabilir metin olmayan sayfadır; yalnızca nav içeren bir sayfa bile null dönmedi, nav’ı makale olarak verdi.

parse() öncesinde isProbablyReaderable çağırmalı mıyım? Kapı olarak değil, ipucu olarak kullanın. Bu fonksiyon, parse() sonrasında başarılı olan üç sayfa biçiminde false döndü: içerik <li> öğelerinde olan sayfalar, her biri 140 karakterin altında on paragraf içeren sayfalar ve tek bir 408 karakterlik paragraf. <li> durumu ayarla düzelmez; çünkü tahmin edici yalnızca p, pre ve article düğümlerini puanlar. Çok kısa-paragraf durumu daha düşük minContentLength ister. Tek uzun paragraf durumu daha düşük minScore ister; çünkü tek bir paragrafın varsayılan eşiği geçebilmesi için 540 karaktere ulaşması gerekir. Sayfa önemliyse, parse edin ve sonucu kontrol edin.

Makale çıkarımı için Readability mi trafilatura mı? Aynı fixture bytes üzerinde trafilatura daha az iskelet içerik tuttu (5/17 bloğa karşı 1/17), buna karşılık Readability daha fazla kısa içeriği geri getirdi ve trafilatura’nın düşürdüğü bir <figcaption>’ı korudu. Seçimi hata toleransına ve çalışma zamanı gereksinimine göre yapın. Kamuya açık benchmark, bu fixture sonucunun doğrulaması değil, ayrı bir bağlamdır.

Ke
Ke
Thunderbit’ta CTO | Kıdemli Veri Bilimci ve ML Uzmanı Makine öğrenimi ve veri bilimi alanında yaklaşık on yıllık deneyime sahip olan Ke Shen, Columbia University mezunu ve Walmart Labs’te eski Kıdemli Veri Bilimci’dir. Python, R, Java ve İstatistik konularında derin ve meslektaşları tarafından tanınan uzmanlığıyla, karmaşık yapay zekâ algoritmalarını teoriden üretim düzeyinde mimariye taşıma konusunda sahada test edilmiş içgörüler paylaşır.
İçindekiler
Thunderbit · AI web veri ajanı

Herhangi bir sayfadan 1 tık içinde veri çıkar

250.000+ kullanıcı tarafından güveniliyor
ücretsiz plan mevcut
Web sayfasından tabloya
İhtiyacını anlat — Thunderbit’in AI Ajanı bunu çeker ve Excel, Google Sheets, Airtable veya Notion’a aktarır. Başlamak ücretsiz.
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week