我可以使用 python 're' 来解析复杂的人名吗?

Posted

技术标签:

【中文标题】我可以使用 python \'re\' 来解析复杂的人名吗?【英文标题】:Can I use python 're' to parse complex human names?我可以使用 python 're' 来解析复杂的人名吗? 【发布时间】:2015-04-28 00:37:41 【问题描述】:

所以我的主要痛点之一是名称理解和拼凑家喻户晓的名字和头衔。我有一个 80% 的解决方案,其中包含我今天早上整理的一个非常庞大的正则表达式,我可能不应该为它感到自豪(但无论如何我都以一种病态的方式)正确匹配以下示例:

John Jeffries
John Jeffries, M.D.
John Jeffries, MD
John Jeffries and Jim Smith
John and Jim Jeffries
John Jeffries & Jennifer Wilkes-Smith, DDS, MD
John Jeffries, CPA & Jennifer Wilkes-Smith, DDS, MD
John Jeffries, C.P.A & Jennifer Wilkes-Smith, DDS, MD
John Jeffries, C.P.A., MD & Jennifer Wilkes-Smith, DDS, MD
John Jeffries M.D. and Jennifer Holmes CPA
John Jeffries M.D. & Jennifer Holmes CPA

正则表达式匹配器如下所示:

(?P<first_name>\S*\s*)?(?!and\s|&\s)(?P<last_name>[\w-]*\s*)(?P<titles1>,?\s*(?!and\s|&\s)[\w\.]*,*\s*(?!and\s|&\s)[\w\.]*)?(?P<connector>\sand\s|\s*&*\s*)?(?!and\s|&\s)(?P<first_name2>\S*\s*)(?P<last_name2>[\w-]*\s*)?(?P<titles2>,?\s*[\w\.]*,*\s*[\w\.]*)?

(对吧?)

为方便起见:http://www.pyregex.com/

所以,例如:

'John Jeffries, C.P.A., MD & Jennifer Wilkes-Smith, DDS, MD'

正则表达式生成的组字典如下所示:

connector: &
first_name: John
first_name2: Jennifer
last_name: Jeffries
last_name2: Wilkes-Smith
titles1: , C.P.A., MD
titles2: , DDS, MD

在理解可能的中间名的最后一步,我需要帮助。

示例包括:

'John Jimmy Jeffries, C.P.A., MD & Jennifer Wilkes-Smith, DDS, MD'
'John Jeffries, C.P.A., MD & Jennifer Jenny Wilkes-Smith, DDS, MD'

这可能吗?有没有更好的方法在没有机器学习的情况下做到这一点?也许我可以使用nameparser(在我进入正则表达式兔子洞后发现)来确定是否有多个名称?以上内容符合我 99.9% 的案例,所以我觉得值得完成。

TLDR:我不知道是否可以使用某种前瞻或后瞻来确保可能的中间名仅在以下情况下匹配 后面有一个姓氏。

注意:我不需要解析像 Mr. Mrs. Ms. 这样的头衔,但我想可以像中间名一样添加。

解决方案说明:首先,听从 Richard 的建议,不要这样做。其次,如有必要,调查 NLTK 或使用/贡献给nameparser 以获得更强大的解决方案。

【问题讨论】:

Python 的Natural Language Toolkit (NLTK) 非常更适合这项任务。看看这个:timmcnamara.co.nz/post/2650550090/… @CurtisMattoon 哦。看起来不错。我正在破解这个正则表达式,以便“立即”获取一些数据,但 NLTK 看起来是一个很好的长期解决方案(也可能是短期解决方案)。我是一名初级 python 开发人员,所以我不知道所有的解决方案。 即使您的任务可能有更好的工具,您也可以使用re.VERBOSE 标志使您当前的正则表达式更具可读性。 docs.python.org/3/library/re.html#re.VERBOSE 另外,您可以像对待字符串一样对待您的模式。例如。 r'%s' % 'cat'. 在继续之前,请阅读:kalzumeus.com/2010/06/17/… 【参考方案1】:

像这样的正则表达式是黑暗者的作品。

谁在稍后查看您的代码时能够理解发生了什么?你会吗?

您将如何测试所有可能的边缘情况?

您为什么选择使用正则表达式?如果您正在使用的工具很难使用,这表明也许另一种工具会更好。

试试这个:

import re

examples = [
  "John Jeffries",
  "John Jeffries, M.D.",
  "John Jeffries, MD",
  "John Jeffries and Jim Smith",
  "John and Jim Jeffries",
  "John Jeffries & Jennifer Wilkes-Smith, DDS, MD",
  "John Jeffries, CPA & Jennifer Wilkes-Smith, DDS, MD",
  "John Jeffries, C.P.A & Jennifer Wilkes-Smith, DDS, MD",
  "John Jeffries, C.P.A., MD & Jennifer Wilkes-Smith, DDS, MD",
  "John Jeffries M.D. and Jennifer Holmes CPA",
  "John Jeffries M.D. & Jennifer Holmes CPA",
  'John Jimmy Jeffries, C.P.A., MD & Jennifer Wilkes-Smith, DDS, MD',
  'John Jeffries, C.P.A., MD & Jennifer Jenny Wilkes-Smith, DDS, MD'
]

def IsTitle(inp):
  return re.match('^([A-Z]\.?)+$',inp.strip())

