制作初衷:

  • 外地开了票到公司后发现信息有错误,无法报销;
  • 公司的行政和财务经常在工作日被问及公司开票信息,影响心情和工作;
  • 引入相应的专业APP来解决发票问题对于一般公司成本较高;
  • 看到朋友孟要早睡写过脚本来解决这个问题,但因为公司场景不相同,无法复用,所以新写了一个。

本代码使用简单的封装方法,并做了比较走心的注释,希望能给初学Python的小伙伴提供一些灵感,也能让有实际需求的人可以快速修改、使用。

源码地址:https://github.com/yc2code/WechatInvoiceParser

P.S. 工具基于微信网页版,因为微信官方对于账号有限制,新建的账号可能无法使用,会报:KeyError: 'pass_ticket',如图:
所以工具只能使用注册时间较早的账号

发票自动校核微信机器人代码部分

1. 工具文件 – Utils
包含三个部分:发票校核类 Invoice、解析数据类 DataParser 和推送日志类 Pushover

  • Invoice 调用的百度API,上传图片信息,得到解析数据;
  • DataParser 对得到的解析数据进行整理,得到发送给用户的信息;
  • Pushover 出现调用问题时,第一时间相关信息推送到维护者的设备上。
# -*- coding: utf-8 -*-
# Utils.py
import base64
import csv
import os
import time
import requests
from Config import configclass Invoice:"""发票识别类使用百度发票识别API,免费使用官方地址 https://ai.baidu.com/docs#/OCR-API/5099e085其它功能及配置请移步官网"""@staticmethoddef get_pic_content(image_path):"""方法--打开图片以二进制格式打开"""with open(image_path, 'rb') as pic:return pic.read()@staticmethoddef parse_invoice(image_binary):"""方法--识别图片调用百度接口,返回识别后的发票数据以下内容基本根据API调用的要求所写,无需纠结各类报错码在官网文档可查百度API注册及使用教程:http://ai.baidu.com/forum/topic/show/867951"""# 识别质量可选high及normal# normal(默认配置)对应普通精度模型,识别速度较快,在四要素的准确率上和high模型保持一致,# high对应高精度识别模型,相应的时延会增加,因为超时导致失败的情况也会增加(错误码282000)access_token = "你的access_token"api_url = f"https://aip.baidubce.com/rest/2.0/ocr/v1/vat_invoice?access_token={access_token}"quality = "high"header = {"Content-Type": "application/x-www-form-urlencoded"}# 图像数据,base64编码后进行urlencode,要求base64编码和urlencode后大小不超过4M,# 最短边至少15px,最长边最大4096px,支持jpg/jpeg/png/bmp格式image_data = base64.b64encode(image_binary)try:data = {"accuracy": quality, "image": image_data}response = requests.post(api_url, data=data, headers=header)if response.status_code != 200:print(time.ctime()[:-5], "Failed to get info")return Noneelse:result = response.json()["words_result"]invoice_data = {'检索日期': '-'.join(time.ctime().split()[1:3]),'发票代码': result['InvoiceCode'],'发票号码': result['InvoiceNum'],'开票日期': result['InvoiceDate'],'合计金额': result['TotalAmount'],'价税合计': result['AmountInFiguers'],'销售方名称': result['SellerName'],'销售方税号': result['SellerRegisterNum'],'购方名称': result['PurchaserName'],'购方税号': result['PurchaserRegisterNum'],"发票类型": result["InvoiceType"]}return invoice_dataexcept:message = "发票识别API调用出现错误"Pushover.push_message(message)return Nonefinally:print(time.ctime()[:-5], "产生一次了调用")@staticmethoddef save_to_csv(invoice_data):"""方法--日志保存将识别记录写入文件夹下work_log.csv文件若无此文件则自动创建并写入表头"""if "work_log.csv" not in os.listdir():not_found = Trueelse:not_found = Falsewith open('./work_log.csv', 'a+') as file:writer = csv.writer(file)if not_found:writer.writerow(invoice_data.keys())writer.writerow(invoice_data.values())@staticmethoddef run(image_path):"""主方法解析完成返回信息,否则返回None"""image_binary = Invoice.get_pic_content(image_path)invoice_data = Invoice.parse_invoice(image_binary)if invoice_data:Invoice.save_to_csv(invoice_data)return invoice_datareturn Noneclass DataParser:"""数据分析类对识别返回后的数据进行整理,并于默认信息对比,查看有无错误这里只简单实现整理信息和检查名称和税号的方法,有兴趣可以增加其他丰富的方法"""def __init__(self, invoice_data):self.invoice_data = invoice_datadef get_detail_message(self):"""对得到的发票信息的格式进行整理:return: 返回整理好的发票信息"""values = [value for value in self.invoice_data.values()]detail_mess = f"完整信息为:" \f"\n发票代码: {values[1]}\n发票号码: {values[2]}\n开票日期: {values[3]}" \f"\n合计金额: {values[4]}\n价税合计: {values[5]}\n销售方名称: {values[6]}" \f"\n销售方税号: {values[7]}\n购方名称: {values[8]}\n购方税号:{values[9]}"return detail_messdef get_brief_message(self):"""将信息中的名称和税号和默认值进行对比只做对错判断,读者丰富一下可以增加指出错误位置的信息:return: 返回判断的信息"""if self.invoice_data["购方名称"] == config["company_name"]:brief_mess = "购方名称正确"else:brief_mess = "!购方名称错误!"if self.invoice_data["购方税号"] == config["company_tax_number"]:brief_mess += "\n购方税号正确"else:brief_mess += "\n!购方税号错误!"return brief_messdef parse(self):brief_mess = self.get_brief_message()detail_mess = self.get_detail_message()return brief_mess, detail_messclass Pushover:"""消息推送类本次使用Pushover为推送消息软件(30 RMB,永久,推荐)官网 https://pushover.net/可以向微信一样把相关信息推送至不同设备如果不需要可以把相关代码注释掉"""@staticmethoddef push_message(message):message += ">>>来自Python发票校验"try:requests.post("https://api.pushover.net/1/messages.json", data={"token": "你的Token","user": "你的User","message": message})except Exception as e:print(time.ctime()[:-5], "Pushover failed", e, sep="\n>>>>>>>>>>\n")

