Apache Tika实战
Tika 简介
Apache Tika 是一个内容分析工具包,可以检测上千种文件类型,并提取它们的元数据和文本。tika在设计上十分精巧,单一的接口使它易于使用,在搜索引擎索引,内容分析,翻译等诸多方面得到了广泛使用。
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-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的应用系统集成
tika-eval
一个命令行工具,可以批量解析文件,然后把结果保存到数据库,支持多种类型的数据库,如h2,mysql......
默认数据库为h2,使用其他类型的数据库需要在启动时将相关的依赖放到classpath下
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内容提取
void parse(InputStream stream, ContentHandler handler, Metadata metadata, ParseContext context) throws IOException, SAXException, TikaException;
接口说明
扩展机制
- 定义Mimetype(可选,如果需要处理一些特殊文件,它们的文件类型tika目前并不支持,就需要自定义Mimetype)
- 实现Parser,可以针对tika已有的文件类型编写自己的解析器,也可以创建支持新的文件类型的解析器
- 注册Parser,通过SPI机制可以轻松地将自己的解析器放进tika的解析器库里(CompositeParser)
- 在类路径下创建文件 - META-INF/services/org.apache.tika.parser.Parser
- 文件内容就是自定义的Parser的全路径类名
注意事项
配置
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的结果依赖于算法模型的训练,需要整理出合适,足够的样本,工作量比较多。
文件修复
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进行处理
提取图片
某些文件(主要是容器文件格式)内部可能含有其他内嵌文件,如压缩文件,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();
}
}