让报销更轻松:自动拉取QQ邮箱发票解析保存

2024-09-02 21:18:21 8点赞 32收藏 5评论

背景:因为每个月有报销发票的需求,但每次到报销发票的时候,都需要去邮箱上一个个把发票下载下来,然后分类整理。这些都是耗时且重复的动作,就想着能不能把它自动化,同时看看能不能结合上最近大火的AI

目标:能够按要求自动下载发票文件,同时解析重命名发票文件

自动下载邮件中附件

这一步是很简单的啦,用Selenium就可以实现,我的是QQ邮箱。我们只需要打开浏览器按下F12一步步的查找从登录到进入邮件下载附件都需要点击哪些元素,然后用Selenium代替我们操作就行

登录

BASE_URL = 'https://wx.mail.qq.com/info/report' def begin(): global origin_window_handle driver.get(Constant.BASE_URL) origin_window_handle = driver.current_window_handle

这里登录就没有做的那么麻烦,自己扫码或者输入账号密码即可。

获取邮件

登录到QQ邮箱后,通过F12可以看到主要功能区是在iframe里面,因此我们要先点击收件箱然后切换到iframe中,否则没法找到元素

让报销更轻松:自动拉取QQ邮箱发票解析保存

image.png

def switch_to_frame(): recv_option = util.getDelayElement(By.PARTIAL_LINK_TEXT, "收件箱") recv_option.click() main_frame = util.getDelayElement(By.CSS_SELECTOR, "#mainFrame") driver.switch_to.frame(main_frame)

接着就获取邮件,每次只能获取当前页数的邮件,我这里是把未读和已读都获取到了

def get_mail_list(): return driver.find_elements_by_class_name("M") + driver.find_elements_by_class_name("F")

处理邮件

因为我只要处理发票的邮件,用了最简单的方式,只处理当前邮件标题是否包含发票两字,同时发票邮件包括两种,带有附件和不带有附件的,带有附件的是邮件里直接附上了发票文件。不带有附件的是邮件里给了一个链接,需要点击后才能下载或者跳转到其他网站下载。因为两种方式的处理方式不同,所以需要分开存储哪些是带有附件的哪些没带有附件。同时为了避免同名标题不同发票的情况,我们使用mailId来进行存储,后续通过mailId来定位每一个邮件

def handle_mail(): global end_flag invoice_list =[] mail_list = get_mail_list() mail_num =len(mail_list) print(f'mail_num:{mail_num}, page: {current_page}') for mail in mail_list: mailid = mail.find_element(By.CSS_SELECTOR,'td.tl.tf ').find_element(By.TAG_NAME,'nobr').get_attribute( 'mailid') title = mail.find_element_by_class_name("tt").text if'发票'in title: try: mail.find_element(By.CSS_SELECTOR,'div.cij.Ju') invoice_list.append({'title': title,'mailId': mailid}) exceptNoSuchElementExceptionas e: invoice_list.append({'title': title,'mailId': mailid}) print(f'-------发票: {len(invoice_list)}-------') for item in invoice_list: monitor.reset_create() mailId = item["mailId"] title = item["title"] tag = driver.find_element_by_xpath(f"//nobr[@mailid='{mailId}']") tag.click() time.sleep(1) if is_out_date(): end_flag =True break try: if exist_element(By.ID,'attachment'): download_attach() check_file(item) else: handle_no_attach() check_file(item) exceptExceptionas e: record_fail(item) switch_to_frame() time.sleep(3)

处理带有附件的邮件

附件可能会有多个附件,我们只需要下载PDF文件即可

让报销更轻松:自动拉取QQ邮箱发票解析保存

def download_attach(): attachment = util.getDelayElement(By.ID,'attachment') attach_items = attachment.find_elements(By.CSS_SELECTOR,'div.att_bt.attachitem') for attach in attach_items: util.getDelayElement(By.CSS_SELECTOR,'div.name_big') if'.pdf'in attach.find_element(By.CSS_SELECTOR,'div.name_big').find_element(By.TAG_NAME,'span').text: attach.find_element_by_partial_link_text('下载').click() break time.sleep(3) # driver.back() driver.refresh() switch_to_frame() time.sleep(4)

