Apache Tika 是 Apache Software Foundation 的文件解析工具包:只要丟給它幾乎任何格式的檔案,它就會回傳純文字,以及一份標準化的 metadata 字典。專案 README 宣稱它支援超過一千種檔案類型,而 Tika 之所以能做到這點,是因為它把各類專用函式庫直接包進來——PDF 用 PDFBox、Office 文件用 Apache POI、HTML 用 jsoup、ODT 用 ODF reader——所以整套工具可以做成單一 fat jar,解析時不需要再另外抓任何依賴。放到資料流程裡,它就是那個最不起眼、卻最關鍵的第一站:在搜尋索引、電子蒐證審查集,或 LLM 語料庫前面,把一堆格式各異的檔案整理成統一的內容。白話一點說,它要做的其實只有兩件事:先判斷這串位元組到底是什麼,再把文字和 metadata 抽出來。
這是我很久以來裝起來最不費力的工具。單一 jar,java -jar tika-app-3.3.2.jar --text file.pdf,不用設定檔、不用模型權重、也沒有任何安裝後步驟;而且它在我主機上那個連其他 Java 工具都直接停擺的最新 JDK 上,依然乾乾脆脆地跑完了。不過我真正想驗證的不是它宣稱支援多少格式,而是更具體的問題:當輸入對你說謊時,Tika 到底會怎麼做?所以我做了一組受控測試樣本,讓每個內容區塊都帶上獨一無二的標記字串,把同一份邏輯文件輸出成九種載體格式,接著再用錯誤副檔名、缺失副檔名、完全沒有檔名、零位元組檔案,以及寫到一半的二進位檔去攻擊它。
真正有趣的行為,出現在偵測這一關。我把一個 PDF 改名成 .txt 再問 Tika 這是什麼,它回答 application/pdf。接著我把檔名整個刪掉,直接把原始位元組從 stdin 丟進去,得到的答案還是一樣。在我那組五種可由內容判別的格式裡,這個結果在全部 20 種獨立邏輯情境 中都成立:每種格式有三種檔名條件,再加上一種沒有檔名的串流條件。測試框架其實把串流案例用不同標籤跑了三次,總共得到 30 次成功執行,但那些重複不能算獨立證據。PDF 和 RTF 有可辨識的位元組特徵;DOCX 可從其容器格式辨識;HTML 與 XML 則能從標記或根節點內容判別。機制不同,結果一樣實用:在這組樣本裡,副檔名沒有蓋過內容。至於文字類別的那一組,Markdown 只要檔名不對或直接消失,就會退回成 text/plain。在這裡,它的身分完全繫於 .md。
接下來所有數字,都有兩個前提。第一,我測的是 Apache Tika 3.3.2——我在 2026 年 7 月 27 日確認時,它仍是最新穩定版;4.0.0 目前在 Maven Central 上只有 alpha 和 beta 版本。第二,這個專案在我查詢當天大約有 3.9k GitHub stars,而且採用 Apache-2.0 授權,商用上幾乎沒有額外負擔。還有一點更重要:我完全沒測 OCR。 一頁掃描檔都沒有,一個只有影像的 PDF 也沒有。因為我跑測試的機器上沒有安裝 Tesseract 和 poppler,所以所有 OCR 路徑在開始前就被擋住了。這裡沒有任何 OCR 數據,因為真的沒有 OCR 數據,就是這樣。
先把包裝紙拿掉:Tika 到底是什麼
很多人會把 Apache Tika 當成文件轉換器——丟一個 DOCX 進去,回來應該是乾淨的 Markdown,標題和表格都要原封不動。它不是這種工具;越早把這件事弄清楚,Tika 看起來就越合理。
這次測試的流程有三個相關階段:內容類型偵測器、把位元組送去對應解析器的 dispatcher,以及 CLI 的 --text 輸出處理器,它會輸出扁平文字,並同時附帶可單獨取用的 metadata。在這個輸出契約裡,不會出現 Title 物件、不會有 ListItem,也不會重建表格格線。Tika 另外還提供其他 handler 和 API,包括偏向 XHTML/SAX 的輸出;那些我沒有測。因此,下面所有關於結構的結論,都只針對 tika-app --text,不是在宣稱整個工具包完全沒有結構化事件流。
這聽起來像是一種限制,而某種層面上確實如此。但它也代表 Tika 不會把東西分類錯,這正是那些更喧鬧的同類工具往另一個方向所付出的代價。
偵測本身有明確的處理順序:先看簽章位元組,再看 XML 根節點,再看檔名 glob,最後才是你自己指定的型別(Tika 官方偵測文件 就是這麼寫的)。等型別一旦確定,dispatcher 才會把位元組交給對應的內建 parser——PDFBox、POI、jsoup、以及文字/CSV 類別用的 TextAndCSVParser。
這個「先偵測、再解析」的分工不是什麼內部八卦。它解釋了為什麼即使檔案壞到不能解析,仍然可能先被正確辨識出類型;而這也正是 Tika 一旦出現錯誤時,最實用的一個功能。
安裝方式:一個 jar、一條指令,以及一個不挑剔的 JVM
安裝其實就是下載而已。Maven Central 上的 tika-app-3.3.2.jar 大約 67 MB——一個把所有 parser 都包進去的 fat jar——然後就可以直接 java -jar tika-app-3.3.2.jar --text file.pdf。沒有設定檔、沒有模型權重、沒有安裝後處理,也不需要一路走完一串 brew install。
JDK 這件事倒是讓我有點意外。我是在 OpenJDK 26.0.1 這個最新、非 LTS 的版本上跑完整測試,而 --version、--text、--metadata 和 --detect 全都正常回傳 exit 0,沒有任何相容性抱怨。這值得特別提一下,因為我同一天在同一台主機上也測了 Apache Nutch,而它的 crawl 流程在 JDK 26 上根本跑不起來——由於新版 JDK 移除了 SecurityManager,它需要的是 21 或更低的 LTS 版本。Tika 沒有這個問題。若你一直因為這類 JVM 相容性痛點而避開相關工具,Tika 不會是那個讓你卡住的地方。
但安裝層面還是有兩個誠實的限制。首先,CLI 每次呼叫都會啟動一個全新的 JVM,所以冷啟動是真實存在的——我那組測試框架跑了 131 次呼叫,差不多花了一分鐘,絕大多數時間都在 JVM warmup。若你要處理大量檔案,應該選 library 或 server 模式,而不是在 shell 裡迴圈呼叫 jar。其次,這個「不依賴外部套件」的說法有明確邊界:PDF 文字層抽取不需要任何外部工具,但 OCR 需要 tesseract 和 poppler。 我測的文字層 PDF、DOCX、ODT、RTF、HTML、XML、TXT、Markdown、CSV,在沒裝這兩個工具的主機上都能解析;但掃描文件不會,我也沒打算假裝它會。
這點和我同一天測的另一個套件 unstructured 比起來更明顯:它的電子 PDF 路徑整個被擋住,因為匯入 PDF 模組時就會載入推理堆疊(torch 之類的東西)——還沒走到 strategy dispatch,就先因為依賴問題無法匯入,即使是所謂的「快速」策略也一樣。Tika 則是直接用一條 java -jar 把同一份 PDF 的文字層處理掉。
騙人副檔名測試:檔名說謊,Tika 不信

