前言🥴

公司打车需要报销,需要发票与行程单一一对应并打印,行程少手动问题不大,行程多起来,这个就耗费大量摸鱼时间,所以要想办法解决掉该问题。

常规步骤

- 前往高德找到对应行程开发票

- 邮箱找到邮件(每笔打车对应一个邮件),打开,分别点击下载行程单和发票两个附件

- 将发票和行程单规整,使得一一对应,找到PDF免费合并的在线网站进行合并打印

思考🧐

其实核心就是怎么才能自动化的下载,规整,以及合并。

实现🤪

  • 通过获取邮箱授权码读取邮箱内容

  • 将未读的邮件进行筛选

  • 读取邮件内容进行附件提取

  • 合并PDF

  • 将邮件标记为已读

源码🤩

使用前提一定要保证发票邮件是未读的

import email
import imaplib
import os
from email.header import decode_header

from PyPDF2 import PdfMerger

# 连接到 QQ 邮箱的 IMAP 服务器
imap_server = "imap.qq.com"
user = "xxxx@qq.com"
password = "xxxxx"

# 查找特定主题的邮件
target_subject = '高德打车电子发票'

# 登录到邮箱
mail = imaplib.IMAP4_SSL(imap_server)
mail.login(user, password)
mail.select("inbox")


# 搜索邮件
def save_attachment(part):
    # 检查是否有Content-Disposition且为attachment
    if part.get("Content-Disposition") is None:
        return
    disp = part.get("Content-Disposition").split(";")
    if disp[0].lower() != "attachment":
        return

    filename = part.get_filename()
    if not filename:
        return

    # 解码文件名
    filename = decode_header(filename)[0][0]
    if isinstance(filename, bytes):
        filename = filename.decode()

    # 解码并保存附件
    attachment_data = part.get_payload(decode=True)
    filepath = os.path.join("./attachments", filename)
    with open(filepath, "wb") as f:
        f.write(attachment_data)
    print(f"附件已下载到:{filepath}")
    merger.append(filepath)


def search_mail():
    # 使用SEARCH命令找到所有未读邮件
    status, messages = mail.search(None, '(UNSEEN)')
    # 邮件ID列表
    id_list = messages[0].split() if messages[0] else []

    # 遍历每一封邮件
    for email_id in id_list:
        status, msg_raw = mail.fetch(email_id, "(RFC822)")
        for response_part in msg_raw:
            if isinstance(response_part, tuple):
                msg = email.message_from_bytes(response_part[1])
                # 解码邮件主题
                subject_header = decode_header(msg["subject"])
                if subject_header:
                    subject, encoding = subject_header[0]
                    if isinstance(subject, bytes):
                        subject = subject.decode(encoding or 'utf-8')
                    # 检查邮件主题是否包含目标主题
                    if target_subject in subject:
                        # 遍历邮件的所有部分,查找附件
                        for part in msg.walk():
                            save_attachment(part)
                        # 标记为已读
                        mail.store(email_id, '+FLAGS', '\Seen')


if __name__ == '__main__':
    merger = PdfMerger()
    search_mail()
    # 关闭连接
    mail.close()
    mail.logout()
    if len(merger.pages) == 0:
        print("没有找到附件")
        exit(0)
    merger.write("merged.pdf")
    merger.close()
    print("所有附件已合并为 merged.pdf")

效果😎

运行后,所有的发票和行程单都下载在附件下

打开merged.pdf,就是合并后的效果,且能发票行程一一对应

打印时选择一张多页即可