def ParseName(name):
  #Titles are separated from each other and from names with ","
  #We don't need these, so we remove them
  name = name.replace(',',' ') 
  #Split name and titles on spaces, combining adjacent spaces
  name = name.split()
  #Build an output object
  ret_name = "first":None, "middle":None, "last":None, "titles":[]
  #First string is always a first name
  ret_name['first'] = name[0]
  if len(name)>2: #John Johnson Smith/PhD
    if IsTitle(name[2]): #John Smith PhD
      ret_name['last']   = name[1]
      ret_name['titles'] = name[2:]
    else:                #John Johnson Smith, PhD, MD
      ret_name['middle'] = name[1]
      ret_name['last']   = name[2]
      ret_name['titles'] = name[3:]
  elif len(name) == 2:   #John Johnson
    ret_name['last'] = name[1]
  return ret_name

def CombineNames(inp):
  if not inp[0]['last']:
    inp[0]['last'] = inp[1]['last']

def ParseString(inp):
  inp = inp.replace("&","and")    #Names are combined with "&" or "and"
  inp = re.split("\s+and\s+",inp) #Split names apart
  inp = map(ParseName,inp)
  CombineNames(inp)
  return inp

for e in examples:
  print e
  print ParseString(e)

输出:

John Jeffries
['middle': None, 'titles': [], 'last': 'Jeffries', 'first': 'John']
John Jeffries, M.D.
['middle': None, 'titles': ['M.D.'], 'last': 'Jeffries', 'first': 'John']
John Jeffries, MD
['middle': None, 'titles': ['MD'], 'last': 'Jeffries', 'first': 'John']
John Jeffries and Jim Smith
['middle': None, 'titles': [], 'last': 'Jeffries', 'first': 'John', 'middle': None, 'titles': [], 'last': 'Smith', 'first': 'Jim']
John and Jim Jeffries
['middle': None, 'titles': [], 'last': 'Jeffries', 'first': 'John', 'middle': None, 'titles': [], 'last': 'Jeffries', 'first': 'Jim']
John Jeffries & Jennifer Wilkes-Smith, DDS, MD
['middle': None, 'titles': [], 'last': 'Jeffries', 'first': 'John', 'middle': None, 'titles': ['DDS', 'MD'], 'last': 'Wilkes-Smith', 'first': 'Jennifer']
John Jeffries, CPA & Jennifer Wilkes-Smith, DDS, MD
['middle': None, 'titles': ['CPA'], 'last': 'Jeffries', 'first': 'John', 'middle': None, 'titles': ['DDS', 'MD'], 'last': 'Wilkes-Smith', 'first': 'Jennifer']
John Jeffries, C.P.A & Jennifer Wilkes-Smith, DDS, MD
['middle': None, 'titles': ['C.P.A'], 'last': 'Jeffries', 'first': 'John', 'middle': None, 'titles': ['DDS', 'MD'], 'last': 'Wilkes-Smith', 'first': 'Jennifer']
John Jeffries, C.P.A., MD & Jennifer Wilkes-Smith, DDS, MD
['middle': None, 'titles': ['C.P.A.', 'MD'], 'last': 'Jeffries', 'first': 'John', 'middle': None, 'titles': ['DDS', 'MD'], 'last': 'Wilkes-Smith', 'first': 'Jennifer']
John Jeffries M.D. and Jennifer Holmes CPA
['middle': None, 'titles': ['M.D.'], 'last': 'Jeffries', 'first': 'John', 'middle': None, 'titles': ['CPA'], 'last': 'Holmes', 'first': 'Jennifer']
John Jeffries M.D. & Jennifer Holmes CPA
['middle': None, 'titles': ['M.D.'], 'last': 'Jeffries', 'first': 'John', 'middle': None, 'titles': ['CPA'], 'last': 'Holmes', 'first': 'Jennifer']
John Jimmy Jeffries, C.P.A., MD & Jennifer Wilkes-Smith, DDS, MD
['middle': 'Jimmy', 'titles': ['C.P.A.', 'MD'], 'last': 'Jeffries', 'first': 'John', 'middle': None, 'titles': ['DDS', 'MD'], 'last': 'Wilkes-Smith', 'first': 'Jennifer']
John Jeffries, C.P.A., MD & Jennifer Jenny Wilkes-Smith, DDS, MD
['middle': None, 'titles': ['C.P.A.', 'MD'], 'last': 'Jeffries', 'first': 'John', 'middle': 'Jenny', 'titles': ['DDS', 'MD'], 'last': 'Wilkes-Smith', 'first': 'Jennifer']

这花了不到 15 分钟,在每个阶段,逻辑清晰,程序可以分段调试。虽然单行代码很可爱,但应优先考虑清晰性和可测试性。

【讨论】:

我后悔使用了黑魔法!我希望这可以帮助其他人避免同样的情况。我会接受这个作为解决方案,并添加一个注释,将人们指向 NLTK 和 nameparser lib。

以上是关于我可以使用 python 're' 来解析复杂的人名吗?的主要内容,如果未能解决你的问题,请参考以下文章

Python爬虫——数据解析

python正则表达式解析(re)

将html数据解析成python列表进行操作

Python正则re.S,re.I等作用

爬虫解析Re 之(六 ) --- Re模块

Python正则表达式解析流