在一份最長段落只有 151 個字元的文件上,jusText 用預設設定回傳 0 個字元。不是只抓到一點點,而是整頁直接空白。只要把其中一個閾值往下調,讓剛好有一個段落跨過門檻,同一份文件就會輸出 832 個字元。再繼續降低,結果還是 832。
在這個測試樣本裡,整體行為比較像是懸崖,不像斜坡。預設值到底站對邊還是站錯邊,完全取決於你的語料裡段落長度和模板文字的分布。
什麼是 jusText,為什麼詞彙密度這麼重要
和這組測試中的其他抽取器相比,jusText 對 語言專屬停用詞密度 的依賴特別高。一個功能詞比例比較高的區塊——像是 the、and、of、was——更有可能是正文。這個詞彙訊號不是整個分類器的全部:段落長度、連結密度、從 HTML 推得的區塊邊界、標題距離、相鄰類別,以及一個帶上下文感知的第二輪判定,都會一起影響結果。
官方參考:jusText 官方倉庫。

因為分類器需要語言停用詞清單,jusText 內建了 100 種。在這次比較裡,這種明確的語言詞彙層是它最鮮明的差異化特徵,不過這次並沒有測多語言品質。
測試版本:3.0.2,BSD 2-Clause,822 個 GitHub stars。Python 3.14.2。
這道斷崖,有多明顯

jusText 的分類器分兩輪運作。第一輪不看上下文,會把每個段落標成 good、bad、short 或 neargood。第二輪會把 neargood 提升成 good——但前提是它旁邊已經有一個 good 區塊。而一個段落只有在超過 length_high 時,才會單獨拿到 good;預設值是 200 個字元。
在一份沒有任何段落超過 200 字元的文件裡,就不會有種子段落可供提升;所有 neargood 區塊最後都會被當成模板文字。整頁結果自然就是空的。
我在一份最長段落只有 151 字元的樣本上掃過這個閾值:
length_high | 被分類為 good 的段落數 | 回傳字元數 |
|---|---|---|
| 200(預設) | 0 | 0 |
| 150 | 8 | 832 |
| 120 | 8 | 832 |
| 100 | 8 | 832 |
| 80 | 8 | 832 |
justext-length-threshold.json。
在這個閾值測試裡,只要有一個段落跨過門檻,八個目標段落就會全部被輸出;再繼續放寬也不會多出任何內容。這條軌跡和上下文輪次在既有 good 區塊周圍推進合格 neargood 鄰居的機制一致;它不代表在任意頁面上都會無條件提升所有鄰居。
在把 length_high 當成開關之前,我先把 length_low 掃過四個值,再和 max_link_density 的兩個值交叉測試:8 種組合,全都回傳 0。改這兩個設定,沒辦法在這份樣本上救回輸出。
整個 22 個樣本的集合也呈現同樣模式:length_high=200 時,22 個裡只有 2 個 有輸出;降到 150 時是 9 個;降到 120 時是 15 個。
這裡不是在說 jusText 抽取很差——在一個真實的自然語言頁面上、以預設值執行,它回傳了 1,190 個字元 的乾淨文章內容,因為真實新聞段落第一次就能超過 200 字元。它也不是在說預設值一定錯了;它只是表示預設值假設段落夠長,而你在跑之前應該先確認自己的語料有沒有符合這個前提。
這組測試的另一面:預設值下的漏抓更高
在這組標記樣本中、以預設值執行時,jusText 的模板文字漏抓率是最高的。
| Library | Article recall (all 22) | Boilerplate leak | Content-token precision | Contaminating tokens |
|---|---|---|---|---|
| Readability | 1.0000 | 0.2353 | 0.9109 | 35 |
| trafilatura | 0.9865 | 0.0588 | 0.9411 | 4 |
| newspaper4k | 0.9865 | 0.0000 | 0.9452 | 0 |
| resiliparse | 0.9054 | 0.0588 | 0.9381 | 7 |
| jusText | 0.8378 | 0.4706 | 0.8760 | 74 |
| goose3 | 0.8243 | 0.0000 | 1.0000 | 0 |
sixway-scores.json。同一組樣本、同一個評分器,每個單位都用獨特的標記值標註,因此可精準以子字串方式回收。

