Node.jsでApatchTikaを使用して文字抽出を行う

# はじめに
様々な文書の文字抽出を行うことができる**[Apache Tika](https://tika.apache.org/)**(以降Tika)
ソースはJavaで書かれており、jarファイルから使用することができる。
今私の中でNode+TypeScriptが熱く、
Node.jsからこのTikaが使用できればいいなと思い、試行錯誤して何とか使用できるようになりました。
それまでの経緯を残したいと思います。

# 環境
- Node v14.17.0
- Typescript 4.3.2
- java 1.8.0_131 (jdk)
- npm 6.14.9
- Windows 10 Pro
- Python 2.7.12
- Visual Studio 2017


# Apatch Tikaのダウンロード
[ダウンロードサイト](https://tika.apache.org/download.html)から`Mirrors for tika-app-2.1.0.jar`をクリックしダウンロード

# npmを使用してモジュールをインストール
NodeプログラムからJavaを使用できるモジュール
npmのページは[こちら](https://www.npmjs.com/package/java)

```
npm i java
npm i --save-dev @types/java
```
このモジュールをインストールする際にエラーが多発しますが、
`Visual Studio2017`と`Python2系`のインストールをすれば正常にモジュールをインストールできるはずです。
VisualStadioをインストールせずにできる方法もあるようですが、
私の環境ではうまくいかず泣く泣くVisualStadioをインストールしました。



# コード(テキスト抽出)
```index.ts
import java from "java";

java.classpath.push("{ダウンロードしたTikaのjarファイルのパス}")
const AutoDetectParser = java.import('org.apache.tika.parser.AutoDetectParser')
const BodyContentHandler = java.import('org.apache.tika.sax.BodyContentHandler')
const Metadata = java.import('org.apache.tika.metadata.Metadata')
const upfile = java.import('java.io.FileInputStream')

export const tikaText = async () => {
  try {
    const parser = new AutoDetectParser()
    const handler = new BodyContentHandler()
    const metadata = new Metadata();
    const filestream = new upfile("test.pdf")　//文字抽出をしたいファイル名(パス)

    parser.parseSync(filestream, handler, metadata)
    console.log(handler.toString())
  } catch (e: any) {
    const regex = new RegExp('ZeroByteFileException', 'g')
    if (regex.test(e)) {
      console.log("ファイルに文字列がありません")
    } else {
      console.log("予期しないエラーが時発生しました。：" + e)
    }
  }
}

tikaText()
```
## 出力
```
8 31, 2021 6:21:35 ?? org.apache.tika.config.InitializableProblemHandler$3 handleInitializableProblem
??: J2KImageReader not loaded. JPEG2000 files will not be processed.
See https://pdfbox.apache.org/2.0/dependencies.html#jai-image-io
for optional dependencies.

8 31, 2021 6:21:36 ?? org.apache.tika.config.InitializableProblemHandler$3 handleInitializableProblem
??: org.xerial's sqlite-jdbc is not loaded.
Please provide the jar on your classpath to parse sqlite files.
See tika-parsers/pom.xml for the correct version.
WARN  Format 14 cmap table is not supported and will be ignored
WARN  Format 14 cmap table is not supported and will be ignored

テストファイルです。
```
ワーニングメッセージがコンソールに表示されますがファイルの内容を取り出せています。
`handler.toString()`の内容をファイル出力すればファイルの内容のみを見ることが出来ます

# コード(Xhtmlで抽出)
```index.ts
import java from "java";

java.classpath.push("{ダウンロードしたTikaのjarファイルのパス}")
const AutoDetectParser = java.import('org.apache.tika.parser.AutoDetectParser')
const ToXMLContentHandler = java.import('org.apache.tika.sax.ToXMLContentHandler')
const Metadata = java.import('org.apache.tika.metadata.Metadata')
const upfile = java.import('java.io.FileInputStream')

export const tikaXhtml = async () => {
  try {
    const parser = new AutoDetectParser()
    const handler = new ToXMLContentHandler()
    const metadata = new Metadata();
    const filestream = new upfile("test.pdf")

    parser.parseSync(filestream, handler, metadata)
    console.log(handler.toString())
  } catch (e: any) {
    const regex = new RegExp('ZeroByteFileException', 'g')
    if (regex.test(e)) {
      console.log("ファイルに文字列がありません")
    } else {
      console.log("予期しないエラーが時発生しました。：" + e)
    }
  }
}

export const tikaText = async () => {
  try {
    const parser = new AutoDetectParser()
    const handler = new BodyContentHandler()
    const metadata = new Metadata();
    const filestream = new upfile("test.pdf")  //文字抽出をしたいファイル名(パス)

    parser.parseSync(filestream, handler, metadata)
    console.log(handler.toString())
  } catch (e: any) {
    const regex = new RegExp('ZeroByteFileException', 'g')
    if (regex.test(e)) {
      console.log("ファイルに文字列がありません")
    } else {
      console.log("予期しないエラーが時発生しました。：" + e)
    }
  }
}

tikaXhtml()

```

## 出力
```
8 31, 2021 6:20:37 ?? org.apache.tika.config.InitializableProblemHandler$3 handleInitializableProblem
??: J2KImageReader not loaded. JPEG2000 files will not be processed.
See https://pdfbox.apache.org/2.0/dependencies.html#jai-image-io
for optional dependencies.

8 31, 2021 6:20:37 ?? org.apache.tika.config.InitializableProblemHandler$3 handleInitializableProblem
??: org.xerial's sqlite-jdbc is not loaded.
Please provide the jar on your classpath to parse sqlite files.
See tika-parsers/pom.xml for the correct version.
WARN  Format 14 cmap table is not supported and will be ignored
WARN  Format 14 cmap table is not supported and will be ignored
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta name="date" content="2021-08-31T09:18:20Z" />
<meta name="pdf:PDFVersion" content="1.7" />
<meta name="xmp:CreatorTool" content="Microsoft® Word 2016" />
<meta name="pdf:hasXFA" content="false" />
<meta name="access_permission:modify_annotations" content="true" />
<meta name="access_permission:can_print_degraded" content="true" />
<meta name="dc:creator" content="天野僚太" />
<meta name="language" content="ja-JP" />
<meta name="dcterms:created" content="2021-08-31T09:18:20Z" />
<meta name="Last-Modified" content="2021-08-31T09:18:20Z" />
<meta name="dcterms:modified" content="2021-08-31T09:18:20Z" />
<meta name="dc:format" content="application/pdf; version=1.7" />
<meta name="xmpMM:DocumentID" content="uuid:FCD419A5-3C1E-432C-A789-79E5B5B56632" />
<meta name="Last-Save-Date" content="2021-08-31T09:18:20Z" />
<meta name="pdf:docinfo:creator_tool" content="Microsoft® Word 2016" />
<meta name="access_permission:fill_in_form" content="true" />
<meta name="pdf:docinfo:modified" content="2021-08-31T09:18:20Z" />
<meta name="meta:save-date" content="2021-08-31T09:18:20Z" />
<meta name="pdf:encrypted" content="false" />
<meta name="xmp:CreateDate" content="2021-08-31T18:18:20Z" />
<meta name="modified" content="2021-08-31T09:18:20Z" />
<meta name="pdf:hasMarkedContent" content="true" />
<meta name="Content-Type" content="application/pdf" />
<meta name="xmp:ModifyDate" content="2021-08-31T18:18:20Z" />
<meta name="pdf:docinfo:creator" content="天野僚太" />
<meta name="X-Parsed-By" content="org.apache.tika.parser.DefaultParser" />
<meta name="X-Parsed-By" content="org.apache.tika.parser.pdf.PDFParser" />
<meta name="creator" content="天野僚太" />
<meta name="dc:language" content="ja-JP" />
<meta name="meta:author" content="天野僚太" />
<meta name="pdf:producer" content="Microsoft® Word 2016" />
<meta name="meta:creation-date" content="2021-08-31T09:18:20Z" />
<meta name="created" content="2021-08-31T09:18:20Z" />
<meta name="access_permission:extract_for_accessibility" content="true" />
<meta name="access_permission:assemble_document" content="true" />
<meta name="xmpTPg:NPages" content="1" />
<meta name="Creation-Date" content="2021-08-31T09:18:20Z" />
<meta name="pdf:hasXMP" content="true" />
<meta name="access_permission:extract_content" content="true" />
<meta name="access_permission:can_print" content="true" />
<meta name="Author" content="天野僚太" />
<meta name="producer" content="Microsoft® Word 2016" />
<meta name="access_permission:can_modify" content="true" />
<meta name="pdf:docinfo:producer" content="Microsoft® Word 2016" />
<meta name="pdf:docinfo:created" content="2021-08-31T09:18:20Z" />
<title></title>
</head>
<body><div class="page"><p />
<p>テストファイルです。 </p>
<p />
</div>
</body></html>
```
こちらも上記と同様に、
`handler.toString()`の内容をファイル出力すればファイルの内容のみを見ることが出来ます。

# 終わりに
npmモジュールのjavaを使用してNodeプログラムからTikaを操作する方法でした。
一癖も二癖もあるモジュールでしたので
npmモジュールjavaを使用しない、ほかの方法を模索してみたいと思いました。。。。

特にnpmのインストールとメソッドの呼び出し方に癖があり、苦労しました。
他にいい方法があればぜひ教えていただきたいです。