【问题标题】:How to extract specific information from multi-line string如何从多行字符串中提取特定信息
【发布时间】:2019-05-08 13:23:40
【问题描述】:

我已经从电子邮件正文中提取了一些与发票相关的信息到 Python 字符串中,我的下一个任务是从字符串中提取发票编号。 电子邮件的格式可能会有所不同,因此很难从文本中找到发票编号。我还尝试了 SpaCy 的“命名实体识别”,但由于在大多数情况下,发票编号来自标题“发票”或“发票#”的下一行,因此 NER 不理解该关系并返回不正确的详细信息。

以下是从邮件正文中提取的两个文本示例:

示例 - 1.

Dear Customer:
The past due invoices listed below are still pending. This includes the 
following:

Invoice   Date     Purchase Order  Due Date  Balance
8754321   8/17/17  7200016508      9/16/18   140.72
5245344   11/7/17  4500199620      12/7/18   301.54

We would appreciate quick payment of these invoices.

示例 - 2.

Hi - please confirm the status of below two invoices.

Invoice#               Amount               Invoice Date       Due Date          
7651234                $19,579.06          29-Jan-19           28-Apr-19            
9872341                $47,137.20          27-Feb-19           26-Apr-19 

我的问题是,如果我将整个文本转换为单个字符串,则会变成这样:

Invoice   Date     Purchase Order  Due Date  Balance 8754321   8/17/17 
7200016508     9/16/18   140.72

可以看出,发票编号(在本例中为 8754321)改变了位置,不再跟随关键字“发票”,这更难找到。

我想要的输出是这样的:

Output Example - 1 - 

8754321
5245344

Output Example - 2 - 

7651234                
9872341        

我不知道如何检索关键字“发票”或“发票#”下的文本,即发票编号。

如果需要更多信息,请告诉我。谢谢!!

编辑:发票号码没有任何预定义的长度,它可以是 7 位或更多位。

【问题讨论】:

  • 你试过regex吗?是否所有发票编号都是 7 位数字且没有其他数据与此匹配?熊猫也可能是替代品
  • 抱歉,我已经编辑了这个问题,在给定的 2 个示例中,它是一个 7 位数字,但实际上它可以有 7 位以上的数字。邮件正文上还有其他数字,例如 PO 编号、客户 ID、帐号。等
  • 蛮力将遍历行,使用查找字符串方法查找“发票”的索引,然后为后续行获取从该索引开始的数字(正则表达式或拆分行拼接)。需要小心额外的“发票”。如果您可以隔离表格,(它们总是用行空间分隔吗?)然后将表格喂给熊猫或类似的东西可能会更容易。如果文本被编辑,你能反馈到 spacy(不熟悉这个)吗?如果是这样,可以用上面的关键字加上空格替换每个数字,然后再输入数字并再次输入 NER。
  • 另一个可能的启发是,它看起来像列标题行总是驼峰式或大写字母 (ID)。可以对此进行测试以及是否包含“发票”以获取表格起始行

标签: python information-extraction


【解决方案1】:

根据我的 cmets 编写代码。

email = '''Dear Customer:
The past due invoices listed below are still pending. This includes the 
following:

Invoice   Date     Purchase Order  Due Date  Balance
8754321   8/17/17  7200016508      9/16/18   140.72
5245344   11/7/17  4500199620      12/7/18   301.54

We would appreciate quick payment of these invoices.'''

index = -1
# Get first line of table, print line and index of 'Invoice'
for line in email.split('\n'):
    if all(x != x.lower() for x in line.split()) and ('Invoice' in line) and len(line) > 0:
        print('--->', line, ' --- index of Invoice:', line.find('Invoice'))
        index = line.find('Invoice')

使用启发式方法,列标题行始终是驼峰式大小写或大写 (ID)。如果说标题正好是“帐号”,这将失败。而不是“帐号”。

# get all number at a certain index
for line in email.split('\n'):
     words = line[index:].split()
     if words == []: continue
     word = words[0]
     try:
         print(int(word))
     except:
         continue

这里的可靠性取决于数据。所以在我的代码中发票列必须是表头的第一个。即您不能在“发票”之前有“发票日期”。显然这需要修复。

【讨论】:

  • 太棒了!!,这对我有用,虽然我知道我需要确保表头的一致性,但它适用于我一半以上的数据,谢谢,感谢您的帮助! !
【解决方案2】:

只要这两个假设是正确的,就不要说Andrew Allen 所说的:

  1. 发票号码始终是 7 位数字
  2. 发票编号总是跟在一个空格之后,再跟一个空格

使用正则表达式应该可以。类似的东西;

import re

email = '''Dear Customer:
The past due invoices listed below are still pending. This includes the 
following:

Invoice   Date     Purchase Order  Due Date  Balance
8754321   8/17/17  7200016508      9/16/18   140.72
5245344   11/7/17  4500199620      12/7/18   301.54

We would appreciate quick payment of these invoices.'''

invoices = re.findall(r'\s(\d\d\d\d\d\d\d)\s', email)

invoice 在这种情况下有一个包含 2 个字符串的列表,['8754321', '5245344']

【讨论】:

  • 谢谢,我已经编辑了这个问题,在给定的 2 个示例中,它是一个 7 位数字,但实际上它可以有 7 位以上的数字。邮件正文上还有其他数字,例如 PO 编号、客户 ID、帐号。等
【解决方案3】:

使用正则表达式。 re.findall

例如:

import re

email = '''Dear Customer:
The past due invoices listed below are still pending. This includes the 
following:

Invoice   Date     Purchase Order  Due Date  Balance
8754321   8/17/17  7200016508      9/16/18   140.72
5245344   11/7/17  4500199620      12/7/18   301.54

We would appreciate quick payment of these invoices.'''

email2 = """Hi - please confirm the status of below two invoices.

Invoice#               Amount               Invoice Date       Due Date          
7651234                $19,579.06          29-Jan-19           28-Apr-19            
9872341                $47,137.20          27-Feb-19           26-Apr-19 """

for eml in [email, email2]:
    print(re.findall(r"\b\d{7}\b", eml, flags=re.DOTALL))

输出:

['8754321', '5245344']
['7651234', '9872341']
  • \b - 正则表达式边界
  • \d{7} - 获取 7 位数字

【讨论】:

  • 谢谢,我已经编辑了这个问题,在给定的 2 个示例中,它是一个 7 位数字,但实际上它可以有 7 位以上的数字。邮件正文上还有其他数字,例如 PO 编号、客户 ID、帐号。等
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多