Snowflake画像解析 

SnowflakeでPDFからテキスト抽出してみた

はじめに

データエンジニア部の大住です。
Snowflakeを使い、PDFからテキストを抽出してみました!

準備

このようなディレクトリ構成になっています。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
./
├── build.sbt
├── docker-compose.yml
├── project/
│ └── plugins.sbt
├── pdf/
│ └── test.pdf
└── src/
└── main/
├── resources/
│ └── pdfbox-2.0.26.jar
└── scala/
└── pdfocr.scala
./ ├── build.sbt ├── docker-compose.yml ├── project/ │ └── plugins.sbt ├── pdf/ │ └── test.pdf └── src/ └── main/ ├── resources/ │ └── pdfbox-2.0.26.jar └── scala/ └── pdfocr.scala
./
├── build.sbt
├── docker-compose.yml
├── project/
│   └── plugins.sbt
├── pdf/
│   └── test.pdf
└── src/
    └── main/
        ├── resources/
        │   └── pdfbox-2.0.26.jar
        └── scala/
            └── pdfocr.scala

必要なライブラリ pdfocr-2.0.26.jar は https://mvnrepository.com/ からダウンロードしました。

それぞれのファイルは以下のようになっています。

build.sbt

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
ame := "pdfocr"
version := "1.0"
scalaVersion := "2.12.15"
libraryDependencies ++= Seq(
"org.apache.pdfbox" % "pdfbox" % "2.0.26",
)
ame := "pdfocr" version := "1.0" scalaVersion := "2.12.15" libraryDependencies ++= Seq( "org.apache.pdfbox" % "pdfbox" % "2.0.26", )
ame := "pdfocr"
version := "1.0"
scalaVersion := "2.12.15"
libraryDependencies ++= Seq(
   "org.apache.pdfbox" % "pdfbox" % "2.0.26",
)

docker-compose.yml

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
ersion: '3'
services:
scala:
image: hseeberger/scala-sbt:11.0.12_1.5.5_2.12.15
tty: true
volumes:
- .:/work
working_dir: /work
ersion: '3' services: scala: image: hseeberger/scala-sbt:11.0.12_1.5.5_2.12.15 tty: true volumes: - .:/work working_dir: /work
ersion: '3'
services:
 scala:
   image: hseeberger/scala-sbt:11.0.12_1.5.5_2.12.15
   tty: true
   volumes:
     - .:/work
   working_dir: /work

plugins.sbt

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.7")
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.7")
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.7")

pdfocr.scala

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
package pdfocr
import java.io.InputStream
import org.apache.pdfbox.pdmodel.PDDocument
import org.apache.pdfbox.text.PDFTextStripper
object pdfocr {
def ocrFunc(filepath: InputStream): String = {
val pdDocument = PDDocument.load(filepath)
val pdfTextStripper = new PDFTextStripper()
val pdfText = pdfTextStripper.getText(pdDocument)
return pdfText
}
}
package pdfocr import java.io.InputStream import org.apache.pdfbox.pdmodel.PDDocument import org.apache.pdfbox.text.PDFTextStripper object pdfocr { def ocrFunc(filepath: InputStream): String = { val pdDocument = PDDocument.load(filepath) val pdfTextStripper = new PDFTextStripper() val pdfText = pdfTextStripper.getText(pdDocument) return pdfText } }
package pdfocr
 
import java.io.InputStream
import org.apache.pdfbox.pdmodel.PDDocument
import org.apache.pdfbox.text.PDFTextStripper
 
object pdfocr {
 def ocrFunc(filepath: InputStream): String = {
   val pdDocument = PDDocument.load(filepath)
   val pdfTextStripper = new PDFTextStripper()
   val pdfText = pdfTextStripper.getText(pdDocument)
   return pdfText
 }
}

test.pdfとしてSnowProCoreExamStudyGuideを利用しました。

pdfを配置するためのステージを作成します

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
create or replace stage pdf_stage
directory = (enable=true auto_refresh=true)
encryption = (type = 'SNOWFLAKE_SSE')
;
create or replace stage pdf_stage directory = (enable=true auto_refresh=true) encryption = (type = 'SNOWFLAKE_SSE') ;
create or replace stage pdf_stage
    directory = (enable=true auto_refresh=true)
    encryption = (type = 'SNOWFLAKE_SSE')
;

directory = (enable=true)とすることで非構造化データを扱えるようになります。
pdfをステージにputしておきます。コマンドは普通のputコマンドと変わりません。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
$ snowsql -a <account> -u <user> -d <database> -s <schema> -r <role> -q "put file://./pdf/test.pdf @pdf_stage auto_compress=FALSE"
$ snowsql -a <account> -u <user> -d <database> -s <schema> -r <role> -q "put file://./pdf/test.pdf @pdf_stage auto_compress=FALSE"
$ snowsql -a <account> -u <user> -d <database> -s <schema> -r <role> -q "put file://./pdf/test.pdf @pdf_stage auto_compress=FALSE"

ステージに配置したpdfは
スコープURL
ファイルURL
事前署名済みURL
などのURLからアクセスできます。

PDFからテキストを抽出するjarファイルを作成する

dockerコンテナを起動します。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
$ docker-compose up
$ docker-compose up
$ docker-compose up

起動したコンテナに入ります。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
$ docker-compose exec scala bash
$ docker-compose exec scala bash
$ docker-compose exec scala bash