八種格式,各自搭配正確副檔名、故意錯誤副檔名、或完全沒有副檔名,再加上 stdin 上沒有檔名的串流。總共是 32 種獨立邏輯情境。原始測試框架還把同一串流位元組在每個檔名標籤下又各跑了一次,因此有 48 次原始執行;但那三列串流案例都應視為同一個情境,因為 stdin 根本沒有檔名可讀。
| 樣本 | 真實類型 | 改名成 | 正確副檔名 | 騙人副檔名 | 無副檔名 | 原始串流,無檔名 |
|---|---|---|---|---|---|---|
application/pdf | .txt | ✅ | ✅ | ✅ | ✅ | |
| DOCX | OOXML wordprocessingml | .jpg | ✅ | ✅ | ✅ | ✅ |
| RTF | application/rtf | .html | ✅ | ✅ | ✅ | ✅ |
| HTML | text/html | .csv | ✅ | ✅ | ✅ | ✅ |
| XML | application/xml | .txt | ✅ | ✅ | ✅ | ✅ |
| 純文字 | text/plain | .pdf | ✅ | ✅ | ✅ | ✅ |
| Markdown | text/markdown | .pdf | ✅ | ❌ text/plain | ❌ text/plain | ❌ text/plain |
| CSV | text/csv | .txt | ✅ | ❌ text/plain | ❌ text/plain | ❌ text/plain |
(串流欄位把三種副檔名條件合併了,因為沒有檔名時,glob 根本沒東西可讀。)
五種可由內容偵測的格式——PDF、DOCX、RTF、HTML、XML——在 20 個獨立情境中全部命中真實類型(若把重複的串流執行也算進去,則是 30 次原始執行全部成功)。叫做 report.txt 的 PDF 仍然是 PDF。叫做 photo.jpg 的 DOCX 仍然是 DOCX。這些結果都不需要檔名。這不代表這五種格式全都靠固定位元組簽章:PDF 和 RTF 有可辨識的檔頭,DOCX 是 ZIP 容器,HTML/XML 則可以從標記或根內容判別。在這些樣本裡,騙人的副檔名沒有贏。
再來是文字類別的那一組。Markdown 只有在 .md 副檔名存在且可讀時,才會被辨識成 text/markdown。只要你把它改名、移除副檔名,或改成串流送進去,在這次測試裡它都退化成 text/plain。CSV 在這個刻意做得很小的測試格上也一樣:只有 .csv glob 才會回傳 text/csv。如果只看獨立情境,Markdown 與 CSV 都只在四種情境中的一種被辨識成它們自己的型別;而純文字本來就是 text/plain,所以沒有什麼可以「退化」的。48 次原始執行對重現性來說很有價值,但不適合作為更大的分母。
這裡有一點特別對 Tika 有利:騙人的副檔名也沒有成功。 我把 Markdown 樣本改成 .pdf 後,結果是 text/plain,而不是 application/pdf。Tika 沒有相信謊言;它只是無法確認真相。退一步降成父類型,遠比自信地斷言錯誤類型來得好,而且 text/markdown 本來就是 text/plain 的已定義子類型,所以這樣的 fallback 是有原則的,不是亂猜。
不過 CSV 這裡有一個例外要說明。Tika 其實有一個統計式 CSV 偵測器,而在解析階段——從 X-TIKA:Parsed-By 鏈條裡出現 TextAndCSVParser 可以確認——我那個小型 2 欄 × 3 列的表格最後被辨識成 text/plain,不是 text/csv。這只是針對刻意縮小的樣本得到的一個觀察。更大一點、或帶引號的 CSV,很可能就會觸發偵測器。我不是在說 CSV 內容偵測壞掉了;我是在說,至少在這個格子裡,會產生 text/csv 的其實是副檔名。
這對實際上傳流程有什麼意義
最直接的場景就是上傳分流器。假設你接受使用者上傳,然後依型別分流:PDF 送去發票解析器、試算表送去帳務匯入器、其他全部進文字索引。如果你只相信副檔名,那麼一個名為 notes.txt 的 PDF 就會被送進錯的分支——而這還算是比較無害的案例;更惡意的版本,則是帶著友善副檔名的 polyglot 檔。
對這裡測到的二進位與標記式樣本而言,即使檔名被拿掉,Tika 仍然能依內容正確分流;當 blob store 或 HTTP body handler 把檔名丟掉時,這點非常有用。不過這個結果不涵蓋 Tika 的長尾格式、模稜兩可的檔案,或 polyglot 檔。至於測試到的文字類別樣本,行為就不同了:當流程把檔名剝掉後,Markdown 與 CSV 都變成 text/plain,因此以它們專屬 media type 為條件的規則就不再觸發。比較好的做法,是把原始檔名當作 sidecar metadata 保留下來,而不是指望內容偵測能幫你重建它。
植入的內容都活下來了,但 --text 把結構全壓平了
保真度是第二條軸線,而且它很清楚地分成兩半。我把一份標準文件(標題、兩段內文、項目符號清單、編號清單、結尾段落)輸出成 HTML、Markdown、純文字、DOCX、PDF、RTF、ODT 和 XML;另外又把一份表格文件輸出成 HTML、Markdown、文字、DOCX、CSV 和 XML。總共十四種載體輸出。每個區塊都帶有獨特 token——像是 zztitle1、zzitem3、zztblcell_beta 之類——所以「有沒有存活」和「有沒有掉失」可以直接用字串比對精準判定,不是主觀判斷。
標記字串召回率在這十四種輸出裡全部都是 1.000。 沒有一個植入的 token 消失:每個被標記的表格儲存格、清單項目和標題都還在。暖機後,每個載體各跑三次本地重複測試,回傳的 --text 輸出都完全一致。這個結果只能說明有沒有區塊存在;它無法告訴你未標記字元、順序、空白、Unicode 正規化、重複內容、連結、頁首頁尾或內嵌物件的正確性。這是一個區塊存在檢查,不是完整文件保真的證明。
但平坦文字輸出,還是丟掉了大部分原始結構。
這是那份 HTML 表格文件經過 --text 輸出的結果:
Tool Throughput
zztblcell_alpha 120
zztblcell_beta 95
這只是用 tab 串起來的幾行字。表頭沒有被標成表頭,沒有格線,除了 tab 之外看不出任何 cell 邊界,也無從知道原本曾經是 <table>。DOCX 的表格也會被壓平成同樣的樣子。
清單的情況更微妙,會依原始內容到底是什麼而分岔:
| 原始內容中的項目符號是什麼 | 載體 | --text 回傳什麼 |
|---|---|---|
一個 字面字元 —— 這些輸出裡都把 - 當成實際文字寫出來 | 純文字、Markdown、RTF、ODT、PDF | - 會原樣保留,因為 Tika 只是把字元直接傳過去 |
真正的結構 —— HTML 的 <li>、DOCX 的 List Bullet 樣式 | HTML、DOCX | 標記會整個消失,只剩項目文字本身:HTML 裡以 tab 縮排,DOCX 裡就是一行沒有裝飾的純文字 |
Tika 不會把自己沒收到的文字標記重新渲染出來。內容一樣,但輸出看起來不一樣。
Markdown 的案例把這件事講得最清楚。把一個包含 pipe table 的 .md 檔丟給 Tika,回來的 pipe 會原封不動保留,看起來好像有把結構保存下來。其實不是。Tika 只是把它當文字解析,然後把位元組交回來。它根本沒理解那個表格。
所以,這次量到的契約更窄:所有植入的標記都存活了,但 --text 沒有保留型別化元素,也沒有保留可重建的表格格線。 如果你把這說成 parser 缺陷,反而會偏離重點。平坦抽取的設計,本來就刻意避開元素分類問題;但相對地,它也無法滿足需要那些元素型別的下游消費者。如果你需要型別化區塊或重建表格,--text 只是整個堆疊的一部分,不是整個堆疊本身。Tika 其他 handler 也許能提供更多結構,但這次沒有測到它們。
這裡所有保真度數字,都要照例加上一句前提:它們來自一台機器、一個版本、一次 JDK 的受控合成樣本。它們只能證明那些被標記的區塊有出現在輸出中,並不能證明在真實、混亂的資料集上,字元能逐字逐句完全一致且正確無誤。
Metadata:經過標準化,而且罕見地不愛亂編

