PythonでPDF結合
先日の仕事の後日談です。
しかし・・・
終わったと思ったら、どうやら上司からまた追加で面倒なことを頼まれたみたいです。
手書きの書類をスキャンして、それぞれの対応するPDFファイルの後ろに付けなければいけないとのこと。
こういうのは一度に言ってほしいですね・・・
(人のことは言えませんが)
乗り掛かった舟、放ってはおけません。
どんな仕様でしょうか。
手書き資料のスキャンは終わっている
結合する2つのPDFは別々のフォルダに格納されている
手書きスキャンのPDFは、結合PDFの一番後ろに来る
結合したファイルのファイル名は
Word→PDF変換ファイル名+”_signed.pdf”結合したPDFは1つのファイルに格納する
まずは各フォルダ内のPDFファイルをリスト化します。
from pathlib import Path
directory_path = r'C:\Word_PDF\docs'
# ディレクトリ内のサブフォルダ単位でLoop
for pdf_file_path in Path(directory_path).glob('*'):
# PDFファイルのパスを格納するリスト
pdf_files = []
#サブフォルダ内のPDFファイルをリストに追加
for pdf_name in pdf_file_path.glob('*.pdf'):
pdf_files.append(pdf_name)
print(pdf_files)
これで、サブフォルダごとにリスト化ができました。どうやらPDFの入っていない空のフォルダもあるようです。
[WindowsPath('C:/Word_PDF/docs/v1/Test1.pdf'), WindowsPath('C:/Word_PDF/docs/v1/test_sign1.pdf')]
[WindowsPath('C:/Word_PDF/docs/v2/Test2.pdf'), WindowsPath('C:/Word_PDF/docs/v2/test_sign2.pdf')]
[WindowsPath('C:/Word_PDF/docs/v3/Test3.pdf'), WindowsPath('C:/Word_PDF/docs/v3/test_sign3.pdf')]
[]
次にこのリストを、日付が新しい順にします。紙ファイルをスキャンした後に、Word→PDF変換をしたので、紙ファイルPDFはWord→PDF変換ファイルよりも古い日付になります。
日付ソートのやり方は、深いことを考えず、これをそのまま使えばよいです。日付で降順ソートしています。
sorted_pdf_files = sorted(pdf_files, key=lambda x: x.stat().st_ctime, reverse=True)
from pathlib import Path
directory_path = r'C:\Word_PDF\docs'
# ディレクトリ内のサブフォルダ単位でLoop
for pdf_file_path in Path(directory_path).glob('*'):
# PDFファイルのパスを格納するリスト
pdf_files = []
#サブフォルダ内のPDFファイルをリストに追加
for pdf_name in pdf_file_path.glob('*.pdf'):
pdf_files.append(pdf_name)
# PDFファイルを作成日時でソートして表示
sorted_pdf_files = sorted(pdf_files, key=lambda x: x.stat().st_ctime, reverse=True)
for file_name in sorted_pdf_files:
print(file_name)
このように、意図通りの順番に取り出せそうです。
C:\Word_PDF\docs\v1\Test1.pdf
C:\Word_PDF\docs\v1\test_sign1.pdf
C:\Word_PDF\docs\v2\Test2.pdf
C:\Word_PDF\docs\v2\test_sign2.pdf
C:\Word_PDF\docs\v3\Test3.pdf
C:\Word_PDF\docs\v3\test_sign3.pdf
各サブフォルダ内で、日付の新しいPDFファイルのファイル名を取ります。もしsorted_pdf_filesが空リストだったら処理がいらないので、
if sorted_pdf_files:で始めています。
ついでに、else:でPDFファイルが該当サブフォルダにはないことをお知らせします。
ファイル名の取得には、pathlibの".stem"を使いました。
from pathlib import Path
directory_path = r'C:\Word_PDF\docs'
# ディレクトリ内のサブフォルダ単位でLoop
for pdf_file_path in Path(directory_path).glob('*'):
# PDFファイルのパスを格納するリスト
pdf_files = []
#サブフォルダ内のPDFファイルをリストに追加
for pdf_name in pdf_file_path.glob('*.pdf'):
pdf_files.append(pdf_name)
# PDFファイルを作成日時でソートして表示
sorted_pdf_files = sorted(pdf_files, key=lambda x: x.stat().st_ctime, reverse=True)
if sorted_pdf_files:
# 最新のPDFファイルのファイル名を取得
latest_file_name = sorted_pdf_files[0].stem
print(latest_file_name)
else:
print(f"Not found PDF file in {pdf_file_path} ")
結果はこの通り。順調です。
Test1
Test2
Test3
Not found PDF file in C:\Word_PDF\docs\v4
日付降順でソートしたPDFファイル名のリストでfor loopします。
for file_name in sorted_pdf_files:
まずはprint(file_name)でプリントしてみます。
-->実際にはここにPDF mergeのプログラムがはいります。
from pathlib import Path
directory_path = r'C:\Word_PDF\docs'
# ディレクトリ内のサブフォルダ単位でLoop
for pdf_file_path in Path(directory_path).glob('*'):
# PDFファイルのパスを格納するリスト
pdf_files = []
#サブフォルダ内のPDFファイルをリストに追加
for pdf_name in pdf_file_path.glob('*.pdf'):
pdf_files.append(pdf_name)
# PDFファイルを作成日時でソートして表示
sorted_pdf_files = sorted(pdf_files, key=lambda x: x.stat().st_ctime, reverse=True)
if sorted_pdf_files:
# 最新のPDFファイルのファイル名を取得
latest_file_name = sorted_pdf_files[0].stem
for file_name in sorted_pdf_files:
print(file_name)
else:
print(f"Not found PDF file in {pdf_file_path} ")
予想通りにとれています。
C:\Word_PDF\docs\v1\test_sign1.pdf
C:\Word_PDF\docs\v1\Test1.pdf
C:\Word_PDF\docs\v2\test_sign2.pdf
C:\Word_PDF\docs\v2\Test2.pdf
C:\Word_PDF\docs\v3\test_sign3.pdf
C:\Word_PDF\docs\v3\Test3.pdf
Not found PDF file in C:\Word_PDF\docs\v4
ではいよいよ完成版です。PDFの結合にはPyPDF2ライブラリを使いました。
結合ファイルの保存先は
output_folder = r'C:\Word_PDF\output'とします。
ここで問題が発生!
merger.write(Path(f"{output_folder}\\{latest_file_name}_signed.pdf")
だとエラーが出てしまうのです。原因はPyPDF2がpathlibで取得した形式に対応していないからです。
そこで、 merged_pdf_path = str(Path(f"{output_folder}\\{latest_file_name}_signed.pdf"))のようにpathlib形式のpathを文字列変換してから使うとうまくいきました。
from pathlib import Path
import PyPDF2
directory_path = r'C:\Word_PDF\docs'
output_folder = r'C:\Word_PDF\output'
# ディレクトリ内のサブフォルダ単位でLoop
for pdf_file_path in Path(directory_path).glob('*'):
# PDFファイルのパスを格納するリスト
pdf_files = []
#サブフォルダ内のPDFファイルをリストに追加
for pdf_name in pdf_file_path.glob('*.pdf'):
pdf_files.append(pdf_name)
# PDFファイルを作成日時でソートして表示
sorted_pdf_files = sorted(pdf_files, key=lambda x: x.stat().st_ctime, reverse=True)
if sorted_pdf_files:
# 最新のPDFファイルのファイル名を取得
latest_file_name = sorted_pdf_files[0].stem
merger = PyPDF2.PdfFileMerger()
for file_name in sorted_pdf_files:
merger.append(str(file_name))
# 結合したPDFを保存
merged_pdf_path = str(Path(f"{output_folder}\\{latest_file_name}_signed.pdf"))
merger.write(merged_pdf_path)
merger.close()
print(f'Merged PDF saved to: {merged_pdf_path}')
else:
print(f"Not found PDF file in {pdf_file_path} ")
実行すると
Merged PDF saved to: C:\Word_PDF\output\Test1_signed.pdf
Merged PDF saved to: C:\Word_PDF\output\Test2_signed.pdf
Merged PDF saved to: C:\Word_PDF\output\Test3_signed.pdf
Not found PDF file in C:\Word_PDF\docs\v4
outputフォルダにまとめて保存されました。
ひとまずこれで終わったのですが、その後、これを封筒に入れて送るという指示が・・・