ホームページ >バックエンド開発 >Python チュートリアル >週末コーディング: PDF 給与明細を単一の CSV レポートに変換

週末コーディング: PDF 給与明細を単一の CSV レポートに変換

Susan Sarandon
Susan Sarandonオリジナル
2024-12-25 22:20:17124ブラウズ

PDF ファイルを使用してプログラムしたことがありますか?一緒に Python スクリプトを書きましょう!

Weekend Coding: Turn PDF Payslips Into a Single CSV Report

今後のブログ投稿でわかるように、私は金融リテラシーの時代にいます。年末が近づいてきたので、自分の数字を確認したいと思いました。自分はいくら税金を支払ったのでしょうか?オンコールシフトの収入はいくらですか?複数の PDF ファイルはこのデータを表示するのに最も快適な方法ではありません。Excel で操作できる単一の CSV ファイルが必要でした。

多くの優秀な開発者と同じように、私も数字を手動で挿入するのが面倒だったので、スクリプトを書きました。プログラミングが好きなら、私と一緒に冒険に出かけましょう!気分が良くない場合は、給与明細の構造に合わせてコードを調整する方法を説明します :D

Weekend Coding: Turn PDF Payslips Into a Single CSV Report
This script receives a directory with payslip PDFs and returns a CSV file with the desired data

計画

main.py:
# translate 1 pdf to 1 dict
# loop over the pdf dir
# save all dicts to 1 json file
# translate json report to csv report

まず、レポートにどのフィールドが必要かを決定するなど、PDF を読み取るコードを記述します。これは、給与明細の構造に合わせて調整する必要がある部分です。それがわかったら、給与明細ディレクトリ全体を繰り返し処理します。

3 番目のステップでは、PDF と CSV の間に追加のステップ、つまり JSON レポートを追加することにしました。すべてが機能することが確認できたら、そのファイルの使用を削除します。

最後に、JSON データを CSV ファイルに変換します。その CSV は、Google スプレッドシート ([開く] をクリックするだけ) または Excel (手順はこちら) に簡単に変換できます。

それは簡単で素晴らしい計画ですが、どのように進むかはご存知です — 途中で課題が発見されます…どこで物事が複雑になるかわかりますか?

始める前に - 重要な注意事項: 給与明細は非公開にしてください!プロジェクトを GitHub にアップロードする場合は、個人情報を共有しないようにしてください。これには .gitignore を使用できます:

/payslips_pdf
pdf_rows.txt
report.json
report.csv

始めましょうか?

Weekend Coding: Turn PDF Payslips Into a Single CSV Report

PDFファイルを読む

まず、PDF を読み取り、すべての行を印刷します。こうすることで、各行に何が表示されているかがわかります。これは 1 回だけ行う必要があります (レポートはおそらく月に 1 回または年に 1 回作成されます)。これはレポートの一部ではないため、別のファイルに作成します。

まず、新しい Python ファイル (私は pdf_to_txt.py と呼びました) を作成し、pdf を読み取り、結果を .txt ファイルに出力する関数を作成します。



これは機能しますが、より使いやすくするために 3 つの変更があります:
  • ファイル パスをコマンド ライン引数として取得すると、ユーザーはコードに触れることなくファイル パスを実行できます。
  • ユーザーがコマンドを間違って実行した場合に備えて、エラーをキャッチする手順を追加します。 (警告色を追加しましたが、追加する必要はありません)

  • メイン スクリプトでも PDF ファイルを読み取るため、この関数をそこに移動する方が良いでしょう。

ぜひ試してみてください!正しい引数と間違った引数を指定してコマンドを実行してみてください:)


py ./main.py を実行すると、プロジェクト ディレクトリに pdf_rows.txt という新しいファイルが表示されます。
<script></script> <script></script> <script></script>
(心配しないでください。これは私のデータではありません。下の画像の給与明細の例に基づいています)

PDFデータを加工する

PDF が読み取られる構造がわかったので、必要な値を取得できます。私の場合、私が興味を持った情報は次のとおりです:

Weekend Coding: Turn PDF Payslips Into a Single CSV Report

テーブル内にデータ (毎月異なる可能性のあるカテゴリ) とテーブルの外にデータがあることに注意してください。

テーブル外のデータ:

支払い期間 — 行 19

にあります

総支払額 — これは支払いリストのに表示され、「総支払額」というタイトルがついていないため、ルールを見つけるのが困難でした。

前述したように、支払い控除は変動する可能性があり、毎月同じであるわけではありません。したがって、総支払額は月ごとに異なる行に表示される場合があります。

これが従業員名の直後にあることに気付きました。それで、それを使用しました。まずハードコーディングして追加し、後で外部から取得します。

Nett Pay: これは簡単です。17 行目に表示されます。

これらの表外の値を関数に集めました:


テーブル内のデータ