我把已知的作者、標題和建立日期值嵌入所有有 metadata 層的載體,然後檢查回來的是什麼。
| 載體 | author → dc:creator | title → dc:title | created → dcterms:created |
|---|---|---|---|
HTML (<meta name=author>, <title>) | ✅ | ✅ | 未嵌入 |
| DOCX(core properties) | ✅ | ✅ | ✅ 完整對上 2021-03-15T09:30:00Z |
| PDF(info dict) | ✅ | ✅ | 有出現,但那是產生器自己蓋的時間戳記——不列為得分 |
ODT(meta.xml) | ✅ | ✅ | ✅ 完整對上 2021-03-15T09:30:00Z |
| TXT / MD / CSV / RTF / XML | 沒有 metadata 層 | — | — |
作者和標題在 4 種有 metadata 的載體中全數成功回收,而且——這點很值得——它們是標準化後的。HTML 的 <meta name="author">、DOCX 的 core property、PDF 的 /Author 欄位,以及 ODT 的 dc:creator 元素,最後都會匯到同一個 dc:creator key 下。你只需要寫一個 consumer,不必寫四個。
created 的部分就比較誠實地有些波動。DOCX 和 ODT 回傳了我嵌入的 2021 時間戳記原值。PDF 也回傳了一個建立日期,但那是生成器在建構時蓋上的時間,不是我想嵌入的值——所以我把它記為「有出現」,不是「成功還原」。而那些沒有 metadata 層的格式,也就真的什麼都沒顯示,這才是正確答案。Tika 不會從本文內容自己猜出作者。
故意把它弄壞,然後看能不能用來分流
四個敵意輸入:零位元組檔案、有效 PDF 檔頭但正文被切掉的檔案、被截斷的 DOCX ZIP,以及一個含多位元組字元、沒有 BOM 也沒有編碼宣告的 UTF-8 檔。這些都是本地樣本形狀,不是 Tika 的門檻值。
底層測試框架、生成樣本、原始 JSON、jar checksum 和環境清單,在這份草稿裡都沒有連結。外部讀者因此暫時還不能獨立重現這些分母。請把這些表格視為已報告的觀察;若要對外發布,應先附上穩定的套件,之後這些數字才能作為第三方證據使用。
| 輸入 | --text / --json | 丟出的錯誤 | --detect |
|---|---|---|---|
| 0 位元組檔案 | exit 1,stdout 空白 | ZeroByteFileException: InputStream must have > 0 bytes | exit 0 → 有檔名時是 text/plain,串流則是 application/octet-stream |
| 截斷的 PDF | exit 1,stdout 空白 | TikaException: TIKA-198: Illegal IOException from PDFParser | exit 0 → application/pdf |
| 截斷的 DOCX | exit 1,stdout 空白 | POI FATAL: "XML document structures must start and end within the same entity" | exit 0 → OOXML type |
| UTF-8、無 BOM、未宣告編碼 | exit 0 | 無 | exit 0 → text/plain,charset UTF-8 |
抽取失敗會直接報錯,而且這些失敗的外觀都很像。 零位元組檔案、截斷 PDF、截斷 DOCX 都會各自丟出例外、exit 1,且 stdout 是空的。CLI 不會把失敗包裝成一個乾淨的空結果。在這些情況下它是 process-safe 的——不會卡住,也不會 segfault——但呼叫端必須檢查 exit status 和 stderr,不能只看是不是空字串。
偵測和解析是分離的。 在兩個截斷的二進位檔上,--detect 都會 exit 0,並根據完整的開頭內容回傳預期型別;接著 parser 才會在損壞的正文上失敗。所以,一個 pipeline 可以把 detection 當成獨立的分流信號,在解析失敗前或失敗後先判斷一次。detect-first 是否該成為預設,取決於部署模式:這次測試沒有比較 detect-first 與 parse-only 的成本,而兩次全新的 CLI JVM 啟動,對大量資料來說可能不是最好的交換。
字元集偵測是正常的。 那個沒有 BOM、也沒有宣告編碼的 UTF-8 檔,被解碼成 UTF-8,而且 日本語テスト 完整通過。給 metadata 字典一個小補充:我那些純 ASCII 樣本顯示的是 charset=ISO-8859-1,但在 ASCII 位元組上它和 UTF-8 看起來完全一樣。這不是漏判,只是平手。
Tika 對上 unstructured:同樣是文件類型,不同工作重點
這兩個工具是在同一個研究時段內測的,但這裡比較的是輸出契約的分類法,不是對稱式基準測試。它們評分的目標不同。
相關評測:Unstructured 評測。
| Apache Tika | unstructured | |
|---|---|---|
| 我拿來測的是什麼 | 內容保真度:有沒有漏掉任何東西? | 元素分類保真度:每個區塊有沒有被分到正確型別? |
| 結果 | 在十四種輸出裡,所有植入標記都存在 | 在另一組分類測試裡,一個純文字表格的 Table 召回率是 0.000,而且一個含動詞的標題被分成 narrative text |
| 回傳的型別化元素 | 沒有——完全沒有結構回來 | Title、NarrativeText、ListItem、Table——剛好是 Tika 不做的事 |
| OCR | 受限於我的主機,因為沒有 tesseract 而無法測 | 受限於我的主機,因為沒有 tesseract 而無法測 |
平坦、保留標記的輸出,對上帶有型別元素但分類有誤的輸出。要選哪一種,取決於下游系統到底需要什麼。如果只是搜尋索引或 LLM 的 context window,平坦文字可能就夠了;如果是以元素型別為條件,那 Tika 的 --text 路徑無法提供這份契約。
我們兩個都沒有掃描文件的數據。
優點與缺點
優點
- 在五種可由內容偵測的樣本上,20/20 種獨立情境都忽略了騙人的檔名;重複的串流執行也一致。
- 在全部 14 種載體輸出中,所有植入標記都成功存活,包括標記過的表格儲存格與清單項目。
- 三次本地重跑結果一致:在這個環境裡,每個載體回傳的文字都完全相同。
- metadata 跨格式標準化良好——不論原始格式為何,
dc:creator/dc:title/dcterms:created都能統一回來,且在 4/4 個有 metadata 的載體上成功回收。 - 對我測過的格式來說,真的幾乎沒有外部依賴:PDF 文字層、DOCX、ODT、RTF、HTML 都能直接從單一 jar 解析,不需要外部二進位工具。
- 在 OpenJDK 26 上可正常運作——沒有只能跑 LTS 的限制。
- 遇到截斷的二進位檔時,偵測仍然正確(exit 0),所以在解析失敗時,它能提供可靠的分流信號。
- Apache-2.0、成熟,而且持續維護中。
缺點
- Markdown 和 CSV 的身分完全依賴檔名副檔名;在 18 個沒有簽章的格子裡,有 10 個在檔名消失或錯誤後都退回成
text/plain。 --text不會回傳元素型別;表格格線被壓成 tab 連接的文字,結構性清單標記也會消失。- 遇到空檔和損壞檔會直接丟例外;光看抽取呼叫,這兩種情況外觀一模一樣。
- CLI 模式下,67 MB 的 jar 加上每次呼叫都要冷啟動 JVM。
- OCR 和掃描影像 PDF 在這裡完全沒測——因為沒有 tesseract 和 poppler,所以對那條路徑不做任何結論。
- 這裡的每個數字都只是單一機器、單一版本的合成真值。沒有測真實語料的準確率,也沒有測加密檔、內嵌/遞迴文件,或大規模吞吐量。
誰適合用,誰不適合
如果你的輸入是你已經拿到手的檔案,而輸出需要的是能被機器索引的文字加 metadata,那 Tika 很適合。搜尋索引、電子蒐證、檔案封存處理、把語料丟給 LLM、建立上傳流程中的內容型別驗證層——這些都很合適。它非常適合作為第一層分流與標準化步驟,放在更聰明的系統前面:先偵測測過的格式、抽出平坦文字,再把結果連同明確檢查一起往下傳,避免你不能承受的內容遺失。
如果你需要的是型別化元素、重建表格,或文件排版,最好跳過它——或者更準確地說,不要只停在 --text。如果你的文件是掃描檔,也先別用,至少要等你裝好 tesseract 並自己跑過數據,因為我這裡沒有。若是大量處理,請拿 library 或 server mode 跟 CLI 在代表性文件上做基準測試。這次的小檔案框架裡,process 啟動成本看得見,但吞吐量和資源消耗沒有被量測。
最容易踩雷的一點是:如果你的儲存層會把檔名移掉,而你又要處理 Markdown 或 CSV,不要期待 Tika 幫你把它們和純文字分辨開來。請把原始檔名保留下來。
替代方案,以及 Thunderbit 在哪裡
先把界線畫清楚,因為這裡真正該比較的是輸入來源,不是品質。Tika 是一個免費、Apache-2.0、可自架的文件解析工具。你已經放在磁碟或 bucket 裡的檔案,它能處理;它不會抓網頁、不會跑 JavaScript、不會處理 anti-bot,也不假裝自己會。
這正是像我們自己的 Thunderbit 這類受管網頁擷取服務可能進入架構的邊界:它負責抓即時網頁,而 Tika 負責解析你手上已經有的檔案。這篇文章沒有把這些服務拿來和 Tika 做基準比較,因為它們不是針對同一種輸入的替代品。
最乾淨的區分方式就是:Tika 用在你已經持有的文件,受管擷取 API 用在你需要去抓回來的網頁。很多流程兩者會一起用——網頁端負責爬取與抽取,回來的 PDF 和 DOCX 附件再交給 Tika。
如果你想看更廣泛的開源領域比較,我另外整理了 完整的開源 scraper 比較、一份 GitHub 上最實用的爬蟲專案總覽、一篇實作導向的 Crawl4AI 評測(內容涵蓋瀏覽器驅動的 Markdown 方法),以及更廣泛的 爬蟲工具整理。如果你想走免程式碼路線,也有一篇教你如何 用 AI 抓取網站 的完整教學。
結論
要不要用 Apache Tika?如果你的工作是把各式各樣的檔案轉成扁平文字和標準化 metadata,而且你會自己驗證那些對流程來說不能丟掉的欄位或標記,那答案是要。
這次跑測試最強的部分是偵測器。對五種可由內容偵測的樣本,它在 20/20 種獨立情境中都回傳了正確型別,包括沒有檔名的串流。所有植入標記在十四種輸出裡都完整存活,而且三次本地重跑都逐位元組一致。這些都是有用的證據,但仍然只是合成樣本的證據。用單一 jar、在這個 JDK 上、而且非 OCR 路徑不依賴外部二進位工具,就能完成這件事,部署起來確實很省心。
但工具要用對地方。你餵給它的每個表格,最後都會變成 tab 連接的文字。所有結構性的清單標記都會消失。只要檔名不見,Markdown 和 CSV 的身分也會跟著不見。空檔與損壞檔會丟出同樣型態的失敗,而你還得靠獨立的 detect 呼叫才能把它們分開。至於很多 Tika 使用者最在意的 OCR,我這裡沒有任何可提供的結果:我根本沒法跑,也不會硬估。
在那些界線之內,Tika 做的是一件不起眼、但異常可靠的工作。它讀的是位元組,不是盒子上的標籤。只是別叫它告訴你,那些位元組原本長什麼樣子。
免費試用 Thunderbit 進行網頁資料擷取 Get Started Free
常見問題
Apache Tika 在副檔名錯誤時,還能正確偵測檔案類型嗎?
在這裡測到的五種可由內容偵測的樣本上,答案是可以。PDF、DOCX、RTF、HTML 和 XML 在全部 20 種獨立邏輯情境中都回傳了預期的 media type(若把重複的串流執行算進去,則是 30 次原始執行全部成功),即使副檔名有誤、沒有副檔名,或是只有沒有檔名的串流也一樣。一個叫做 .txt 的 PDF,仍然會被偵測成 application/pdf。Markdown 和那個小型 CSV 樣本則依賴檔名資訊;當檔名缺失或錯誤時,它們會退回成 text/plain。
Tika 會保留表格和文件結構嗎?
在這次測到的 --text 模式中不會。表格格線會變成 tab 串接的文字,沒有 cell 或 header 的語意;而結構性的清單標記(HTML 的 <li>、DOCX 的 List Bullet 樣式)會直接消失。所有植入標記雖然都在 14 種載體輸出中存活了,但這不代表完整內容保真,而且 --text 不會回傳元素型別。如果你需要型別化元素或重建表格,請測 Tika 的其他輸出 handler,或搭配別的工具一起用。
Apache Tika 可以對掃描 PDF 做 OCR 嗎? Tika 支援透過 Tesseract 做 OCR,但我沒有測,而且這裡的結果都不是在聲稱這條路徑。 我的測試主機上沒有 Tesseract 和 poppler,所以所有 OCR 與掃描影像路徑在執行前就被擋住了。這次測試裡沒有任何 OCR 數據。如果你的用途就是 OCR,請自己安裝 tesseract 再做基準測試——把 Tika 在這部分視為尚未驗證。
Tika 遇到空檔或損壞檔時會怎樣?
它會直接報錯,而不是默默吞掉。0 位元組檔案會丟出 ZeroByteFileException;截斷的 PDF 會從 PDFParser 丟出 TikaException;截斷的 DOCX 則會丟出 POI 的 XML 錯誤。這三種情況都會 exit 1 且 stdout 為空,所以只看抽取呼叫的話,空檔和損壞檔無法分辨。不過偵測本身很穩——在這兩種截斷二進位檔上,--detect 都能 exit 0 並回傳正確型別,因此它很適合作為解析前的分流步驟。
這次的 Tika 測試沒有涵蓋什麼? 明講四件事。第一,OCR 和掃描影像(因為受限,未測)。第二,真實語料的準確率——所有結果都來自受控的合成樣本,而且有植入標記字串,測的是對已知標記的保真,不是真實雜亂文件上的準確率。第三,資源成本、吞吐量和峰值記憶體,我都沒測。第四,所謂「一千種檔案類型」的長尾部分:我只測了九種具代表性、且不需額外依賴的格式,不是完整目錄。以上全部都只是 Tika 3.3.2、OpenJDK 26.0.1、macOS arm64、單機環境下的結果。


