見出し画像

PythonでPDF結合

先日の仕事の後日談です。

しかし・・・
終わったと思ったら、どうやら上司からまた追加で面倒なことを頼まれたみたいです。

手書きの書類をスキャンして、それぞれの対応するPDFファイルの後ろに付けなければいけないとのこと。
こういうのは一度に言ってほしいですね・・・
(人のことは言えませんが)

乗り掛かった舟、放ってはおけません。

どんな仕様でしょうか。

  • 手書き資料のスキャンは終わっている

  • 結合する2つのPDFは別々のフォルダに格納されている

  • 手書きスキャンのPDFは、結合PDFの一番後ろに来る

  • 結合したファイルのファイル名は
    Word→PDF変換ファイル名+”_signed.pdf”

  • 結合したPDFは1つのファイルに格納する

Test1とTest_sign1をこの順番で結合しoutputフォルダに保存


まずは各フォルダ内の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フォルダにまとめて保存されました。


ひとまずこれで終わったのですが、その後、これを封筒に入れて送るという指示が・・・


いいなと思ったら応援しよう!