2. 微信机器人文件 – Wechat
包含一个部分:微信处理类 Wechat
作用是初始化机器人,对微信的消息进行处理,分析并作出回应。

# -*- coding: utf-8 -*-
# Wechat.py
import os
from wxpy import *class Wechat:"""微信处理类对微信的消息进行处理,分析并作出回应"""def __init__(self, group_name, admin_name):self.bot = Bot()  # 类被实例化的时候即对机器人实例化self.group_name = group_name  # 指定群聊名self.admin_name = admin_name  # 管理员微信名self.received_mess_list = []  # 过滤后的消息列表self.order_list = []  # 管理命令列表self.pic_list = []  # 待解析图片绝对路径列表def get_group_mess(self):"""方法--获取消息获取所有正常消息,进行过滤后存进消息列表"""# 调用此方法时先清空上次调用时列表所存储的数据self.received_mess_list = []for message in self.bot.messages:# 如果为指定群聊或管理员的消息,存入group_messsender = message.sender.name# >>>这里有一点要注意,如果你是用一个微信作为机器人且作为管理员<<<# >>>然后用这个微信号在群聊发消息,则信息sender会之指向自己而不是群聊<<<# >>>建议使用单独一个微信号作为机器人if sender == self.group_name or sender == self.admin_name:self.received_mess_list.append(message)# 其他的消息过滤掉self.bot.messages.remove(message)return Nonedef parse_mess(self):"""方法--处理群聊消息过滤获得的指定群聊消息设定所有新增群聊图片的绝对路径及群聊中产生的文字命令"""# 调用此方法时先清空上次调用时列表所存储的数据self.pic_list = []self.order_list = []# self.group_order = []for message in self.received_mess_list:# 如果信息类型为图片,则保存图片并添加到图片列表if message.type == 'Picture' and message.file_name.split('.')[-1] != 'gif':self.pic_list.append(Wechat.save_file(message))# 如果消息类型为文字,则视为命令,保存到命令列表中if message.type == 'Text':self.order_list.append(message)return None@staticmethoddef save_file(image):"""方法--存储图片这里使用静态方法,是因为本方法和类没有内部交互,静态方法可以方便其他程序的调用解析名称,设定绝对路径,存储:param image: 接收到的图片(可以看成是wxpy产生的图片类,它具有方法和属性):return: 返回图片的绝对路径"""path = os.getcwd()# 如果路径下没有Pictures文件夹,则创建,以存放接收到的待识别图片if "Pictures" not in os.listdir():os.mkdir("Pictures")# 设定一个默认的图片格式后缀file_postfix = "png"try:# 尝试把图片的名称拆分,分别获取名称和后缀file_name, file_postfix = image.file_name.split('.')except Exception:# 当然有时候可能拆分不了,就把默认的后缀给它file_name = image.file_name# 赋予绝对路径file_path = path + '/Pictures/' + file_name + '.' + file_postfix# 将图片存储到指定路径下image.get_file(file_path)return file_pathdef send_group_mess(self, message):"""方法--发送群消息:param message: 需要发送的内容"""try:# 如果群聊名称被改变,搜索时会报错,如果找不到群聊,消息不会发送group = self.bot.groups().search(self.group_name)[0]group.send(message)except IndexError:print("找不到指定群聊,信息发送失败")return Nonedef send_parse_log(self):"""方法--发送查询日志向群聊内发送查询日志"""try:# 如果群聊名称被改变,搜索时会报错,如果找不到群聊,消息不会发送group = self.bot.groups().search(self.group_name)[0]except IndexError:print("找不到指定群聊,查询日志发送失败")return Nonetry:group.send_file("./work_log.csv")except:group.send("Oops, no log yet")return Nonedef send_system_log(self):"""方法--发送系统日志向群聊内发送查询日志"""try:# 如果群聊名称被改变,搜索时会报错,如果找不到群聊,消息不会发送group = self.bot.groups().search(self.group_name)[0]except IndexError:print("找不到指定群聊,系统日志发送失败")return Nonetry:group.send_file("./system_log.text")except:group.send("System log not found")return None

