在上一篇上实现了线索功能模块,在实际使用中除了线索数据除了输入的结构化数据,也有可能是来自非结构化数据,如名片、PDF文档、语音视频等。为方便线索录入,本篇中将以名片为例,实现利用OCR等技术将名片信息自动转成结构化数据自动填充到线索的对应字段中进行存储的功能。
OCR,全程Optical Character Recognition,翻译过来是光学字符识别的意思。它是指对文本资料的图像文件进行分析识别处理,获取文字及版面信息的过程。简单来说就是将图像中的文字进行识别,并以文本的形式返回。
OCR处理过程可分为图像预处理、文本识别和后处理等步骤。
文字识别在传统技术上采用模板匹配的方式,但这种方式由于缺乏上下文信息识别正确率难以保证,现在多基于深度学习技术进行识别,比如RNN和LSTM等依赖于时序关系的神经网络是最理想的选择。
在本文中将使用pytesseract实现OCR功能。
pytesseract是对Tesseract的Python封装,pytesseract的安装命令如下:
$ pip install pytesseract
Tesseract(/'tesərækt/) 这个词的意思是"超立方体",Tesseract 已经有 30 年历史,开始它是惠普实验室的一款专利软件,然后在 2005 年开源,自 2006 年后由 Google 赞助进行后续的开发和维护。在现在的免费 OCR 引擎中,其识别精度也仍然是出类拔萃的。
安装完成后就可以在项目中使用了。
先在Django中新创建一个img2text的应用。命令如下:
$ python manage.py startapp img2text
执行完成后按照上一篇中的步骤将这个应用的结构重新整理下。
…进行下面的操作:
将目录移动到one_crm目录下面;
编辑leads下面的apps.py,将其中name改成one_crm.img2text;
将“one_crm.leads.apps.Img2TextConfig”添加到config/settings/base.py文件的LOCAL_APPS变量中,使这个模块生效。
接下来依次更新视图、模板和路由。
这里需要提交名片文件,所以选择一个表单视图来实现。先在forms.py文件中添加一个名为CardForm的表单类,内容如下:
class CardForm(forms.Form):
img = forms.FileField(label="请选择名片")
def parse_card(self):
pass
其中parse_card函数是用来解析名片的。解析过程是将名片用OCR将其中的文本解析出来,然后从解析的文本中抽取名字、电话和邮箱等信息,最后把这些信息保存到线索模型中。
抽取名字
这里抽取名字的方式比较简单,基本思路是将名片中提取的信息按行进行分割,然后每行文本就jieba进行分析判断词性,若词性是nr即为人名。实现如下:
def isname(single_word_string):
"""
判断是否是人名
"""
pair_word_list = pseg.lcut(single_word_string)
for _, cixing in pair_word_list:
if cixing == "nr":
return True
return False
def extract_name(s):
"""
提取人名
"""
name = "未知"
data = s.split("\n")
for i in data:
i = i.replace(" ", "")
if isname(i):
name = i
break
return name
在自然语言处理过程中,为了能更好地处理句子,往往需要把句子拆开分成一个一个的词语,这样能更好的分析句子的特性,这个过程叫做——分词。jieba是当前最好的 Python 中文分词库之一。
抽取电话
抽取电话的方式比较简单,就是用正则表达式进行提取(这里的正则没有对国际号,如+86进行处理),实现如下:
def extract_phone(s):
numbers = re.findall("(1\\d{2}-?\\d{4}-?\\d{4})", s)
if numbers:
return numbers[0]
else:
return ""
抽取邮箱
抽取邮箱的方式和电话类似,也是用正则表达式,实现如下:
def extract_email(s):
emails = re.findall(r"([a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+)", s)
if emails:
return emails[0]
else:
return ""
完成信息提取函数后更新parse_card函数如下:
def parse_card(self):
img = Image.open(self.cleaned_data["img"])
s = pytesseract.image_to_string(img, lang="chi_sim")
contact = extract_phone(s)
email = extract_email(s)
name = extract_name(s)
lead = Lead(
name=name,
contact=contact,
email=email,
description=s,
attachment=self.cleaned_data["img"],
)
lead.save()
return lead.pk
以上就完成了表单的实现,接着是更新视图views.py,实现将提交的名片进行解析,成功后跳转到该线索的更新页面进行人工确认,代码如下:
class CardFormView(FormView):
template_name = "img2text/card_form.html"
form_class = CardForm
def form_valid(self, form):
self.pk = form.parse_card()
return super().form_valid(form)
def get_success_url(self) -> str:
return reverse_lazy("leads:lead-update", kwargs={"pk": self.pk})
完成了视图后新建一个模板文件,在templates文件夹下创建一个名为img2text子文件夹,并新建一个card_form.html文件,内容如下:
{% block content %}
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ form|crispy }}
<div class="control-group">
<div class="controls">
<button type="submit" class="btn btn-primary btn-lg btn-block">提交button>
div>
div>
form>
{% endblock content %}
实现的功能就是一个提交文件的表单。
最后一步就是将上面实现的视图添加到项目的路由中,更新urls.py内容如下:
from django.urls import path
from .views import card_form_view
app_name = "img2text"
urlpatterns = [
path("", card_form_view, name="card"),
]
然后更新config文件夹下面的总路由配置文件urls.py即可。
比如这里提交一个名片:
将提取到“张叁”、“139-8888-6666”、“[email protected]”这几个关键信息,并把解析出来的内容保存到“描述”字段,效果如下:
完整的代码请参考:https://github.com/flingjie/one_crm