Apache Tika實戰
Tika 簡介
Apache Tika 是一個內容分析工具包,可以檢測上千種檔案型別,並提取它們的後設資料和文字。tika在設計上十分精巧,單一的介面使它易於使用,在搜尋引擎索引,內容分析,翻譯等諸多方面得到了廣泛使用。
Apache Tika曾經是Apache Lucene的一個子專案,現已成為Apache頂級專案。
Tika的特點
- 支援上千種不同的檔案型別
- 提供了多種實用工具,如tika-app, tika-server等
- 除了Java,還提供了其他程式語言的呼叫,如Julia,Python
- 擴充套件性很好,支援自定義檔案型別和解析器
Tika的組成
tika的核心是一個類庫,提供了檔案型別檢測,內容語言檢測等功能,並有一個完整的解析器框架,通過這個框架整合了許多Java平臺上流行的檔案分析工具,如針對壓縮格式,使用了commons-compress,針對微軟Office文件,使用了Apache POI,針對Adobe PDF格式,採用了Apache PDFbox
tika-core && tika-parsers
tika-core是tika的核心,提供了檔案型別檢測,語言檢測,以及解析器框架。
tika-core並不包含具體的解析器,而是提供了一個api,實際的解析器實現放在tika-parsers中。
tika-parsers具有非常的傳遞依賴,使用時應該注意和專案已有依賴的衝突問題
tika-app
tika-app包含了tika核心類庫和它的相關依賴,提供了命令列工具和圖形使用者介面,可以在指令碼中使用,並支援管道。
tika-server
一個restful服務,方便和現有應用系統整合
$ curl -X PUT --data-binary @GeoSPARQL.pdf http://localhost:9998/tika --header "Content-type: application/pdf"
$ curl -T price.xls http://localhost:9998/tika --header "Accept: text/html"
tika-bundle
一個OSGi bundle,方便和基於OSGi的應用系統整合
OSGi: 開放服務閘道器協議,支援模組的動態載入,熱拔插,可以在不停機的情況下,讓應用程式載入新的模組,並提供新的服務
tika-eval
一個命令列工具,可以批量解析檔案,然後把結果儲存到資料庫,支援多種型別的資料庫,如h2,mysql......
預設資料庫為h2,使用其他型別的資料庫需要在啟動時將相關的依賴放到classpath下
感覺是為Lucene準備的,提取檔案內容後,儲存到資料庫,然後再由索引器進行索引,最後對外提供搜尋服務
tika設計&實現
tika的核心功能是檔案內容分析,這裡分析主要有兩個含義,一是提取檔案的後設資料(Metadata),包括檔案型別,版本,作者,編輯工具,壓縮演算法等;二是解析檔案得到文字內容(Text),這裡的文字是指在相應的閱覽軟體中開啟檔案時看到的內容。
為了實現上述目標,tika設計了一個擴充套件性極強的框架,主要包括檔案型別檢測和內容解析兩個部分。首先判斷檔案型別(Detector),再根據檔案型別選用適當的解析器(Parser),解析結果儲存在Metadata和ContentHandler中,我們可以通過自定義ContentHandler來得到想要的資訊。
檔案型別檢測
檔案型別檢測是處理檔案的第一個步驟,在大部分情況下,我們可以根據檔名簡單判斷出檔案的型別,這樣處理的效率很高,但是結果並不精準(因為檔名可以輕鬆偽造),因此Tika設計了一個檢測器介面,並採用了幾種更加完備的策略來檢測檔案型別。
//org.apache.tika.detect.Detector
MediaType detect(InputStream stream, Metadata metadata) throws IOException;
檔案型別檢測機制
- 檔名檢測 - 簡單地根據檔案字尾名判斷檔案型別。
- 魔術字檢測 - 有些檔案格式會將檔案最開始的幾個位元組設定會特定的模式,通過這些特殊的位元組模式,可以判斷檔案型別。
- 容器格式檢測 - 有些檔案格式是一種容器格式,這一類檔案無法通過魔術字判斷出檔案型別,需要對容器內的資料做更多的分析,如微軟Office文件(.docx, .xlsx, .pptx)這些文件實際上都是zip壓縮檔案,魔術字是一樣的。
容器格式檢測耗時比較長,最壞的情況下需要讀取整個檔案
檔案型別的檢測順序
容器格式檢測(OLE2ContainerDetector, ZipContainerDetector......)=>
魔術字檢測(MimeTypes)=>
檔名檢測(MimeTypes)
MimeTypes底層實際使用了NameDetector和MagicDetector
解析器
Parser是tika的核心概念,它隱藏了不同檔案格式和解析庫的複雜性,為客戶端程式提供了一個簡單而強大的機制,用來從各種各樣的文件中提取後設資料和結構化文字內容。tika提供了很多解析器,用來對各種各種的檔案型別進行處理,如針對微軟Office文件的OfficeParser,針對Adobe PDF文件的PDFParser,針對壓縮檔案的CompressorParser,針對歸檔檔案的PackageParser,還有一些特殊的Parser,如TesseractOCRParser,用來對圖片進行OCR內容提取
如果伺服器安裝了tesseract,那麼TesseractOCRParser就會被啟用,在實時分析系統中,TesseractOCRParser的效能是不可接受的,建議手動禁用掉
void parse(InputStream stream, ContentHandler handler, Metadata metadata, ParseContext context) throws IOException, SAXException, TikaException;
介面說明
引數 | 說明 |
---|---|
InputStream | 待解析的文件,以位元組流形式傳入,可以避免tika佔用太多記憶體 |
ContentHandler | 內容處理器,用來收集結果,Tika會將解析結果包裝成XHTML SAX event進行分發,通過ContentHandler處理這些event就可以得到文字內容和其他有用的資訊 |
Metadata | 後設資料,既是輸入也是輸出,可以將檔名或者可能的檔案型別傳入,tika解析時可以根據這些資訊判斷檔案型別,再呼叫相應的解析器進行處理;另外,tika也會將一些額外的資訊儲存到Metadata中,如檔案修改日期,作者,編輯工具等 |
ParseContext | 解析上下文,用來控制解析過程,比如是否提取Office文件裡面的巨集等 |
擴充套件機制
- 定義Mimetype(可選,如果需要處理一些特殊檔案,它們的檔案型別tika目前並不支援,就需要自定義Mimetype)
- 實現Parser,可以針對tika已有的檔案型別編寫自己的解析器,也可以建立支援新的檔案型別的解析器
- 註冊Parser,通過SPI機制可以輕鬆地將自己的解析器放進tika的解析器庫裡(CompositeParser)
- 在類路徑下建立檔案 - META-INF/services/org.apache.tika.parser.Parser
- 檔案內容就是自定義的Parser的全路徑類名
其他tika元件(如型別檢測器,翻譯器,語言檢測器等)也使用該機制進行擴充套件
注意事項
配置
tika在啟動時可以載入一個配置檔案,通過這個檔案可以對tika-core的各個元件進行配置,可以配置Parser,Detector,Mimetype, ServiceLoader......
<?xml version="1.0" encoding="UTF-8"?>
<properties>
<detector class="org.apache.tika.detect.DefaultDetector">
<detector-exclude class="org.apache.tika.parser.pkg.ZipContainerDetector"/>
<detector-exclude class="org.apache.tika.parser.microsoft.POIFSContainerDetector"/>
</detector>
<parsers>
<!-- Default Parser for most things, except for 2 mime types, and never use the Executable Parser -->
<parser class="org.apache.tika.parser.DefaultParser">
<mime-exclude>image/jpeg</mime-exclude>
<mime-exclude>application/pdf</mime-exclude>
<parser-exclude class="org.apache.tika.parser.executable.ExecutableParser"/>
</parser>
<!-- Use a different parser for PDF -->
<parser class="org.apache.tika.parser.EmptyParser">
<mime>application/pdf</mime>
</parser>
</parsers>
</properties>
安全問題
tika設計了一個擴充套件性很好的解析器框架,但是具體的解析任務交給了外部的各種開源工具,因此也帶來了很多安全問題,在實際使用中推薦使用最新版本的類庫。
!!! 另外,tika有執行緒死鎖的問題,可能導致伺服器CPU資源耗盡,建議在容器(如docker)裡執行,或者使用tika-server
影像,音訊,視訊
tika可以從影像,音訊,視訊檔案中提取後設資料,但是幾乎無法提取出任何有價值的文字內容,在大多數場景下建議禁用這些型別的Parser
針對影像,可以使用TesseractOCRParser進行OCR操作,這需要伺服器安裝了tesseract,OCR的效率很低,普通檔案的解析一般在幾十毫秒左右,OCR的耗時約為幾秒鐘;而且OCR的結果依賴於演算法模型的訓練,需要整理出合適,足夠的樣本,工作量比較多。
解析時間跟檔案大小和伺服器效能有關係,資料來自對8M以下網際網路檔案的解析
檔案修復
tika可以在解析時對檔案進行一定程度的修復
比如,ZipSalvager可以對基於ZipContainer格式的檔案進行修復
為了將解析耗時控制在一定範圍內,不得不對大檔案進行截斷
提取其他資訊
tika的解析邏輯預設只會保留後設資料(Metadata)和文字(Text),如果對其他資訊感興趣,就需要對ContentHandler進行定製
tika提供了很多有用的ContentHandler,
比如ToXMLContentHandler將以XML形式輸出檔案的文字內容,
WriteLimitContentHandler可以在解析得到一定字元數的結果後,中斷解析過程(丟擲異常),
LinkContentHandler可以收集檔案內容中的超連結,
PhoneContentHandler可以收集檔案內容中的電話號碼。
提取壓縮檔案裡的檔名
預設配置下,tika解析壓縮檔案(.gz, .bzip2等)和歸檔檔案(.zip, .tar, .7z等)時,檔名會和檔案內容雜糅在一起,如果需要區分開,可以自定義一個ContentHandler對class="embedded"
的XHTML SAX event進行處理
Tika對壓縮檔案內檔名的提取實現不完整,遇到特殊情況建議手動處理,重寫Parser
提取圖片
某些檔案(主要是容器檔案格式)內部可能含有其他內嵌檔案,如壓縮檔案,Word文件,PDF文件等,tika可以遞迴處理壓縮檔案內部的子檔案,但是除此之外沒有提供別的處理方法。
如果想要提取微軟Office文件或者PDF文件內的圖片,建議在tika的解析器框架下自己實現Parser,將圖片寫入的邏輯加上;或者直接使用具體的解析庫額外處理(比如使用Apache POI可以很方便的提取微軟Office文件裡的圖片)
tika的侷限性
tika支援上千種檔案型別,並且提供了統一的介面,非常容易上手;但是有些檔案格式非常複雜,可能會出現支援不完善的情況,如對壓縮檔案的解析依賴commons-compress,但是commons-compress對壓縮檔案的支援就不完整,所以tika在處理某些檔案時無法得到有用資訊
tika的效能
tika的解析器本質上是一個介面卡,底層使用了很多第三方開源工具來實現具體的內容解析,因此tika的解析效率也跟這些工具有關
對某個具體檔案來說,解析耗時主要跟檔案大小,檔案格式的複雜程度,壓縮演算法,伺服器效能等關係較大
實時系統中最好限制一下檔案大小,推薦在離線環境中使用
簡單使用
tika是一個工具集,包括類庫,cli,gui,rest服務等,如何使用需要根據具體場景進行選擇。以下給出了tika作為類庫使用時的一些demo,更多的例子可以參考http://tika.apache.org/1.24.1/examples.html
- 引入依賴
pom.xml
<dependency>
<groupId>org.apache.tika</groupId>
<artifactId>tika-parsers</artifactId>
<version>1.24.1</version>
</dependency>
//Parsing using the Tika Facade
public String parseToStringExample() throws IOException, SAXException, TikaException {
Tika tika = new Tika();
try (InputStream stream = ParsingExample.class.getResourceAsStream("test.doc")) {
return tika.parseToString(stream);
}
}
//Parsing using the Auto-Detect Parser
public String parseExample() throws IOException, SAXException, TikaException {
AutoDetectParser parser = new AutoDetectParser();
BodyContentHandler handler = new BodyContentHandler();
Metadata metadata = new Metadata();
try (InputStream stream = ParsingExample.class.getResourceAsStream("test.doc")) {
parser.parse(stream, handler, metadata);
return handler.toString();
}
}
//Picking different output formats
public String parseToPlainText() throws IOException, SAXException, TikaException {
BodyContentHandler handler = new BodyContentHandler();
AutoDetectParser parser = new AutoDetectParser();
Metadata metadata = new Metadata();
try (InputStream stream = ContentHandlerExample.class.getResourceAsStream("test.doc")) {
parser.parse(stream, handler, metadata);
return handler.toString();
}
}
參考
- 白寧超 - Tika常見格式檔案抽取內容並做預處理
- Tika官方文件