首页 > 后端开发 > Python教程 > 周末编码:将 PDF 工资单转换为单个 CSV 报告

周末编码:将 PDF 工资单转换为单个 CSV 报告

Susan Sarandon
发布: 2024-12-25 22:20:17
原创
120 人浏览过

曾经使用 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 的代码,包括决定报告中需要哪些字段。这是您需要调整以匹配您的工资单结构的部分。一旦弄清楚,我们将迭代整个工资单目录。

在第三步中,我选择在 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 并打印所有行开始。这样我们就会知道每一行中出现的内容。这只需要完成一次(而报告可能会每月或每年创建一次),并且它不是报告的一部分 - 因此我们将在单独的文件中创建它。

首先创建一个新的 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 循环中节省几毫秒。然后,我需要区分 列表项

和其他行。

我注意到在整个文件中,列表项是唯一符合此规则的项目:以字母字符 开头, 以数字字符 结尾包含空格(最后一个条件是过滤掉my
中错误的行工资单,你可能不需要它)。

<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() 的原因。现在该行的其余部分以非空格字符开头。

并非所有列表项都有代码,因此我们要检查该行是否以代码或数字开头。如果它是代码 - 我将其包装在 () 中(包括左括号前的空格),并将其附加到描述字符串。如果没有 - 不添加任何内容。

金额:

如果有代码——我们就会有更多的空间可以删除。如果没有,我们的行可能包含两个金额:每月金额和余额。

我注意到了 4 个案例:

/payslips_pdf
pdf_rows.txt
report.json
report.csv
登录后复制
登录后复制

提取类别和代码后,我们剩下:

PENSION     G   150.00   587.49
登录后复制

为了涵盖情况 2-3,我们将找到分隔金额的空格索引并剪掉尾部。它也适用于第一种情况,即没有空格(也称为没有尾部)。

为了涵盖案例 4,我依赖于行中具有单个金额的两种类型类别之间的差异:第一种类型就像工资 - 我们要在其中保存金额,第二种类型就像预扣税——我们想忽略它。不同之处在于,只有扣除额会记录表中的年度余额 - 所以我正在检查 -.

总而言之,这就是它的样子:

写入 JSON 文件


这不是强制性步骤 - 我们可以使用 JSON 对象而不导出值。我更喜欢看到它的样子,至少在编码阶段是这样。

缩放至多个 PDF 文件

此步骤获得专用部分的唯一原因是因为将 pdf_to_dict 包装在 for 循环中会带来令人不快的惊喜。为了演示它,我创建了一个名为 iterate_over_pdfs() 的函数:
<script></script> <script></script> <script></script>
发生这种情况是因为文件列表是按字母顺序排序的,因此 10 出现在 2 之前。按时间顺序排列报告条目可以被认为是至关重要的,而不仅仅是一个可有可无的功能。因此,我们需要修复它!

原本,我以为我必须重命名文件(Payslip1.pdf -> Payslip01.pdf),但有一个更好的解决方案:


一旦我们按长度对文件名列表进行排序,10 将出现在 2 之后。在最后一行,我对名称进行了解码以去掉 b''默认结构。

创建 CSV 报告

由于付款和扣除项目可能因工资单而异,因此本节不仅仅是直接翻译。 CSV 是一个关系数据集,这意味着我们需要提前了解付款和扣除的所有类别,并在不存在工资单的情况下将条目保留为空。另一方面,JSON 是非关系型的,每个条目都指定其键。

考虑到这一点,我们的 CSV 报告的第一步是收集类别。所有类别。

收集类别:


现在,乍一看,您可能会认为使用 Set 来实现这一点 - 因为我们希望所有类别只出现一次。我已经尝试过了。问题是套装未列出,我发现匹配原始工资单中出现的项目顺序很重要。使用列表时,不要忘记在追加之前检查列表中是否存在该项目:


现在我们已经弄清楚了:还记得之前我们说过我们不关心哪个项目列表是付款、哪个是扣除吗?好吧,我们现在很关心!我们不必

必须
分开,但我希望工资单报告将所有付款放在右侧,所有扣除放在左侧,而不是混合。

虽然每张工资单可能有不同的清单项目 - 有些将永远存在(因为你总是会缴纳税款;))。我们可以利用这一点为我们带来好处——并将 PAYE 标记为扣除的开始! (我很确定 PAYE 仅适用于爱尔兰,因此您需要更改它以匹配您的工资单)

最后,我返回一个列表,因为将付款与扣除分开是没有用的 - 分开是为了确保付款将显示在右侧,扣除将显示在左侧。

填充 CSV 表:


现在我们有了类别,我们可以开始填充 CSV 表:

每张工资单都是一行,每行都有以逗号分隔的特定顺序的字段。我发现将字段组织在列表中然后将它们连接起来更容易。出现在类别中但未出现在工资单中的字段 — 将保留为空:


<script></script> 最后,我们将写入 CSV 文件:<script></script> <script></script> <script></script> <script></script>
之后,您将获得一份包含所有工资单的精美 CSV 报告!

您可以通过下载 VS 扩展 RainbowCSV(或任何其他 IDE 的并行版本)来使其更易于阅读

删除 .json 文件的使用

一旦我们知道一切正常,我们就不需要写入和读取 JSON 文件 - 我们可以直接使用 JSON 对象:

我们将直接使用 json_payslips,而不是使用 json_object(如 json_object = json.dumps(json_payslips)):

  1. 写入:无需写入report.json - 我们可以删除此部分。

  2. 阅读:将 json_payslips 直接传递给 json_to_csv() 函数:

获取员工的姓名作为参数

一旦您准备好脚本 - 您就会想与您的同事和朋友分享!为了获得良好的用户体验,我们将从命令行导出员工姓名,而不是要求他们打开代码。

阅读论证

我们将从快乐路径开始 - 假设用户输入员工姓名 - 并添加使用它的代码:

在 pdf_to_dict() 中,我们将从参数中读取它:employee_name = sys.argv[1],而不是硬编码 EMPLOYEE_NAME = "IFAT NEUMANN"。不要忘记导入 sys!

现在我们会考虑其他场景:

未提供员工姓名

如果用户没有输入任何员工姓名怎么办?我们希望尽快抓住它,并通知他们!

因此,我们将在 main 函数的第一行添加一个检查。现在,直觉是在那里初始化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>
请注意,我将employee_name = sys.argv[1] 移至此函数,因为它更易于阅读(而不是如果sys.argv[1] 不在文本中)。之后,我将其传递给 get_fixed_values()。

最后,我们在主函数中捕获错误:


总而言之,不要忘记使用新说明更新您的自述文件。

完整脚本


以下是您可用于处理工资单的完整代码:


Weekend Coding: Turn PDF Payslips Into a Single CSV Report
https://cupofcode.blog/
希望你喜欢这个!对另一个周末编码项目感兴趣吗?查看我的自动电子邮件发送博客文章!

写博客是我的爱好,所以我很乐意在上面花费时间和金钱。如果您喜欢这篇博文,请在我的小费罐里放入 1 欧元即可让我知道:) 感谢您的支持! <script></script> <script></script>

以上是周末编码:将 PDF 工资单转换为单个 CSV 报告的详细内容。更多信息请关注PHP中文网其他相关文章!

来源:dev.to
本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
作者最新文章
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板