处理没有附件的邮件

这个才是本次的重点,对于没有附件只有下载链接的邮件,如何让selenium知道该点哪里。不同的邮件他们的展示也不同

让报销更轻松:自动拉取QQ邮箱发票解析保存让报销更轻松:自动拉取QQ邮箱发票解析保存

解决方法就是让AI来告诉selenium该点哪里,通过F12可以发现,邮件的内容都是在一个固定的Div里面

让报销更轻松:自动拉取QQ邮箱发票解析保存

那我们就可以获取这个Div里面的HTML片段,然后告诉AI,让它根据HTML片段解析出带有发票下载链接的标签文本,然后返回,selenium根据这个文本点击,以下是对于prompt

prompt = ''' 你是一名HTML解析助手,你需要解析用户上传的HTML片段。 1.解析出片段中带有发票下载链接的超链接标签文本。 2.如果有多个下载链接,则找出下载为PDF格式的超链接标签文本即可。 例如: 输入:

下载PDF文件: HelloWorld

输出: {"text":"HelloWorld"} 注意只需要返回对应的标签文本即可,不需要其他内容。结果输出为JSON:{'text':'xxx'}"} '''

点击过后,一般会有两种结果,一种是点击后能够直接下载发票,另一种是跳转到其他网站后,再点击下载才能下载

让报销更轻松:自动拉取QQ邮箱发票解析保存

针对这种我们就可以全文搜索带有下载字样的标签进行点击下载。经过上面这一套下来,基本90%的都能成功下载下来

解析整理发票

下载下来的发票命名各异

让报销更轻松:自动拉取QQ邮箱发票解析保存

image.png

我希望能够通过文件名就能知道该发票的类型,金额和开票日期,这里用上了OCR+AI,用OCR来解析发票文件,然后将解析结果送给AI让其根据结果分析该发票属于哪种类别。最后再重命名发票

def parse_invoice(): invoice_list = get_invoice_list() location = config.get_location() for invoice in invoice_list: file_path = os.path.join(location, invoice) invoice_info =OcrUtil.ocr_invoice(file_path) new_file_name = get_new_file_name(invoice_info) util.rename(invoice, new_file_name) print(f'{invoice} : {new_file_name}') defget_new_file_name(invoice_info): date = util.parse_date(invoice_info['Date'],'%Y年%m月%d日','%Y%m%d') summary = get_summary(invoice_info) return summary +'_'+ invoice_info['Number']+'_'+ invoice_info['Total'].split('.')[0]+'_'+ date defget_summary(item): if'VatElectronicItems'in item: info = json.loads(json.dumps(item['VatElectronicItems'])) else: info = json.loads(json.dumps(item['VatInvoiceItemInfos'])) return AI.ai_summary(info[0]['Name'])

最后就是这个样子

让报销更轻松:自动拉取QQ邮箱发票解析保存

在config.json文件设置文件下载地址和日期,日期的作用是在下载附件时只下载日期之后的附件,在OcrUtil.py文件里设置腾讯云的secret_id和secret_key,在AI.py文件里设置api_key后,然后运行main.py,等待浏览器拉起后,手动登录后即可

让报销更轻松:自动拉取QQ邮箱发票解析保存

目前只实现了下载和解析重命名,邮箱目前只支持QQ邮箱,AI目前只支持一个。

地址:https://github.com/WRCoding/autoEmail

作者声明本文无利益相关,欢迎值友理性交流,和谐讨论~

展开 收起
5评论

  • 精彩
  • 最新
  • QQ邮箱和微信发票自带集成,报销软件还支持从微信发票导入,已经很完善了

    校验提示文案

    提交
    emmm……有些公司吧……

    校验提示文案

    提交
    收起所有回复
  • 不懂代码,正在研究学习影刀

    校验提示文案

    提交
    准备打一个exe运行包

    校验提示文案

    提交
    收起所有回复
  • 有exe的包吗,正好有和你一样的需求

    校验提示文案

    提交
提示信息

取消
确认
评论举报

相关文章推荐

更多精彩文章
更多精彩文章
最新文章 热门文章
32
扫一下,分享更方便,购买更轻松