47% 的模板文字漏抓率、74 個污染 token——漏抓率是 Readability 的兩倍,污染量也超過兩倍。這是這組標記合成樣本、以預設值得到的診斷結果,不代表穩定的全產品排名。
觀察到的漏抓現象,和前面那道斷崖用的是同一套上下文判定。當周邊區塊類別與距離規則允許時,合格的 neargood 區塊就可能被提升;因此,一段看起來像正文的宣傳文案或留言,如果緊貼文章正文,就有可能跨過邊界。這個樣本結果展示的是哪些標記區塊發生了漏抓,而簡化後的機制還是取決於 jusText 的實際上下文規則。
它的 recall 是 0.8378,排在倒數第三,而損失幾乎都出在門檻:一篇 129 字元的短文章完全沒抓到、十個短段落的頁面也沒抓到、接近空白的文件同樣沒抓到。
語言停用詞清單盤點
另外 99 種停用詞清單。
justext.get_stoplists() 會回傳 100 種語言。這個分類器從設計上就是以語言參數化,而不是只把英文規則翻一翻而已;切換語言只要改一個參數:
import justext
paragraphs = justext.justext(html, justext.get_stoplist("Czech"))
text = "\n".join(p.text for p in paragraphs if not p.is_boilerplate)
trafilatura 和 goose3 也都有跟語言相關的行為,不過這次評測沒有對任何抽取器做非英文真值評分。jusText 內建的 100 套停用詞清單,讓它成為多語言評估的明顯候選;但光看清單數量,不能證明它在那些語言上的抽取品質,也不能證明競品在那些語言上的支援比較差。
這個 API 只有兩個函式和 9 個可調常數:length_low 70、length_high 200、stopwords_low 0.30、stopwords_high 0.32、max_link_density 0.20、max_heading_distance 200,再加上編碼處理。簡潔、好讀,而且都直接寫在函式簽名裡。
安裝與速度
pip install justext 會拉下 3 個套件——在這次比較中是最少的——以及 22.4 MiB,而且不到兩秒就裝好。
官方參考:PyPI 上的 jusText。
| 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。每個函式庫都在各自空白的 virtualenv 裡測試。
3 個套件和 22.4 MiB 在這次比較中算是很輕量的依賴,而 0.56 ms 的中位數抽取速度,也和這個測試框架裡 trafilatura 的 0.51 ms 很接近。環境量測沒有依檔案類型拆分套件大小,所以無法判斷有多少磁碟占用來自停用詞清單。
維護狀況,要怎麼看才準
這裡採用的「日期偏舊」維護指標,是倉庫最後一次 push 和 PyPI 最後一次 release,兩者都是 2025-02-25。那距離測試時點已經十七個月。總共 8 個版本、91 個 forks、9 個開放 issues,且沒有封存。
PyPI 的 classifiers 最多只標到 Python 3.9。但我在 3.14.2 上執行時,它能正常安裝、0.777 秒完成 import,並在 22 個樣本中的 19 個上完成抽取,沒有任何例外。
所以,metadata 比實際情況落後了五個 Python 版本,而實際情況是它能跑。真正有用的區別在這裡:安靜的倉庫反映的是支援度,不一定代表功能失效。對一個演算法本身就是 2011 年公開方法、再加上一組詞表的函式庫來說,「已經完成」其實很合理——能改的本來就不多,而停用詞清單也不像反爬 workaround 那樣容易失效。
不過,倉庫安靜也代表:如果你真的撞到 bug,很可能要自己修,或者自己 fork。這一點要跟那 9 個開放 issues 一起看;這不是一個問題爆炸、卻完全沒人在處理的典型。
記憶體,以及壞掉的 HTML 會怎麼影響它
這裡分開量兩個營運面問題。
更完整的壓力測試背景,可參考 十個函式庫的記憶體與錯誤 HTML 比較。
峰值常駐記憶體 透過 /usr/bin/time -l 取得,每一格都用一個全新程序執行——import 基線代表函式庫載入後閒置時的成本,峰值則包含文件本身。
| 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。Python 和 Node 的基線彼此不能直接比較,因為兩者都包含了解譯器。
jusText 的基線是 30.3 MiB,而在這個 10 MB 樣本上的峰值是 431.2 MiB,大約是 import 基線的 14.2 倍,也相當於輸入大小的 43.1 倍 RSS。單一樣本和單一程序還不足以推導一般性的縮放曲線;Python 和 Node 的基線也仍然不能直接互比。
壞掉的 HTML。 十二份文件各自只壞一個地方——未閉合標籤、錯誤巢狀的 inline 元素、帶空格卻未加引號的屬性、亂入的結尾標籤、根本沒有 <html>、重複屬性、文件在標籤中途被截斷、錯誤的 entities、未閉合的 <script>、說謊的 charset 宣告、包含標記的註解,以及 600 層巢狀——另外再加上 兩份尺寸相符的正常對照,因為「它什麼都沒回傳」只有在同尺寸的乾淨文件也不回傳時,才真的能說明它對錯誤 HTML 很敏感。
justext 在 14 份中的 0 份上拋出例外,並在 13 份上回傳空結果,在這些錯誤樣本中,33 個已評分的 sentinel 完全沒有被恢復出來(malformed-results.json)。那份正常的短對照也回傳 0 字元;正常的長對照則是唯一有內容的結果,輸出 1,333 個字元。十二份錯誤樣本全都保持空白,而未閉合 <script> 的案例不納入 sentinel 存活評分。因此,這批結果可以說明 parser 容忍度——沒有例外——但不能把空白歸因於 HTML 錯誤,而不是前面已經量到的尺寸閾值。
優點與缺點
優點。 100 種語言停用詞清單,而且分類器確實是圍繞這些清單設計,不只是把英文規則翻過去。這次比較裡套件數量最少,只有 3 個。中位數抽取時間 0.56 ms。有 9 個寫得很清楚、可以直接調的常數。BSD 2-Clause。即使 classifiers 只寫到 3.9,也能在 Python 3.14 上順利運作。
缺點。 在這組合成測試的預設值下,模板文字漏抓率最高:47%,還有 74 個污染 token。短段落樣本在沒有任何段落跨過 length_high 時會直接回傳空字串。這組資料的 recall 是 0.8378。最後一次記錄到的倉庫 push 和 release 都是在測試前十七個月,所以維護責任要一起納入考量。
誰適合用,誰不適合
如果你的語料是多語言,值得評估 jusText,因為它的語言專屬停用詞表面很明確,也很廣。這次只盤點了 100 種停用詞清單,沒有衡量多語言品質。若你的部署很在意只依賴 3 個套件,而且希望有明確可控的閾值,這也是它的候選場景。
如果你是要把內容逐 token 丟給模型,那就不建議用它,因為 74 個污染 token 相較於 goose3 和 newspaper4k 的 0,會是直接成本。若頁面多是短段落——例如產品文案、列表、變更紀錄、FAQ 條目——除非你有意調整 length_high,不然也不建議用。若你所在的合規或採購環境需要一個仍在積極維護的依賴,也應該跳過;就算程式能跑,這還是現實上的限制。
**如果你真的要用,**請拿帶標記的驗證樣本去調 length_high,不要照抄預設值,也不要直接套分位數規則。把可能的閾值都掃一次,分別量 article recall 和 boilerplate precision;把門檻調低雖然可能救回短正文,但也可能把不該進來的鄰近區塊一起放進來。
什麼情況下適合改用代管 API
jusText 只處理你已經拿到的 HTML,這點和這次比較裡所有工具都一樣。它們都不會幫你抓頁面、不會渲染 JavaScript,也不會處理反爬層。
若要看六個抽取器在相同樣本上的比較,請參考 六個函式庫的抽取比較。
像我們自己的 Thunderbit 這類代管的抓取/渲染/抽取服務,負責的是另一個層級的工作。這次沒有把 Thunderbit 納入基準測試。兩者的差別在於:一邊是對已提供的 HTML 做正文分類,另一邊是負責取得並處理網址;這篇文章並沒有提供可直接對照的同指標品質比較。
老實說,jusText 的多語言停用詞清單確實是一項真本事,而且是免費的。如果你的問題是要抓取多語言頁面,而不是分類那些頁面的正文,那你需要買的是另一種東西。
若想看更完整的市場版圖,可參考我們的 web scraping API 總覽 了解代管方案,或看 開源爬蟲主題頁 了解自架方案。如果輸出要送進模型,用 Python 把 HTML 轉成 Markdown 往往就是資訊流失最多的地方。
是否該使用 jusText?
如果你的語料是多語言,或段落長度分布能受惠於明確的閾值調整,它可以列入候選。正式上線前,先驗證這兩點。
內建的 100 套停用詞清單是它真正的設計特色,但多語言準確度並沒有在這裡測試。這道閾值斷崖是可調的;低一些是否可接受,取決於在標記樣本上測得的 recall 和 boilerplate precision。
在這組英文合成樣本、以預設值執行時,newspaper4k 沒有漏掉任何標記模板文字,並回收了 0.9865 的文章單位。這讓它成為這個工作負載裡的比較候選,但不是適用所有場景的通用替代建議。
試用 Thunderbit 進行網頁資料擷取 Get Started Free
常見問題
為什麼 jusText 會回傳空字串,而不是部分抽取結果?
它的分類器分兩輪運作。段落只有在超過 length_high(預設 200 字元)時,才會單獨拿到 good 類別;第二輪才會把鄰近的 neargood 區塊往上提。如果沒有任何段落跨過門檻,就不會有種子,所有候選都會退回成模板文字,所以結果會是空的。這是設計上的全有或全無,不是只找到一部分答案。
jusText 已經沒人維護了嗎? 最後一次倉庫 push 和最後一次 PyPI release 都是在 2025-02-25——距離測試時點十七個月——而且 PyPI classifiers 最多只寫到 Python 3.9。不過它在 Python 3.14.2 上安裝和執行都沒有例外,而且 9 個開放 issues 也不算太多。日期應該解讀成維護風險,而不是功能壞掉的證明;實際風險比較像是你可能得自己修自己的 bug。
為什麼它比 Readability 更容易漏抓模板文字? 在這組樣本中,預設上下文規則讓看起來像正文的鄰近區塊跨過了輸出邊界。是否被提升,取決於區塊類別、距離與上下文,而不是每個鄰居都會自動通過。量到的結果是在預設值下有 47% 的模板文字漏抓率,以及 74 個污染 token。
要怎麼把它用在其他語言?
justext.justext(html, justext.get_stoplist("German"))。get_stoplists() 會回傳全部 100 種可用清單。停用詞清單是這個分類器的語言詞彙輸入,但它同時也會用到段落長度、連結密度、從 HTML 推得的分段、標題距離和鄰近區塊上下文。這只是在改語言輸入;不能只靠這一步就證明德文抽取品質。
這裡沒有測試什麼?
沒有測真實世界頁面——這些都是有標記單位的受控樣本。也沒有測多語言能力;那本來就是這個函式庫的主打特色,雖然我們盤點了 100 套停用詞清單,但沒有對非英文文本評分。也沒有測編碼邊界條件,儘管 jusText 提供了 encoding、default_encoding 和 enc_errors 參數。而 max_heading_distance 以及兩個停用詞比例閾值,全都在整個測試中維持預設值。


