Apache TikaのPDFファイルテキスト抽出で遊んでみる
今更ながらに「Apache Tika」というものの存在を知る‥PDFとかその他諸々のファイルのメタデータやテキストを抽出してくれる‥|Apache Tika https://t.co/CsCBY74ekK
— HHeLiBeX (@hhelibex) 2017年2月27日
ということで、サイトのParser APIを追いかけてコードを組み立ててみたメモ。
環境は、CentOS 7(VM)上のOpenJDK 1.8.0_111。
PDFファイルからのテキスト抽出
以下のようなPDFファイルを使う。
PDFファイルからのテキスト抽出にはorg.apache.tika.parser.pdf.PDFParser
クラスを使う。
最低限のコードは以下のような感じ。
import java.io.*; import java.util.*; import org.apache.tika.exception.TikaException; import org.apache.tika.metadata.Metadata; import org.apache.tika.parser.ParseContext; import org.apache.tika.parser.Parser; import org.apache.tika.parser.pdf.PDFParser; import org.apache.tika.sax.BodyContentHandler; import org.xml.sax.ContentHandler; import org.xml.sax.SAXException; public class ParserSample { public static void main(String[] args) { Parser parser = new PDFParser(); for (String arg : args) { ContentHandler handler = new BodyContentHandler(); try (InputStream in = new FileInputStream(arg)) { Metadata metadata = new Metadata(); ParseContext context = new ParseContext(); try { parser.parse(in, handler, metadata, context); } catch (TikaException | IOException | SAXException e) { e.printStackTrace(); } List<String> list = new ArrayList<>(Arrays.asList(handler.toString().trim().split("\\s"))); List<String> names = new ArrayList<>(Arrays.asList(metadata.names())); Collections.sort(names); for (String name : names) { System.out.printf("%-48s = %s%n", name, metadata.get(name)); } System.out.println(); System.out.println(list); } catch (IOException e) { e.printStackTrace(); } } } }
これを実行すると、出力として以下のようなものが得られる。
Author = hhelibex Content-Type = application/pdf Creation-Date = 2017-02-27T11:04:23Z Last-Modified = 2017-02-27T11:04:23Z Last-Save-Date = 2017-02-27T11:04:23Z access_permission:assemble_document = true access_permission:can_modify = true access_permission:can_print = true access_permission:can_print_degraded = true access_permission:extract_content = true access_permission:extract_for_accessibility = true access_permission:fill_in_form = true access_permission:modify_annotations = true created = Mon Feb 27 20:04:23 JST 2017 creator = hhelibex date = 2017-02-27T11:04:23Z dc:creator = hhelibex dc:format = application/pdf; version=1.4 dc:title = Apache Tikaのテスト用 dcterms:created = 2017-02-27T11:04:23Z dcterms:modified = 2017-02-27T11:04:23Z meta:author = hhelibex meta:creation-date = 2017-02-27T11:04:23Z meta:save-date = 2017-02-27T11:04:23Z modified = 2017-02-27T11:04:23Z pdf:PDFVersion = 1.4 pdf:docinfo:created = 2017-02-27T11:04:23Z pdf:docinfo:creator = hhelibex pdf:docinfo:creator_tool = MicrosoftR Office ExcelR 2007 pdf:docinfo:modified = 2017-02-27T11:04:23Z pdf:docinfo:producer = MicrosoftR Office ExcelR 2007 pdf:docinfo:title = Apache Tikaのテスト用 pdf:encrypted = false pdfa:PDFVersion = A-1b pdfaid:conformance = B pdfaid:part = 1 producer = MicrosoftR Office ExcelR 2007 title = Apache Tikaのテスト用 xmp:CreatorTool = MicrosoftR Office ExcelR 2007 xmpMM:DocumentID = uuid:72AB1A36-B534-4714-90E1-7640E90E7083 xmpTPg:NPages = 1 [ヘッダ1, ヘッダ2, あ, ア, 阿, い, イ, 伊, う, ウ, 宇, え, エ, 江, お, オ, 尾]
PDFファイルだと「pdf:」で始まる名前を使ってMetadataから情報を取ればよい感じか。
コンテンツの方は、handler.toString()
で得られる文字列が抽出結果になっているので、あとは煮るなり焼くなりすればいい。
汎用的にしてみる
実は、上記のことはorg.apache.tika.parser.AutoDetectParser
を使っても実現できる。
ファイルタイプの検出に少し時間が掛かるのだが、PDFファイルだけではなくほかのファイルのテキストも同じコードで抽出したいという場合には有用だろう。
コードはほぼ同じになるが、以下のような感じ。
import java.io.*; import java.util.*; import org.apache.tika.exception.TikaException; import org.apache.tika.metadata.Metadata; import org.apache.tika.parser.AutoDetectParser; import org.apache.tika.parser.ParseContext; import org.apache.tika.parser.Parser; //import org.apache.tika.parser.pdf.PDFParser; import org.apache.tika.sax.BodyContentHandler; import org.xml.sax.ContentHandler; import org.xml.sax.SAXException; public class ParserSample { public static void main(String[] args) { // Parser parser = new PDFParser(); Parser parser = new AutoDetectParser(); for (String arg : args) { ContentHandler handler = new BodyContentHandler(); try (InputStream in = new FileInputStream(arg)) { Metadata metadata = new Metadata(); ParseContext context = new ParseContext(); try { parser.parse(in, handler, metadata, context); } catch (TikaException | IOException | SAXException e) { e.printStackTrace(); } List<String> list = new ArrayList<>(Arrays.asList(handler.toString().trim().split("\\s"))); List<String> names = new ArrayList<>(Arrays.asList(metadata.names())); Collections.sort(names); for (String name : names) { System.out.printf("%-48s = %s%n", name, metadata.get(name)); } System.out.println(); System.out.println(list); } catch (IOException e) { e.printStackTrace(); } } } }
試しに、先に使ったPDFファイルの元であるExcelファイルを食わせてみる。
Application-Name = Microsoft Excel Author = hhelibex Company = Content-Type = application/vnd.ms-excel Creation-Date = 2017-02-27T10:23:34Z Last-Author = hhelibex Last-Modified = 2017-02-27T10:31:13Z Last-Save-Date = 2017-02-27T10:31:13Z X-Parsed-By = org.apache.tika.parser.DefaultParser creator = hhelibex date = 2017-02-27T10:31:13Z dc:creator = hhelibex dc:title = Apache Tikaのテスト用 dcterms:created = 2017-02-27T10:23:34Z dcterms:modified = 2017-02-27T10:31:13Z extended-properties:Application = Microsoft Excel extended-properties:Company = meta:author = hhelibex meta:creation-date = 2017-02-27T10:23:34Z meta:last-author = hhelibex meta:save-date = 2017-02-27T10:31:13Z modified = 2017-02-27T10:31:13Z title = Apache Tikaのテスト用 [Sheet1, , , ヘッダ1, ヘッダ2, , あ, ア, 阿, , い, イ, 伊, , う, ウ, 宇, , え, エ, 江, , お, オ, 尾, , , Sheet2, , , , , Sheet3]
PDFファイルの場合も同様だが、AutoDetectParser
を使うと「X-Parsed-By」がMetadataに追加されている。