原則: PDFデータの再利用 には純正アプリの使用がお勧め
PDF to CSV の変換など、PDFデータの再利用はなかなか手間がかかる作業ですが、Adobe Acrobat Pro DC や ABBYY FineReader を使えば この作業は著しく省力化されます。
前者はサブスクリプション方式(買い切り製品もありますが)、後者は買い切りで、いずれにしてもそれなりのお値段です。正直、高い。
しかし、節約される時間を考えたら十分にお釣りが来ます。
そういうわけで、PDF 絡みの作業は基本的に Acrobat (Reader じゃないよ!)で行っており、月曜日も在インド日本国大使館のインド進出日系企業リスト-2018 [1]から Acrobat Pro DC を使ってデータ抽出を行い、データベースに格納したところでした。
News 2020年08月31日に在インド日本大使館のサイトで「インド進出日系企業リスト-2019」(2019_co_list_jp.pdf)[4] が公表されました。👉 2019版対応 script をこちらで公開しました。
また、上記 2019 年版を元にしたインド進出日系企業検索 [5] データベースを公開しました(2019-09-16)。
だがしかし: PDFデータの再利用 には tabula-py が尋常じゃなくお勧め
作業がひと段落し心に余裕ができたので、試しに Python を使って PDFをCSVに変換 してみようかなと、改めてモジュールを探してみたところ、とんでもないモジュールを発見。その名もTabula [2]。
そして、GitHub で公開されている Tabula のソースを Aki Ariga さんが Python のモジュールとして公開してくださっているのが、tabula-py [3]です。
この感動をシェアするため、tabula-py を使ってオリジナル の PDF から情報を抽出し、database ready なファイルにするまでの手順を書いてみることにしました。
🤫(以下、小さな声で…)確かにお勧めではあるんですが、仕事で PDF をデータ化する機会が多いのであれば、やはり Acrobat を使うのがお勧めです。また、Acrobat でうまくいかない時は ABBYY を使うとうまくいくことが多々あります。
Python を活用した作業は楽しくて勉強にもなったので、今回の立志篇以下、死闘篇、乱麻篇、そして望郷篇で紹介しましたが、Acrobat を使うと、今回の作業と、死闘篇及び乱麻篇の作業は不要になります 😢
そこで: PDF 表変換ユーティリティーを Colab に設置して楽をしよう
ローカルで作業しても良いのですが、せっかくなのでGoogle Colab/Drive(以下「Colab」)上に仕掛けを作ってやることにしました。こうしておけば、旅先の空港のパソコンでもいつも通りに作業できます(という場面は想像しにくい)。
ここで問題となるのは、Colab には tabula がインストールされていないということです。
もちろん、!pip install を使って Colab 上にモジュールを直接インストールすることはできるのですが、そのようにしてインストールされたモジュールは、 runtime を restart すると消えちゃうハカナイものです(*1)。
そこで、 Google Drive に module をインストールして Colab から見に行くようにしよう – という話は Google Colab/Driveにpipインストール: これなら消えない😃にも書きました。でもこれをいちいち見に行くのも面倒だと思いますので、以下では重複をいとわず全体の手順を書くことにします。
(*1) もしかしたら、このハカナサは積極的な意図に基づくものかもしれませんね。下手に永続化するとメンテナンスの問題が生じるから、数百 MB 程度のファイルは使い捨てにして、毎回新鮮なファイルをダウンロードして使おうとか。OS がトラブったら、問題の切り分け云々に時間を浪費しないで即クリーンインストール!的なポリシー。
今回の作業の流れ
- Google Drive 上に必要なフォルダを作る
ディレクトリのセッティング は退屈な作業ですが、我慢して整えておくと後の作業が楽になります。 - Google Drive 上に tabula モジュールをインストールする
tabula-py の pip installation が今回のメインです。 - 読み込み元の PDF を入手し、Google Drive に保存する
インド進出日系企業リスト-2018 を入手して Google Drive に作った data フォルダに格納します。 - 上記 PDF が tabula で正常に読めることを確認する
PDF ファイルの読み込みテストを行います。
ディレクトリのセッティング
細かい話で恐縮ですが、まずは自分の Google Drive に行って、Fig. 1 のようにディレクトリ(フォルダ)を準備してください。
この辺、本質とはあまり関係ない話ですがディレクトリ構成はパターン化しておいた方が何かと円滑に行くので(というか、こうしておかないと書いてて私自身がわからなくなっちゃうので💦)。
あるいは、PROJECT_ROOT_PATH = ‘.’ としておけば、ローカルマシンでも、作業ディレクトリ以下に同じようなディレクトリ構成ができます。
ただし、その場合 pip install は –target option 無しで行ってください。
MODULE_PATH は、自分でインストールする python modules の置き場所、DATA_PATH は、オリジナルの PDF ファイルの置き場所です。
Fig. 1 は Colab にマウントした Google Drive の デイレクトリ を Colab から見た path ですから、直接 Google Drive をブラウザで開いた場合、’Colab Notebooks’ は root 直下のディレクトリになります。
PROJECT_ROOT_PATH
と DATA_PATH
も一応こんな感じで作っておいてください。
Fig. 1: Google Drive のディレクトリ構成(Google Colab 目線)
MODULE_PATH: /content/drive/My Drive/Colab Notebooks/my-modules PROJECT_ROOT_PATH: /content/drive/My Drive/pdf_project DATA_PATH: /content/drive/My Drive/pdf_project/data/
スクリプトでやるなら、Google Drive を mount した 状態で、Google Colab から Fig. 2 のような感じで。
Fig 2: Google Drive のディレクトリ等の設定
import os import sys # tabula はまだインストールされていないので、後で import する。 MODULE_PATH = '/content/drive/My Drive/Colab Notebooks/my-modules' PROJECT_ROOT_PATH = '/content/drive/My Drive/pdf_project' DATA_PATH = os.path.join(PROJECT_ROOT_PATH, 'data') # 上記のディレクトリが無かったら新規作成しておく。 if not os.path.isdir(MODULE_PATH): os.mkdir(MODULE_PATH) if not os.path.isdir(PROJECT_ROOT_PATH): os.mkdir(PROJECT_ROOT_PATH) if not os.path.isdir(DATA_PATH): os.mkdir(DATA_PATH) # PYTHON PATH を通しておく(こうしておかないと python が tabula-py を見つけられない)。 sys.path.append(MODULE_PATH)
tabula-py の pip installation
いよいよ tabula-py のインストールです。
Fig. 3 ではインストール先のオプション(–target)に、上で作った MODULE_PATH を指定しています(これは shell の操作なので、python 内で定義した定数 MODULE_PATH で指示しても「そんなん知らんわ💢」と言われてしまうので、絶対PATH をベタ書きします)。
途中でERRORがでますが、焦らずに待っているとやがて全部うまくインストールされます。
あとで Google Drive を見に行くとわかりますが、依存関係のある packages を全部インストールしてるので、pandas だとか numpy だとか、諸々の packages 一式がインストールされた大所帯になっています😃
一番下に RESTART RUNTIME とあるので、ブラウザ左上の Runtime メニューから Runtime を再起動してください。
Fig 3: tabula-py の pip installation
# path の space を \(back slash) で escape していることに注意。 !pip install --target /content/drive/My\ Drive/Colab\ Notebooks/my-modules tabula-py Collecting tabula-py Downloading https://files.pythonhosted.org/packages/8d/ed/20655a47a603430272c995d908d0dd96f93c2aa8973c8a55a66c8f3b8dfe/tabula_py-2.1.1-py3-none-any.whl (10.4MB) |████████████████████████████████| 10.4MB 2.5MB/s Collecting distro Downloading https://files.pythonhosted.org/packages/25/b7/b3c4270a11414cb22c6352ebc7a83aaa3712043be29daa05018fd5a5c956/distro-1.5.0-py2.py3-none-any.whl Collecting pandas>=0.25.3 Downloading https://files.pythonhosted.org/packages/a7/f7/2adca20a7fa71b6a32f823bbd83992adeceab1d8bf72992bb7a55c69c19a/pandas-1.1.0-cp36-cp36m-manylinux1_x86_64.whl (10.5MB) |████████████████████████████████| 10.5MB 48.3MB/s Collecting numpy Downloading https://files.pythonhosted.org/packages/b1/9a/7d474ba0860a41f771c9523d8c4ea56b084840b5ca4092d96bdee8a3b684/numpy-1.19.1-cp36-cp36m-manylinux2010_x86_64.whl (14.5MB) |████████████████████████████████| 14.5MB 314kB/s Collecting python-dateutil>=2.7.3 Downloading https://files.pythonhosted.org/packages/d4/70/d60450c3dd48ef87586924207ae8907090de0b306af2bce5d134d78615cb/python_dateutil-2.8.1-py2.py3-none-any.whl (227kB) |████████████████████████████████| 235kB 43.9MB/s Collecting pytz>=2017.2 Downloading https://files.pythonhosted.org/packages/4f/a4/879454d49688e2fad93e59d7d4efda580b783c745fd2ec2a3adf87b0808d/pytz-2020.1-py2.py3-none-any.whl (510kB) |████████████████████████████████| 512kB 44.0MB/s Collecting six>=1.5 Downloading https://files.pythonhosted.org/packages/ee/ff/48bde5c0f013094d729fe4b0316ba2a24774b3ff1c52d924a8a4cb04078a/six-1.15.0-py2.py3-none-any.whl ERROR: tensorflow 2.3.0 has requirement numpy<1.19.0,>=1.16.0, but you'll have numpy 1.19.1 which is incompatible. ERROR: google-colab 1.0.0 has requirement pandas~=1.0.0; python_version >= "3.0", but you'll have pandas 1.1.0 which is incompatible. ERROR: datascience 0.10.6 has requirement folium==0.2.1, but you'll have folium 0.8.3 which is incompatible. ERROR: albumentations 0.1.12 has requirement imgaug<0.2.7,>=0.2.5, but you'll have imgaug 0.2.9 which is incompatible. Installing collected packages: distro, six, python-dateutil, pytz, numpy, pandas, tabula-py Successfully installed distro-1.5.0 numpy-1.19.1 pandas-1.1.0 python-dateutil-2.8.1 pytz-2020.1 six-1.15.0 tabula-py-2.1.1 RESTART RUNTIME
RUNTIME RESTART 後の手当て
Runtime を再起動したため:
- 今までのできごとはすべて忘れてしまいました。
- なので、Fig. 2 の設定 をもう一度やってあげてください。
- それから、肝心の tabula のインストールが完了したので、忘れずに import しておいてください(というか、これが一番大事)。
# tabula の import import tabula
これって、要するに setup の手順がマズかっただけなんじゃないの?という説もありますが、順序を組み替えると流れ的に辛いのでこのままでご容赦いただきたく。▶️ボタンをポチる手間が 1 回増えただけだし…。
PDF ファイルの読み込み
いよいよ PDF ファイルの読み込みです。
- 今回は、在インド日本国大使館が公表している「インド進出日系企業リスト-2018」を使います。リンク先のサイトに行って「詳細版」(2018_co_list_jp_r.pdf)をダウンロードしてきてください(そもそもの目的がこのファイルから情報を抽出することでした)。
- 入手した PDF は、Google Drive の /content/drive/My Drive/pdf_project/data に格納して用意しておいてください。
- 心配だったら、次の script を走らせて、PDF ファイルが所定の場所にちゃんと置かれていることを確認後、安心して次に進んでください。
# この script を走らせて True と表示されれば OK. os.path.isfile(os.path.join(DATA_PATH, '2018_co_list_jp_r.pdf'))
読み込みの設定
PDF ファイルの読み込みには、すくなくとも次の 2つの値を与える必要があります。
- File path = 今回は、
os.path.join(DATA_PATH, '2018_co_list_jp_r.pdf')
- 読み込ませるページの指定 = 今回は
5-112
読み込ませるページはオリジナルの PDF を見て決めてください。今回の PDF ファイルでは、概要や説明、目次に続く 5ページ目から最後の112ページまで(全 108 ページ)を読み込ませることにします。
%%time # PDF ファイルの読み込み # WARNING が出ますが、今回の目的との関係では無視して差し支えありません。 df_list = tabula.read_pdf(os.path.join(DATA_PATH, '2018_co_list_jp_r.pdf'), pages='5-112') Got stderr: Aug 05, 2020 5:32:12 AM org.apache.fontbox.ttf.CmapSubtable processSubtype14 WARNING: Format 14 cmap table is not supported and will be ignored # 5,000 行ちょっとを読み込むので少し時間(30 sec ほど)が掛かります。 CPU times: user 432 ms, sys: 31.3 ms, total: 464 ms Wall time: 26.2 s
ファイルを読み込んだ変数(df_list)の中身
tabula は、読み込んだページごとに内容を それぞれ Pandads の DataFrame としてまとめます。そしてそれらの DataFrame を包含したひとつの list (DataFrame の list)にして返します。
いま、108 pages (オリジナル PDF pp. 5-112)を読み込んだので、108 個の DataFrame を含んだひとつの list が返ってきているはずです。試しに、len を取ってみると…
# list(df_list)に含まれる DataFrame の数(= 読み込んだ PDF のページ総数)を確認 len(df_list) 108
FYI: Adobe Acrobat で Microsoft Excel に変換した場合は、ページごとに切り分けられず、全体が連続した 1 つの表に変換されます。
DataFrame の内容を確認
ここで試しに、最初(ゼロ番目)の DataFrame の様子 df_list[0]
(Fig. 4)と、108 個の DataFrame の shape (Fig. 5) を確認しておきます。
OK! 🍣 🍻 🎉 🍾 ❣️
というわけで、ここまでは問題無さそう(*2)。次回、Data cleansing 編に突入し第二話 死闘篇に続きます。
(*2) 実は Fig. 5 には重大な問題の兆候が明白に現れていたのだった…。
Fig 4: PDF 読み込み結果の確認(最初の DataFrame)
Fig 5: PDF 読み込み結果の確認(DataFrame の shape)
for i, each_df in enumerate(df_list): if i % 5 == 0: print() print('p.'+str(i+5).zfill(3), each_df.shape, ' ', end='') p.005 (48, 6) p.006 (48, 6) p.007 (48, 6) p.008 (48, 6) p.009 (48, 6) p.010 (48, 6) p.011 (48, 6) p.012 (111, 6) p.013 (48, 6) p.014 (48, 6) p.015 (48, 6) p.016 (48, 6) p.017 (48, 6) p.018 (48, 6) p.019 (48, 6) p.020 (48, 6) p.021 (48, 6) p.022 (48, 6) p.023 (48, 6) p.024 (48, 6) p.025 (48, 6) p.026 (48, 6) p.027 (48, 6) p.028 (48, 6) p.029 (48, 6) p.030 (48, 6) p.031 (48, 6) p.032 (48, 6) p.033 (48, 6) p.034 (48, 6) p.035 (48, 6) p.036 (48, 6) p.037 (48, 6) p.038 (48, 6) p.039 (48, 6) p.040 (48, 6) p.041 (48, 6) p.042 (48, 6) p.043 (48, 6) p.044 (48, 6) p.045 (48, 6) p.046 (48, 6) p.047 (48, 6) p.048 (48, 6) p.049 (48, 6) p.050 (48, 6) p.051 (48, 6) p.052 (48, 6) p.053 (48, 6) p.054 (48, 6) p.055 (117, 6) p.056 (48, 6) p.057 (48, 6) p.058 (48, 6) p.059 (48, 6) p.060 (48, 6) p.061 (48, 6) p.062 (48, 6) p.063 (48, 6) p.064 (48, 6) p.065 (48, 6) p.066 (48, 6) p.067 (48, 6) p.068 (48, 6) p.069 (48, 6) p.070 (48, 6) p.071 (48, 6) p.072 (48, 6) p.073 (48, 6) p.074 (48, 6) p.075 (48, 6) p.076 (48, 6) p.077 (48, 6) p.078 (48, 6) p.079 (48, 6) p.080 (48, 6) p.081 (48, 6) p.082 (48, 6) p.083 (48, 6) p.084 (48, 6) p.085 (48, 6) p.086 (48, 6) p.087 (48, 6) p.088 (48, 6) p.089 (48, 6) p.090 (48, 6) p.091 (48, 6) p.092 (48, 6) p.093 (111, 6) p.094 (110, 6) p.095 (48, 6) p.096 (48, 6) p.097 (48, 6) p.098 (48, 6) p.099 (48, 6) p.100 (48, 6) p.101 (48, 6) p.102 (48, 6) p.103 (48, 6) p.104 (48, 6) p.105 (48, 6) p.106 (48, 6) p.107 (48, 6) p.108 (48, 6) p.109 (48, 6) p.110 (48, 6) p.111 (48, 6) p.112 (6, 6)
Main Takeaways
今日は以下の作業を片付けました。
- Google Drive 上に必要なフォルダを作った
- Google Drive 上に tabula モジュールをインストールした
- 2018_co_list_jp_r.pdf を入手し、Google Drive に保存した
- 3. の PDF が tabula で正常に読めることを確認した (🤥)
次回の準備
次に作業再開するときのため、Fig. 6 の script を Google Colab の Notebook に貼り付けて、たとえば pdf_to_csv_prep.ipynb など適当な名前をつけて、Colab のメニューから File > Save a copy in Drive で保存しておいてください。
ここまで準備しておけば、次回は上記 4. から再開することができます。
Fig 6: 次回の準備(pdf_to_csv_prep.ipynb)
# Google Drive のマウント。 from google.colab import drive drive.mount('/content/drive') # Modules の import. import os import sys # PATH を通す(Python に modules の場所を教える) MODULE_PATH = '/content/drive/My Drive/Colab Notebooks/my-modules' sys.path.append(MODULE_PATH) import tabula # Module の場所を教えたので、import. # ディレクトリ構造を定義する。 PROJECT_ROOT_PATH = '/content/drive/My Drive/pdf_project' DATA_PATH = os.path.join(PROJECT_ROOT_PATH, 'data') OUTPUT_PATH = os.path.join(PROJECT_ROOT_PATH, 'output') print('準備完了 🍻')
謝辞
在インド日本国大使館が継続的に公表している「インド進出日系企業リスト」(在インド日本国大使館、ジェトロ)には 10年以上にわたり大変お世話になっています。
この極めて貴重なリソースがより多くの方々に活用されるようになる上での一助として、もし本記事が貢献できればと願うものです。
この場を借りて、在インド日本国大使館及びジェトロのみなさまに感謝申しあげます。
GGCS
References
[1] 在インド日本国大使館, (独)日本貿易振興機構 (2019). インド進出日系企業リスト-2018
[2] tabulapdf, Tabula
[3] Aki Ariga, tabula-py (PyPI)
[4] 在インド日本国大使館, (独)日本貿易振興機構 (2020). インド進出日系企業リスト-2019
[5] ごたごた気流調査所 (2020). インド進出日系企業検索