3. 主文件 – Main
包含一个main函数,一部分为发票识别和处理,另一部分对于指令做出反应。

# -*- coding: utf-8 -*-
# Main.py
import time
from Utils import Invoice, DataParser
from Config import config
from Wechat import *# Author  : 达希def main():"""主方法一部分为发票识别和处理,另一部分对于指令做出反应"""# 输出重定向,将print语句都写进系统日志文件file = open("./system_log.text", "a+")sys.stdout = file# 实例化微信机器人,传入群聊名和管理员名wechat = Wechat(config["group_name"], config["admin_name"])while True:time.sleep(1)wechat.get_group_mess()wechat.parse_mess()# 若群聊有要处理的图片,则迭代解析if wechat.pic_list:for pic in wechat.pic_list:invoice_data = Invoice.run(pic)if invoice_data:data_parser = DataParser(invoice_data)brief_mess, detail_mess = data_parser.parse()wechat.send_group_mess(detail_mess)  # 先发送发票识别详细信息time.sleep(0.5)wechat.send_group_mess(brief_mess)  # 返回名称和税号是否有错误else:wechat.send_group_mess("请求未成功,请重试或联系管理员")# 若有相关命令,则做出相应反应if wechat.order_list:for order in wechat.order_list:if "开票信息" in order.text:wechat.send_group_mess(config["company_name"])time.sleep(0.5)wechat.send_group_mess(config["company_tax_number"])elif "SEND LOG" in order.text:wechat.send_parse_log()elif "SEND SYSTEM LOG" in order.text:wechat.send_system_log()elif "BREAK" in order.text:wechat.send_group_mess("收到关机指令,正在关机")file.close()return Noneif __name__ == "__main__":main()

4. 配置文件 – Config
包含微信的配置文件信息

config = {"group_name": "发票校核ASAP",  # 校核群聊名称,由于本代码默认没有同名群聊,所以建议设为复杂值"admin_name": "达希",  # 管理员微信名(非备注)"company_name": "代码网络技术无限公司",  # 默认购方名称"company_tax_number": "XXX00000000000XXX"  # 默认购方税号
}


另外,代码在运行时会在同文件夹下创建一个Picture的文件夹,用于存储待解析的图片,会创建 work_log.csv 文件,用于存储识别信息的记录,还有 system_log.text 用于输出运行相应的日志。

由于本身需求较少,所以以上代码功能相对单薄,仅仅作为一个辅助的小脚本使用。若要进行优化完善,wxpy库提供了很多丰富的功能,可以在此基础上打造更加合理完善的,符合个性化需求的微信机器人。

水平有限,难免存在错误,还望指正。