pdfocr.scala からjarファイルを作成します。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
$ sbt “assembly”
$ sbt “assembly”
$ sbt “assembly”

これで target/scala-2.12 の下に pdfocr-assembly-1.0.jar が作成されます。

SnowflakeでUDFを作成する

作成したjarファイルを配置するためのステージを作成します。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
create or replace stage resources_stage;
create or replace stage resources_stage;
create or replace stage resources_stage;

作成したjarファイルをputします。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
$ snowsql -a <account> -u <user> -d <database> -s <schema> -r <role> -q "put file://./target/scala-2.12/pdfocr-assembly-1.0.jar @resources_stage auto_compress=FALSE"
$ snowsql -a <account> -u <user> -d <database> -s <schema> -r <role> -q "put file://./target/scala-2.12/pdfocr-assembly-1.0.jar @resources_stage auto_compress=FALSE"
$ snowsql -a <account> -u <user> -d <database> -s <schema> -r <role> -q "put file://./target/scala-2.12/pdfocr-assembly-1.0.jar @resources_stage auto_compress=FALSE"

ステージをrefreshして、jarファイルを使ったudfを作成します。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
alter stage resources_stage refresh;
create or replace function ocr_function(filepath string)
returns string
language java
imports = ('@resources_stage/pdfocr-assembly-1.0.jar')
handler = 'pdfocr.pdfocr.ocrFunc'
;
alter stage resources_stage refresh; create or replace function ocr_function(filepath string) returns string language java imports = ('@resources_stage/pdfocr-assembly-1.0.jar') handler = 'pdfocr.pdfocr.ocrFunc' ;
alter stage resources_stage refresh;

create or replace function ocr_function(filepath string)
returns string
language java
imports = ('@resources_stage/pdfocr-assembly-1.0.jar')
handler = 'pdfocr.pdfocr.ocrFunc'
;

PDFからテキストを抽出する

作成したudfを使ってpdf_stage上のpdfからテキストを抽出します。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
select
ocr_function(build_stage_file_url('@pdf_stage', relative_path)) as text
from
directory(@pdf_stage)
;
select ocr_function(build_stage_file_url('@pdf_stage', relative_path)) as text from directory(@pdf_stage) ;
select
    ocr_function(build_stage_file_url('@pdf_stage', relative_path)) as text
from
    directory(@pdf_stage)
;

結果

pdfからテキストの抽出ができました!

自動でテキストを抽出する

ステージにファイルが追加されるたびに自動でテキストの抽出を行い、抽出したテキストをテーブルに保存するようにしたいと思います。

まず、ステージの変更を追跡するためのストリームと、抽出したテキストを格納するテーブルを作成します。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
create or replace stream pdf_stream on stage pdf_stage;
create or replace table text_table(text varchar, filename varchar);
create or replace stream pdf_stream on stage pdf_stage; create or replace table text_table(text varchar, filename varchar);
create or replace stream pdf_stream on stage pdf_stage;

create or replace table text_table(text varchar, filename varchar);

ストリームから追加されたファイルを確認し、テキストを抽出してテーブルに保存するタスクを作成します。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
create or replace task ocr_task
warehouse = <warehouse>
schedule = '5 minute'
as
insert into text_table(text, filename)
select
ocr_function('@pdf_stage/' || RELATIVE_PATH)
, RELATIVE_PATH
from
pdf_stream
where
metadata$action = 'INSERT'
;
create or replace task ocr_task warehouse = <warehouse> schedule = '5 minute' as insert into text_table(text, filename) select ocr_function('@pdf_stage/' || RELATIVE_PATH) , RELATIVE_PATH from pdf_stream where metadata$action = 'INSERT' ;
create or replace task ocr_task
warehouse = <warehouse>
schedule = '5 minute'
as
    insert into text_table(text, filename)
    select
        ocr_function('@pdf_stage/' || RELATIVE_PATH)
        , RELATIVE_PATH
    from
      pdf_stream
    where
      metadata$action = 'INSERT'
;

実際にpdfをステージに追加してタスクを実行してみます。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
$ snowsql -a <account> -u <user> -d <database> -s <schema> -r <role> -q "put file://./pdf/test2.pdf @pdf_stage auto_compress=FALSE"
$ snowsql -a <account> -u <user> -d <database> -s <schema> -r <role> -q "put file://./pdf/test2.pdf @pdf_stage auto_compress=FALSE"
$ snowsql -a <account> -u <user> -d <database> -s <schema> -r <role> -q "put file://./pdf/test2.pdf @pdf_stage auto_compress=FALSE"

test2.pdfとしてSnowflake Key Concepts LEVEL UPを利用しました。

結果を確認します。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
execute task ocr_task;
select * from text_table;
execute task ocr_task; select * from text_table;
execute task ocr_task;

select * from text_table;

結果

できました!ちゃんと抽出されたテキストがテーブルに入っています!

おわりに

Snowflakeを使ってpdfからテキスト抽出を行いました。Snowflakeについてお困りごとがあればDATUM STUDIOまでお気軽にお問い合わせください。

このページをシェアする:



DATUM STUDIOは、クライアントの事業成長と経営課題解決を最適な形でサポートする、データ・ビジネスパートナーです。
データ分析の分野でお客様に最適なソリューションをご提供します。まずはご相談ください。