繁体   English   中英

在Python中解析非结构化文本

[英]Parsing unstructured text in Python

我想解析包含非结构化文本的文本文件。 我需要获取地址,出生日期,姓名,性别和身份证明。

. 55 MORILLO ZONE VIII,
BARANGAY ZONE VIII
(POB.), LUISIANA, LAGROS
F
01/16/1952
ALOMO, TERESITA CABALLES
3412-00000-A1652TCA2
12    
. 22 FABRICANTE ST. ZONE
VIII LUISIANA LAGROS,
BARANGAY ZONE VIII
(POB.), LUISIANA, LAGROS
M
10/14/1967
AMURAO, CALIXTO MANALO13

在上面的例子中,前3行是地址,只有“F”的行是性别,DOB是“F”后面的行,DOB后面的名字,名称后面的ID和no 。 12下的ID是索引/记录号。

但是,格式不一致。 在第二组中,地址是4行而不是3而索引/记录号。 在名称后附加(如果此人没有ID字段)。

我想将文本重写为以下格式:

name, ID, address, sex, DOB

这是对pyparsing解决方案的第一次尝试(在pyparsing pastebin上易于复制的代码 )。 根据交错的评论,浏览单独的部分。

data = """\
. 55 MORILLO ZONE VIII,
BARANGAY ZONE VIII
(POB.), LUISIANA, LAGROS
F
01/16/1952
ALOMO, TERESITA CABALLES
3412-00000-A1652TCA2
12
. 22 FABRICANTE ST. ZONE
VIII LUISIANA LAGROS,
BARANGAY ZONE VIII
(POB.), LUISIANA, LAGROS
M
10/14/1967
AMURAO, CALIXTO MANALO13
"""

from pyparsing import LineEnd, oneOf, Word, nums, Combine, restOfLine, \
    alphanums, Suppress, empty, originalTextFor, OneOrMore, alphas, \
    Group, ZeroOrMore

NL = LineEnd().suppress()
gender = oneOf("M F")
integer = Word(nums)
date = Combine(integer + '/' + integer + '/' + integer)

# define the simple line definitions
gender_line = gender("sex") + NL
dob_line = date("DOB") + NL
name_line = restOfLine("name") + NL
id_line = Word(alphanums+"-")("ID") + NL
recnum_line = integer("recnum") + NL

# define forms of address lines
first_addr_line = Suppress('.') + empty + restOfLine + NL
# a subsequent address line is any line that is not a gender definition
subsq_addr_line = ~(gender_line) + restOfLine + NL

# a line with a name and a recnum combined, if there is no ID
name_recnum_line = originalTextFor(OneOrMore(Word(alphas+',')))("name") + \
    integer("recnum") + NL

# defining the form of an overall record, either with or without an ID
record = Group((first_addr_line + ZeroOrMore(subsq_addr_line))("address") + 
    gender_line + 
    dob_line +
    ((name_line +
        id_line + 
        recnum_line) |
      name_recnum_line))

# parse data
records = OneOrMore(record).parseString(data)

# output the desired results (note that address is actually a list of lines)
for rec in records:
    if rec.ID:
        print "%(name)s, %(ID)s, %(address)s, %(sex)s, %(DOB)s" % rec
    else:
        print "%(name)s, , %(address)s, %(sex)s, %(DOB)s" % rec
print

# how to access the individual fields of the parsed record
for rec in records:
    print rec.dump()
    print rec.name, 'is', rec.sex
    print

打印:

ALOMO, TERESITA CABALLES, 3412-00000-A1652TCA2, ['55 MORILLO ZONE VIII,', 'BARANGAY ZONE VIII', '(POB.), LUISIANA, LAGROS'], F, 01/16/1952
AMURAO, CALIXTO MANALO, , ['22 FABRICANTE ST. ZONE', 'VIII LUISIANA LAGROS,', 'BARANGAY ZONE VIII', '(POB.), LUISIANA, LAGROS'], M, 10/14/1967

['55 MORILLO ZONE VIII,', 'BARANGAY ZONE VIII', '(POB.), LUISIANA, LAGROS', 'F', '01/16/1952', 'ALOMO, TERESITA CABALLES', '3412-00000-A1652TCA2', '12']
- DOB: 01/16/1952
- ID: 3412-00000-A1652TCA2
- address: ['55 MORILLO ZONE VIII,', 'BARANGAY ZONE VIII', '(POB.), LUISIANA, LAGROS']
- name: ALOMO, TERESITA CABALLES
- recnum: 12
- sex: F
ALOMO, TERESITA CABALLES is F

['22 FABRICANTE ST. ZONE', 'VIII LUISIANA LAGROS,', 'BARANGAY ZONE VIII', '(POB.), LUISIANA, LAGROS', 'M', '10/14/1967', 'AMURAO, CALIXTO MANALO', '13']
- DOB: 10/14/1967
- address: ['22 FABRICANTE ST. ZONE', 'VIII LUISIANA LAGROS,', 'BARANGAY ZONE VIII', '(POB.), LUISIANA, LAGROS']
- name: AMURAO, CALIXTO MANALO
- recnum: 13
- sex: M
AMURAO, CALIXTO MANALO is M

你必须利用文本所具有的任何规律性和结构。

我建议你一次读一行并将它与正则表达式匹配以确定其类型,填写人物对象中的相应字段。 每当你得到一个你已填写的字段时,写出该对象并开始一个新对象。

这可能是矫枉过正,但针对此类问题的前沿机器学习算法基于条件随机场 例如, 使用条件随机场从研究论文中提取准确信息

有软件可以让这些模型相对容易。 请参阅MalletCRF ++

您可以使用正则表达式执行此操作而不会有太多困难。 如果您以前从未使用过它们,请查看python文档,然后启动redemo.py(在我的计算机上,它位于c:\\ python26 \\ Tools \\ scripts中)。

第一项任务是将平面文件拆分为实体列表(每个记录一个文本块)。 从您提供的文本片段中,您可以使用与行的开头匹配的模式拆分文件,其中第一个字符是点:

import re
re_entity_splitter = re.compile(r'^\.')

entities = re_entity_splitter.split(open(textfile).read())

请注意,必须对点进行转义(默认情况下,它是通配符)。 还要注意模式之前的r。 r表示“原始字符串”格式,这使您不必转义转义字符,从而导致所谓的“反斜杠瘟疫”。

将文件拆分为单个人后,选择性别和生日就很容易了。 使用这些:

re_gender     = re.compile(r'^[MF]')
re_birth_Date = re.compile(r'\d\d/\d\d/\d\d')

你离开了。 您可以将平面文件粘贴到重新演示GUI中,并尝试创建匹配所需的模式。 你马上就可以解析了。 一旦掌握了这一点,就可以使用符号组名称(请参阅文档)快速,干净地选择单个元素。

这是一个快速的黑客工作。

f = open('data.txt')

def process(file):
    address = ""

    for line in file:
        if line == '': raise StopIteration
        line = line.rstrip() # to ignore \n
        if line in ('M','F'):
            sex = line
            break
        else:
            address += line

    DOB = file.readline().rstrip() # to ignore \n
    name = file.readline().rstrip()

    if name[-1].isdigit():
        name = re.match(r'^([^\d]+)\d+', name).group(1)
        ID = None
    else:
        ID = file.readline().rstrip()
        file.readline() # ignore the record #

    print (name, ID, address, sex, DOB)

while True:
    process(f)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM