PyMuPDF
というPDF内の表抽出ができるライブラリがありますが、削除する直接的なメゾッドがなさそうだったので、何か方法がないか調べてみました。
手順
本記事では以下の手順で実装しています。
- PDFを読み込み
- 表の座標を抽出
- 座標を使って表を塗りつぶし保存
- 再度PDFを読み込み
注意事項
そもそもですが、私の経験上PyMuPDF
はすべての表を認識できる訳ではありません。
例えば縦線が入っていない表は表と認識されません。
また逆に画像中のテキストなど、表ではないのに表と認識されてしまうケースもあります。
今回はPyMuPDF
が正しく表と認識できるPDFを使用することを前提として記載しています。
実行環境
- Google Colaboratory
- Python: 3.10.12
- PyMuPDF: 1.24.11
参考にしたドキュメント・サイト
-
pymupdf
の基本的な使い方
https://pymupdf.readthedocs.io/ja/latest/the-basics.html#extracting-tables-from-a-page -
bbox
の使い方
https://medium.com/@pymupdf/table-recognition-and-extraction-with-pymupdf-54e54b40b760 -
add_redact_annot
とapply_redactions
の使い方
https://pymupdf.readthedocs.io/en/latest/page.html#redactions
実装
①PDFを読み込み
今回はこちらの論文を使用させていただきました。
「電車混雑予測 ~混雑の可視化が社会にもたらすインパクト~」
https://data.navitime.co.jp/pdf/monograph_20170610_2.pdf
colaboにはデフォルトでpymupdf
が入っていないのでpip
します。
!pip install pymupdf
早速ファイルを読み込み、正しく読み込めているか確認します。
今回はp. 12の表7を使用します。
import pymupdf
# PDFファイルを読み込み
pdf_path = "/content/sample.pdf"
doc = pymupdf.open(pdf_path)
page = doc[11] #12ページ目を指定
page.get_text()
【出力結果】
※全体が見えるように各文で改行しています
\n12 \n表-7 運賃収入減少上位路線 \n線名 \n収支(円/日) 割合(%) \nJR山手線 \n-262866 \n-0.098 \nJR東海道本線 \n-129302 \n-0.068 \nJR京浜東北線 \n-94601 \n-0.045 \nJR宇都宮線〔東北本線〕 \nJR上野東京ライン \n-64110 \n-0.046 \n京急本線 \n-58849 \n-0.065 \n東武東上線 \n-51553 \n-0.055 \n京急空港線 \n-48776 \n-0.994 \n小田急小田原線 \n-44747 \n-0.026 \n都営浅草線 \n-34341 \n-0.049 \n東武野田線 \n-30825 \n-0.986 \n \n表6,表7 は利用人員の増減を金額換算した表である.
\n収支の変化が大きい上位20 路線を掲載している.
黒字\nは増額,赤字は減額を表す.鉄道運賃は同一事業者内で\nは路線ごとの運賃というものはないが,距離に比例して\n運賃を按分することに仮想的に路線ごとの運賃を算出し\nた...
表7の情報も抽出されていることが確認できました。
②表の座標を抽出
まず表が正しく抽出できているか確認します。
tabs = page.find_tables()
print(f"{len(tabs.tables)} found on {page}")
【出力結果】
1 found on page 11
p. 12には1つしか表がないので、正しく認識できていると考えます。
次に座標を抽出します。
tab = tabs[0]
rect = tab.bbox
rect
【出力結果】
(51.21999979019165, 69.77996826171875, 278.46998087565106, 240.32000732421875)
余談ですが、今回はp. 12のテーブルは1つですが、tabs
とtab
は異なるクラスに属するので、上記をtabs.bbox
で取得しようとするとエラーが返ってきます。
いくつ目のテーブルか指定してから座標を抽出する必要がありそうです。
tabs.bbox
【出力結果】
AttributeError: 'TableFinder' object has no attribute 'bbox'
print(type(tabs))
print(type(tab))
【出力結果】
<class 'pymupdf.table.TableFinder'>
<class 'pymupdf.table.Table'>
③座標を使って表を塗りつぶし保存
# 赤字で塗りつぶし
page.add_redact_annot(rect, fill=(1, 0, 0)) # 赤字を指定
page.apply_redactions()
# 塗りつぶししたPDFを保存
output_path = "masked_pdf.pdf"
doc.save(output_path)
doc.close()
④再度PDFを読み込み
保存したPDFを読み込んで表の内容が削除されているか確認します。
doc_new = pymupdf.open(output_path)
page_new = doc_new[11]
page_new.get_text()
【出力結果】
※全体が見えるように各文で改行しています
\n12 \n表-7 運賃収入減少上位路線 \n \n表6,表7 は利用人員の増減を金額換算した表である.\n収支の変化が大きい上位20 路線を掲載している.
黒字\nは増額,赤字は減額を表す.鉄道運賃は同一事業者内で\nは路線ごとの運賃というものはないが,距離に比例して\n運賃を按分することに仮想的に路線ごとの運賃を算出し\nた.
乗継割引がある会社をまたいだ時の運賃も同様に距\n離按分した.
\n結果としては概ね路線別増減と一致している.
1日あ\nたり200 人増減,1 人あたりの運賃が200 円とすれば運\n賃収入は1日4万円前後の変化があることになり,結果\nは妥当だと考えられる.年額換算すると平日が年間250\n日とすれば1 路線1000 万円前後の変化がある計算にな\nる...
テーブル処理前の内容と比較してみると、「線名〜〜」意向が削除されており、表7の後の内容からが抽出されています。
念の為、masked_pdf.pdf
をローカルにダウンロードし、PDFを見に行ってみます。
上記のように赤く塗りつぶされていることがわかります。
終わりに
少し手間のかかる方法ですが、参考になれば幸いです。