我发现我如果不是更新系列博客(例如django做网站系列),就总会在千面加一个事件的起因。格式之固定简直像极了小学生写作文。
所以……事情的起因是这样的,一位朋友是做财务相关的工作的。有一天向我叙述了一下其中一项工作内容,即打开发票,将信息录入excel,并同步对发票扫描件进行重命名。作为一个热心的python爱好者,自然挺身而出,帮助其解决这个问题。
我的思路
自己调用库
这个小项目的思路可以说是很easy了,无非就是,打开图片,ocr识别,然后进行一系列操作。
说到ocr,我很自然而然的联想到python的两个库
- PIL
PIL的功能就是对图片进行一系列操作,如打开,锐化、改变大小、旋转等等,功能丰富。 - tesseract
它是一个强大的ocr识别库。
但是最终我没有用这个方案,一来我自己的tesseract没有经过机器学习,识别率低得可怕,二来自己调用的话确实挺麻烦,可能还得自己写个服务器、客户端,但是我急于交货(写完这段代码,我过1024屌丝节(屌丝节这个词是我一个程序员兄弟说的,那是他的自嘲,不过却是我的向往))
腾讯云
在这里我没有打广告,我为什么选择了腾讯云,因为我有很多腾讯云的产品了,再增加一个就是了。
而且它有相对完善的接口,文档也很清楚,识别率奇高,我只要做一个说得过去的APIcaller即可。同时把接口改写一下,封装到类里面去,面向对象的思维有很多好处,我这个脚本虽然是单进程单线程,但是我这个改写做出来之后呢,多线程其实问题也不大。
调用方法
代码
代码其实比较简单的,需要这个思路的可以直接复制,我给用我自己不成熟的面向对象思维封装起来了。注释我写的详细一些。
依赖的库
- tencentcloud-sdk-python
主要使用的核心库,集中了最主要的命令 - PIL
负责打开图片,压缩图片,因为部分图片过大,请求起来比较慢 - base64
负责把图片转化为base64代码,方便解析 - json
腾讯云返回的信息是json格式,我要转回python的字典,才可以方便的操作
更详细的内容,见代码注释
#安装tencentcloud-sdk-python库之后方可调用
from tencentcloud.common import credential
from tencentcloud.common.profile.client_profile import ClientProfile
from tencentcloud.common.profile.http_profile import HttpProfile
from tencentcloud.common.exception.tencent_cloud_sdk_exception import TencentCloudSDKException
from tencentcloud.ocr.v20181119 import ocr_client, models
#导入PIL的Image,打开图片,压缩图片用
from PIL import Image
#获取目录下的文件,以及改名必备
import os
#图片转base64,必备
import base64
#腾讯云返回的信息是json格式,我要转回python的字典,才可以方便的操作
import json
import time
#面向对象思维,创建类
class recognition:
def __init__(self):
#requestId和key是腾讯云开通后申请的,我这里保护下隐私,隐藏起来
self.requestId = "AKIDiBNIFx8aU7JE8n**************"
self.key = "VllY9VbR1OUJtDH**************"
#必备参数
self.Action = "ap-beijing"
#这个方法是把图片转化为base64的方法,并添加类的属性
def getBase64(self, name):
with open(name, "rb") as f:
self.base64_data = base64.b64encode(f.read()).decode()
#核心部分,图片解析,根据腾讯云的文档来做即可
def getInformation(self):
try:
cred = credential.Credential(self.requestId, self.key)
httpProfile = HttpProfile()
httpProfile.endpoint = "ocr.tencentcloudapi.com"
clientProfile = ClientProfile()
clientProfile.httpProfile = httpProfile
client = ocr_client.OcrClient(cred, "ap-beijing", clientProfile)
req = models.VatInvoiceOCRRequest()
dic = {"ImageBase64": self.base64_data}
params = json.dumps(dic)
req.from_json_string(params)
self.resp = client.VatInvoiceOCR(req)
except TencentCloudSDKException as err:
print(err)
#解析返回的数据,这个是根据返回的json来做的,他的格式是一个字典列表,即元素是字典的列表,需要遍历,获得想要的发票数据
def analysis(self):
self.data = json.loads(self.resp.to_json_string())
self.fatherName = self.data['VatInvoiceInfos'][6]['Value']
for i in self.data['VatInvoiceInfos']:
if i['Name'] == "小写金额":
self.value = i['Value']
self.money = self.value.replace("¥", "")
break
#方法调用,这里可以开多线程其实,封装在函数里
def start(self, name):
self.getBase64(name)
self.getInformation()
self.analysis()
os.rename(name, self.fatherName + "-" + self.money + ".jpg")
print(self.data)
print(self.fatherName)
print(self.money)
#主函数部分应该不用解释了,很easy的方法。
if __name__ == "__main__":
nameList = os.listdir()
nameList.remove("main.py")
for name in nameList:
sImg = Image.open(name)
w, h = sImg.size
dImg = sImg.resize((int(w/2), int(h/2)), Image.ANTIALIAS)
dImg.save(name)
try:
re = recognition()
re.start(name)
except AttributeError:
print(name + "错误")
总结
这个活没啥难度,主要是我进行了面向对象的改写。朋友们有想让我帮忙识别发票的,我可以来哦。
github
连接在此:
github上托管的代码地址