Python制作发票自动校核微信机器人相关推荐

  1. 发票识别 python_Python实现发票自动校核微信机器人的方法

    外地开了票到公司后发现信息有错误,无法报销: 公司的行政和财务经常在工作日被问及公司开票信息,影响心情和工作: 引入相应的专业APP来解决发票问题对于一般公司成本较高: 看到朋友孟要早睡写过脚本来解决 ...

  2. python发微信提醒天气_基于Python实现定时自动给微信好友发送天气预报

    效果图 from wxpyimport * import requests from datetimeimport datetime import time from apscheduler.sche ...

  3. python发送文件给微信好友_基于Python实现定时自动给微信好友发送天气预报

    效果图 from wxpyimport * import requests from datetimeimport datetime import time from apscheduler.sche ...

  4. 用python写一个自动群发微信脚本

    使用 Python 写一个自动群发微信脚本需要使用微信第三方 API 来实现.推荐使用 itchat 库,它提供了简单易用的 API,可以方便地编写微信自动化脚本. 首先,你需要安装 itchat 库 ...

  5. 封装Python脚本:使用企业微信机器人发送消息至企业微信

    官方文档地址:https://developer.work.weixin.qq.com/document/path/91770#%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8 ...

  6. python、C# 写企业微信机器人推送【图文消息】

    企业微信机器人发送图文消息(基础版) 使用工具 进入代码模式 1. 引入 2. 发送方式 3. 发送到企业微信机器人步骤 3. 总代码 C#写法 看下效果图: 代码展示 使用工具 突然来兴趣搞了个机器 ...

  7. python窗口制作_利用Python制作属于自己的微信小客服

    由于要以微信作一个某学习网站的登陆途径,用小程序就又有一些额外的花费,所以想以微信聊天窗口做些文章,就在网上找了很多关于微信机器人的方法,大多数都是用的老旧并且已经失效的wxpy,没办法了,放弃吗? ...

  8. 微信公众号python人工智能回复_python实现微信机器人: 登录微信、消息接收、自动回复功能...

    安装wxpy pip install -u wxpy 登录微信 # 导入模块 from wxpy import * # 初始化机器人,扫码登陆 bot = bot() 运行以上代码,会生成一个二维码, ...

  9. 想去看演唱却总是抢不到票?教你用Python制作一个自动抢票脚本

    前言 嗨喽!大家好,这里是魔王!! 大麦网,是中国综合类现场娱乐票务营销平台,业务覆盖演唱会. 话剧.音乐剧.体育赛事等领域. 但是因为票数有限,还有黄牛们不能丢了饭碗,所以导致了,很多人都抢不到票 ...

最新文章

  1. 【精选】Nginx模块Lua-Nginx-Module学习笔记(一)Nginx Lua API 接口详解
  2. QXTEND QUERY SERVICE调试成功
  3. SQLite剖析之异步IO模式、共享缓存模式和解锁通知
  4. iphone(苹果)手机登陆Exchange 2013邮箱帐号的配置
  5. 并行语言开发平台 Erlang
  6. oracle字符数量,Oracle中统计表中每行字段符合条件的数量
  7. PHP方法的重载主要格式,PHP函数重载方法及技巧
  8. 使用DxVcl为Python的飞信库写一个简单的GUI
  9. MongoDB学习——介绍一款MongoDB连接管理工具
  10. excel限制只能输入身份证号
  11. java对齐_java字符串对齐方法
  12. Learning Enriched Features for Real Image Restoration and Enhancement Paddle模型复现93号(1)
  13. CAD高版本窗体阵列LISP_[转载]AutoCAD高版本怎么把阵列对话框调出来?
  14. 爬取热榜数据,通过Qt界面显示,代码可直接运行(python3经典编程案例)
  15. 第十七届全国大学智能车竞赛赛场合影集锦
  16. CheckForIllegalCrossThreadCalls = false
  17. 基于区域生长的图像分割算法!
  18. 【机器学习】python机器学习使用scikit-learn对模型进行评估:使用t分布及z分布评估模型误差的95%置信空间
  19. 站长收入差距逐渐拉开 高收入站长稳步增加
  20. js删除数组对象中的某个属性的方法

热门文章

  1. Be All That You Can Be(做你自己)
  2. SAP HANA是什么 和SAP Fiori 是什么
  3. 桌面只能进入计算机,Win7电脑开机无法正常启动只能进入安全模式怎么解决
  4. Windows 10 系统下载网址推荐
  5. VR入门-02(在场景中显示出手)
  6. 【虚拟终端工具】SecureCRT工具连接虚拟机、rz/sz传输、中文乱码问题解决
  7. HashMap面试题
  8. 了解GPIO端口的初始化设置三步骤LED流水灯C语言 寄存器方式编程实现
  9. 微信小程序金额输入限制
  10. ucos OS_ENTER_CRITICAL