使用Apache Tika实现内容分析
Apache Tika可以抽取不同类型的内容和元信息的开源工具,如word、excel、pdf,甚至多媒体文件如JPEG、MP4。所有基于文本的和多媒体文件都可以使用通用接口进行解析,这使得Tika成为功能强大且用途广泛的内容分析库。
本文将介绍Apache Tika,包括解析API、如何自动监测文档内容类型,同时提供示例说明。
引用类库
为了使用Apache Tika,我们需要通过maven引入依赖:
<dependency>
<groupId>org.apache.tika</groupId>
<artifactId>tika-parsers</artifactId>
<version>1.17</version>
</dependency>
读者可以查找合适的版本。
解析API
解析API是Tika的核心功能,抽象解析操作的复杂性,仅依赖单个方法:
void parse(
InputStream stream,
ContentHandler handler,
Metadata metadata,
ParseContext context)
throws IOException, SAXException, TikaException
我们简要解释方法参数:
- stream,从需要被解析文档创建的InputStream实例
- handler,接收从输入文档解析XHTML SAX事件序列的ContentHandler对象,负责处理事件并以特定的形式导出结果。
- metadata,元数据对象,它在解析器中传递元数据属性
- context,带有上下文相关信息的ParseContext实例,用于自定义解析过程。
如果从输入流读取失败,则parse方法抛出IOException异常,从流中获取的文档不能被解析抛TikaException异常,处理器不能处理事件则抛SAXException异常。
当解析文档时,Tika尽量重用已经存在的解析库,如Apache POI或PDFBox。因此,大多数解析器实现类仅适配这些外部类库。下面,我们将了解如何使用处理程序和元数据参数来提取文档的内容和元数据。为了方便,我们能使用Tika的门面类调用解析器Api。
自动监测
Apache Tika可以根据文档本身而无需额外信息则可以自动检测文档的类型和语言。
检测文档类型
可以通过Detector接口的实现类检测文档类型,其仅有一个方法:
MediaType detect(java.io.InputStream input, Metadata metadata) throws IOException
方法带有文档流和其元数据参数,返回MediaType对象用于描述文档最佳可能相关类型。
元数据并不是探测器所依赖的唯一信息来源,还可以使用对应在文件开头的特殊模式的魔术字节,或者将检测过程委托给更合适的检测器。
事实上,探测器算法依赖实现。举例说明,默认探测器首先使用魔术字节,然后是元数据属性。如果还不能获得文档类型,则使用service loader去发现所有有效的探测器,然后依次尝试。
语言检测
除了文档类型之外,Tika还可以在没有元数据信息的情况下识别它的语言。
Tika早期版本,使用LanguageIdentifier 实例检测文档语言。然而,LanguageIdentifier对于web服务的支持已经被弃用。
语言检测服务现在通过抽象类LanguageDetector的子类提供。使用web服务,您还可以访问完全成熟的在线翻译服务,如谷歌翻译或Microsoft Translator。为了简洁起见,我们不详细讨论这些服务。
Tika实战举例
本节通过示例描述Tika特性,示例方法封装在类里:
public class TikaAnalysis {
// illustration methods
}
检测文档类型
下面代码可以检测InputStream对应文档类型:
public static String detectDocTypeUsingDetector(InputStream stream)
throws IOException {
Detector detector = new DefaultDetector();
Metadata metadata = new Metadata();
MediaType mediaType = detector.detect(stream, metadata);
return mediaType.toString();
}
假设我们有一个pdf文件在类路径下,扩展名我们故意修改为txt去误导tika,但实际真实类型仍然能被检测:
@Test
public void whenUsingDetector_thenDocumentTypeIsReturned() throws IOException {
InputStream stream = this.getClass().getClassLoader()
.getResourceAsStream("tika.txt");
String mediaType = TikaAnalysis.detectDocTypeUsingDetector(stream);
assertEquals("application/pdf", mediaType);
stream.close();
}
很明显,错误的文件扩展名没能让tika犯错,因为pdf的魔术字节在pdf文件的开头。
为了简洁,我们可以使用Tika的门面类重写之前的方法:
public static String detectDocTypeUsingFacade(InputStream stream)
throws IOException {
Tika tika = new Tika();
String mediaType = tika.detect(stream);
return mediaType;
}
抽取文档内容
下面我们使用Parser API抽取文件内容,然后返回结果字符串:
public static String extractContentUsingParser(InputStream stream)
throws IOException, TikaException, SAXException {
Parser parser = new AutoDetectParser();
ContentHandler handler = new BodyContentHandler();
Metadata metadata = new Metadata();
ParseContext context = new ParseContext();
parser.parse(stream, handler, metadata, context);
return handler.toString();
}
类路径下有word文档,内容如下:
Apache Tika - a content analysis toolkit
The Apache Tika™ toolkit detects and extracts metadata and text ...
测试内容代码:
@Test
public void whenUsingParser_thenContentIsReturned()
throws IOException, TikaException, SAXException {
InputStream stream = this.getClass().getClassLoader()
.getResourceAsStream("tika.docx");
String content = TikaAnalysis.extractContentUsingParser(stream);
assertThat(content,
containsString("Apache Tika - a content analysis toolkit"));
assertThat(content,
containsString("detects and extracts metadata and text"));
stream.close();
}
当然,使用Tika类会更方便:
public static String extractContentUsingFacade(InputStream stream)
throws IOException, TikaException {
Tika tika = new Tika();
String content = tika.parseToString(stream);
return content;
}
抽取元数据
除了文档内容,Parser API也能抽取元数据:
public static Metadata extractMetadatatUsingParser(InputStream stream)
throws IOException, SAXException, TikaException {
Parser parser = new AutoDetectParser();
ContentHandler handler = new BodyContentHandler();
Metadata metadata = new Metadata();
ParseContext context = new ParseContext();
parser.parse(stream, handler, metadata, context);
return metadata;
}
在类路径下放一个tika.xlsx文件进行单元测试:
@Test
public void whenUsingParser_thenMetadataIsReturned()
throws IOException, TikaException, SAXException {
InputStream stream = this.getClass().getClassLoader()
.getResourceAsStream("tika.xlsx");
Metadata metadata = TikaAnalysis.extractMetadatatUsingParser(stream);
assertEquals("org.apache.tika.parser.DefaultParser",
metadata.get("X-Parsed-By"));
assertEquals("Microsoft Office User", metadata.get("creator"));
stream.close();
}
当然,另一版本可以通过Tika类实现:
public static Metadata extractMetadatatUsingFacade(InputStream stream)
throws IOException, TikaException {
Tika tika = new Tika();
Metadata metadata = new Metadata();
tika.parse(stream, metadata);
return metadata;
}
总结
文本介绍了内容分析工具Apache Tike。使用其Parser和Detechtor API,可以自动检测文档类型,也可以抽取其内容和元数据。对于特定场景,我们可以自定义Parser和Detechtor类实现更多解析过程控制。