支払いと控除の詳細: ここが重要な部分です。まず、行配列をカットして、今後の for ループで数ミリ秒を節約します。次に、リスト項目

と他の行を区別する必要がありました。

ファイル全体の中で、このルールに一致するのはリスト項目だけであることに気付きました: 英字 で始まり、 数字 で終わる にはスペースが含まれています (最後の条件は、間違った行をフィルターで除外することです) 私の
給与明細、必要ないかもしれません)。

<script></script> <script></script>
行のグループができたので、それらを処理して JSON オブジェクトに保存しましょう。この時点では、それが支払いであるか控除であるかは気にしません。

たとえば、年金項目を見てみましょう:

main.py:
# translate 1 pdf to 1 dict
# loop over the pdf dir
# save all dicts to 1 json file
# translate json report to csv report

残高 (右側の数字) は気にしませんが、コードは気にします (G は、税引き前の総給与から差し引かれることを意味します) — そして N は、税引後の純額から差し引かれることを意味します)。したがって、理想的には、 json_obj["Pension (G)"]=150.00 になります。

スペースを使用して行を分割します。スペースが重複しているのは良いことです。そうすることで、いくつかの単語間のスペース分割と、いくつかのフィールド間のスペース分割を区別できます。

説明:

最初のダブルスペースを見つけて、それによって分割します。

コード:

スペースの量は説明の長さに依存するため、スペースの数を事前に知ることはできません。そのため、lstrip() も使用します。行の残りの部分はスペース以外の文字で始まります。

すべてのリスト項目にコードがあるわけではないため、行がコードで始まるか数字で始まるかを確認する必要があります。コードの場合は、() (左括弧の前のスペースを含む) で囲み、説明文字列に付加します。そうでない場合は、何も追加しません。

金額:

コードがあった場合、削除するスペースがさらに多くなります。そうでない場合、明細行には月次と残高という 2 つの金額が含まれる可能性があります

私が気づいたケースは 4 つあります:

/payslips_pdf
pdf_rows.txt
report.json
report.csv
カテゴリとコードを抽出すると、次のものが残ります:

PENSION     G   150.00   587.49
ケース 2 ~ 3 をカバーするために、金額を区切るスペースのインデックスを見つけて、末尾をカットします。これは、スペースがない (別名テールなし) という最初のケースにも機能します。

ケース 4 をカバーするために、行に 1 つの金額が含まれる 2 つのタイプのカテゴリの違いに依存しています。最初のタイプは給与のようなもので、金額を保存したい場所であり、2 番目のタイプは次のようなものです。源泉徴収税は無視したいものです。違いは、表の年間残高を追跡するのは控除のみであることです。したがって、私は - をチェックしています。

すべてをまとめると次のようになります:


JSONファイルへの書き込み

これは必須の手順ではありません。値をエクスポートせずに JSON オブジェクトを操作できます。少なくともコーディング段階では、それがどのようなものかを確認することを好みます。

複数の PDF ファイルに合わせて拡大縮小する


このステップに専用のセクションが設けられている唯一の理由は、pdf_to_dict を for ループでラップすると不快な驚きが明らかになるからです。それを実証するために、iterate_over_pdfs():

という関数を作成しました。
<script></script> <script></script>
これは、ファイルのリストがアルファベット順に並べ替えられているため、10 が 2 の前に表示されるために発生します。レポート エントリを時系列順に並べることは、単なる便利な機能ではなく、重要であると考えられます。したがって、それを修正する必要があります。

当初、ファイルの名前を変更する必要があると考えていました (Pay Slip1.pdf -> Pay Slip01.pdf) が、より良い解決策があります:


ファイル名のリストを長さで並べ替えると、2 の後に 10 が表示されます。最後の行で、名前をデコードして b'' を削除しました。デフォルトの構造。

CSVレポートを作成する

支払いと控除の項目は給与明細によって異なる場合があるため、このセクションは単なる直訳ではありません。 CSV はリレーショナル データセットです。つまり、支払いと控除のすべてのカテゴリを事前に把握し、給与明細が存在しない場合はエントリを空にしておく必要があります。一方、JSON は非リレーショナルであり、各エントリはそのキーを指定します。

これを念頭に置いて、CSV レポートの最初のステップはカテゴリを収集することです。すべてのカテゴリ。

カテゴリを収集します:


一見すると、そのために Set を使用することを考えるかもしれません。なぜなら、すべてのカテゴリを 1 回だけ表示したいからです。それを試してみました。これの問題は、セットがリストに掲載されていないことであり、元の給与明細に表示される項目の順序と一致させることが重要であることがわかりました。リストを使用する場合は、項目を追加する前にリストに項目が存在するかどうかを必ず確認してください:


これで次のことが理解できました。以前、どの品目リストが支払いでどの品目リストが控除であるかは気にしないと言ったのを覚えていますか?まあ、私たちは今は気にしています!私たちは分ける

必要はありません
が、給与明細報告書には、右側にすべての支払いと左側にすべての控除が混在せずに記載されることを期待します。

各給与明細には異なるリスト項目がある可能性がありますが、一部の項目は常に存在します (税金は常に支払うため ;) )。これを有効に利用して、控除の開始として PAYE にフラグを立てることができます。 (PAE はアイルランドのみにあると確信しているため、給与明細に合わせて変更する必要があります)

最後に、単一のリストを返します。支払いと控除を分離しても意味がないためです。この分割は、支払いが右側に表示され、控除が左側に表示されるようにするためのものです。

CSV テーブルにデータを入力します:


カテゴリーができたので、CSV テーブルへの入力を開始できます。

各給与明細は 1 行になり、各行にはカンマで区切られた特定の順序でフィールドが含まれます。フィールドをリストに整理して結合する方が簡単だと思います。カテゴリには表示されるが給与明細には表示されないフィールドは空のままになります:


<script></script> 最後に、CSV ファイルに書き込みます:<script></script> <script></script> <script></script> <script></script>
この後、すべての給与明細を含む素敵な CSV レポートが作成されます。

VS 拡張機能 RainbowCSV (または別の IDE の類似物) をダウンロードすると、読みやすくなります

.json ファイルの使用を削除する

一度動作することがわかったら、JSON ファイルへの書き込みや読み取りを行う必要はありません。JSON オブジェクトを直接使用できます。

json_object (json_object = json.dumps(json_paylips) のように) を使用する代わりに、json_paylips を直接使用します。

  1. 書き込み: report.json に書き込む必要はありません。このセクションは削除できます。

  2. 読み取り: json_paylips を json_to_csv() 関数に直接渡します:

従業員の名前を引数として取得します

スクリプトの準備ができたら、同僚や友人と共有したくなるでしょう。優れたユーザー エクスペリエンスを実現するために、従業員にコードを開いてもらうのではなく、コマンド ラインから従業員名をエクスポートします。

引数の読み取り

ユーザーが従業員名を入力したと仮定して、ハッピー パスから始めて、それを使用するコードを追加します。

pdf_to_dict() では、EMPLOYEE_NAME = "IFAT NEUMANN" をハードコーディングする代わりに、引数から読み取ります:employee_name = sys.argv[1]。 sys をインポートすることを忘れないでください!

次に、他のシナリオについて考えてみましょう:

従業員名が指定されていません

ユーザーが従業員名を入力しなかったらどうなるでしょうか?できるだけ早く見つけて、彼らに通知したいと思います!

そこで、main関数の1行目にチェックを追加します。ここで、直感的には、そこでemployee_name変数を初期化することになりますが、これにより、この変数を使用する関数に到達するまで関数のプロパティが泡立つことになります。そして、私はそれがあまりきれいなアプローチとは思えません。


最後に、このフィールドにアクセスしてみます。フィールドが存在しない場合はキャッチします。


main.py:
# translate 1 pdf to 1 dict
# loop over the pdf dir
# save all dicts to 1 json file
# translate json report to csv report

例外を追加するということは、print_warning() 関数が main.py に移動することを意味することに注意してください。そうしないと、エラーが発生します。

引用符なしの従業員名


コマンドライン引数はスペースで区切られるため、ユーザーに名前を引用符で囲むよう求めています。私たちが期待する唯一の引数は従業員名です。そのため、別の引数がある場合、彼らが引用符を使用していないことがわかっているので、次のように通知できます。



引用符の要件をスキップし、引数をループしてユーザー名のすべての部分を収集することもできますが、このアプローチでは不必要な複雑さが追加されることがわかりました。

給与明細に従業員の名前が記載されていない


給与明細に記載されている従業員の名前がないと、総給与を見つけることができません。

不一致を早期に特定するのは、PDF を読み取るときです。したがって、pdf_to_dict() 関数の先頭にチェックを追加します。
<script></script> <script></script> <script></script> <script></script>
sys.argv[1] がテキストにない場合よりも読みやすいため、employee_name = sys.argv[1] をこの関数に移動したことに注意してください。その後、それを get_fixed_values() に渡します。

最後に、メイン関数でエラーをキャッチします。


最後に、新しい手順で README ファイルを更新することを忘れないでください。

完全なスクリプト


給与明細の操作に使用できる完全なコードは次のとおりです:


Weekend Coding: Turn PDF Payslips Into a Single CSV Report
https://cupofcode.blog/
楽しんでいただければ幸いです!週末のコーディング プロジェクトに興味がありますか?私の自動メール送信に関するブログ投稿をご覧ください。

ブログは私の趣味なので、喜んで時間とお金を費やします。このブログ投稿を気に入っていただけた場合は、投げ銭瓶に 1 ユーロを入れていただければお知らせいたします :) ご支援ありがとうございます! <script></script> <script></script>

以上が週末コーディング: PDF 給与明細を単一の CSV レポートに変換の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。