如何parsing自由forms的街道/邮政地址的文本和组件

我们主要在美国开展业务,并试图通过将所有地址字段组合到单个文本区域来改善用户体验。 但是有一些问题:

  • 用户input的地址可能不正确,或者是标准格式
  • 地址必须分成几部分(街道,城市,州等)来处理信用卡付款
  • 用户可以input的不仅仅是他们的地址(如他们的名字或公司)
  • 谷歌可以做到这一点,但服务条款和查询限制是禁止的,特别是在预算紧张的情况下

显然,这是一个常见的问题:

  • PHP脚本parsing地址?
  • 如何parsing自由格式地址以保存到数据库中
  • Java邮政地址parsing器
  • 更有效的方式来提取地址组件
  • 我怎样才能显示联系人屏幕上的街道,城市,在Android上的邮编pre-populated邮政地址
  • PHP的正则expression式美国地址

有没有办法从周围的文本中分离出一个地址并将其分解? 有一个正则expression式来parsing地址?

当我在地址validation公司工作时,我看到了很多这个问题。 我在这里发布的答案,使更多的程序员正在search周围的同一个问题。 我当时正在处理数十亿个地址,我们在这个过程中学到了很多东西。

首先,我们需要了解一些有关地址的东西。

地址不规则

这意味着正则expression式已经出来了。 我已经看到了这一切,从简单的正则expression式匹配一个非常具体的格式地址到这个:

/ \ S +(\ d {2,5} \ S +)([A | P]?!米\ b)中(([A-ZA-Z | \ S +] {1,5}){1,2}) ?([\ S | \,|。] +)(([A-ZA-Z | \ S +] {1,30}){1,4})(法院|克拉|街道| ST |驱动|博士|车道| LN |道路| RD | BLVD)([\ S | \,| | \;] +)(([A-ZA-Z | \ S +] {1,30}){1,2} )([\ S | \,|。] +)\ b(AK?| AL | AR | AZ | CA | CO | CT | DC | DE | FL | GA | GU | HI | IA | ID | IL | IN | KS |肯塔基州| LA | MA | MD | ME | MI | MN | MO | MS | MT |数控| ND | NE | NH |新泽西州| NM | NV |纽约州| OH | OK |和| PA | RI | SC | SD | TN | TX | UT | VA | VI | VT | WA | WI | WV | WY)([\ S | \,|。]?+)(\ S + \ d {5})([\ S | \,|。] +)/ I

…到这个地方,一个900多个线条类的文件在飞行中产生超大规模的正则expression式来匹配更多。 我不推荐这些(例如, 这是上面的正则expression式的小提琴,这使得大量的错误 )。 没有一个简单的魔术公式来得到这个工作。 在理论上和理论上,用正则expression式来匹配地址是不可能的。

USPS出版物28logging了所有可能的地址格式,以及它们的所有关键字和variables。 最糟糕的是,地址往往含糊不清。 单词可能意味着不止一个东西(“圣”可以是“圣”或“街”),并且有我敢肯定他们发明的话。 (谁知道“街道”是街道后缀?)

你需要一些真正了解地址的代码,如果代码确实存在,这是一个商业秘密。 但是如果你真的了解了,你可以推出自己的产品。

地址有意想不到的形状和大小

以下是一些人为的(但是完整的)地址:

1) 102 main street Anytown, state 2) 400n 600e #2, 52173 3) po #104 60203 

即使这些可能是有效的:

 4) 829 LKSDFJlkjsdflkjsdljf Bkpw 12345 5) 205 1105 14 90210 

显然,这些都不是标准化的。 标点符号和换行符不保证。 以下是发生了什么事情:

  1. 号码1是完整的,因为它包含街道地址和城市和州。 有了这些信息,就足以识别地址,并且可以将其视为“可交付”(有一些标准化)。

  2. 号码2是完整的,因为它还包含一个街道地址(带有辅助/单元号码)和一个5位的邮政编码,足以识别一个地址。

  3. 号码3是一个完整的邮局框格式,因为它包含一个邮政编码。

  4. 第4号也是完整的,因为邮政编码是唯一的 ,这意味着私人实体或公司已经购买了该地址空间。 一个独特的邮政编码是为大批量或集中交付空间。 任何写给邮政编码12345的东西都送到纽约州斯克内克塔迪的通用电气公司。 这个例子不会特别到达任何人,但是USPS仍然能够传递它。

  5. 5号也是完整的,信不信由你。 使用这些数字,可以在对所有可能地址的数据库进行分析时发现完整地址。 当你将每个数字看作一个组件时,填补缺less的方向,辅助指示符和ZIP + 4代码是微不足道的。 这是它的样子,完全扩展和标准化:

205 N 1105 W Apt 14

比佛利山庄CA 90210-5221

地址数据不是你自己的

在大多数向授权供应商提供官方地址数据的国家,地址数据本身属于pipe理机构。 在美国,USPS拥有这些地址。 加拿大邮政,皇家邮政和其他国家也是如此,不过每个国家的所有权都有所不同。 知道这一点很重要,因为它通常禁止反向devise地址数据库。 您必须小心如何获取,存储和使用数据。

Google地图是快速修复地址的常用工具,但TOS相当令人望而却步; 例如,您不能在不显示Google Map的情况下使用他们的数据或API,并且仅用于非商业目的(除非您支付),并且您不能存储数据(临时caching除外)。 说得通。 Google的数据是世界上最好的。 不过,Google地图不会validation地址。 如果一个地址不存在,它仍然会告诉你,如果地址是真的存在的话(在你自己的街道上试一试,使用一个你不知道的房屋号码)。 有时候这很有用,但要注意这一点。

Nominatim的使用政策也同样受到限制,特别是对于大量使用和商业使用,数据主要来自免费来源,因此不能很好地维护(这是开放项目的性质) – 但是,这仍然适用你的需要。 它得到了一个伟大的社区的支持。

美国邮政本身有一个API,但是这个 API 下降很多 ,没有保证也没有支持。 这可能也很难使用。 有些人使用它没有问题。 但是很容易错过USPS要求您仅使用他们的API来确认通过它们发送的地址。

人们希望地址很难

不幸的是,我们限制了我们的社会,期望地址变得复杂。 整个互联网上有好几十篇关于这方面的优秀的用户体验文章,但事实是,如果你有一个单独的字段的地址表单,这是用户期望的,即使这使得难以处理的边缘地址不符合格式的forms是期待,或者forms可能需要一个领域,它不应该。 或者用户不知道把他们地址的某个部分放在哪里。

我现在可以继续讨论结账表单糟糕的用户体验,但是我只想说将这些地址合并到一个单一字段将是一个值得欢迎的改变 – 人们将能够input他们的地址,而不是试图找出你冗长的forms。 然而,这种变化将是意想不到的 ,用户可能会觉得起初有点震惊。 只要知道这一点。

在这个地址之前,把这个国家领域放在前面,可以减轻这种痛苦的一部分。 当他们首先填写国家领域时,你知道如何让你的表格出现。 也许你有一个很好的方法来处理美国单一字段的地址,所以如果他们select美国,你可以减less你的表单到一个字段,否则显示组件字段。 只是想想!

现在我们知道为什么很难 你能做些什么呢?

USPS通过一个名为CASS™authentication的stream程授权供应商为客户提供经过validation的地址。 这些供应商可以访问USPS数据库,每月更新一次。 他们的软件必须符合严格的标准才能获得authentication,而且他们通常不需要同意上述这样的限制条款。

有许多CASSauthentication的公司可以处理列表或API:Melissa Data,Experian QAS和SmartyStreets等等。

(由于“广告”的声望越来越大,我已经截断了我的答案,这取决于你是否find适合你的解决scheme。)

真相:真的,伙计们,我不在这些公司工作。 这不是一个广告。

有很多街道地址parsing器。 他们有两种基本口味 – 一种是地名和街道名称数据库,另一种是没有。

正则expression式街道地址parsing器可以达到约95%的成功率,而不会有太多的麻烦。 然后你开始打不寻常的情况。 CPAN中的Perl“Geo :: StreetAddress :: US”就是这样的。 有Python和Javascript的端口,所有的开源。 我有一个改进的Python版本,通过处理更多的案例,稍微提高了成功率。 为了获得最后3%的权利,你需要数据库来帮助消除歧义。

具有3位邮政编码和美国州名和缩写的数据库是一大帮助。 当parsing器看到一致的邮政编码和州名称时,可以开始locking格式。 这对美国和英国来说效果很好。

正确的街道地址parsing从结尾开始并向后工作。 这就是美国邮政系统如何做的。 最后地址是最不明确的,国名,城市名称和邮政编码相对容易识别。 街道名称通常可以被隔离。 街道上的地点是最复杂的parsing; 在那里你会遇到诸如“五楼”和“Staples Pavillion”之类的东西。 这是一个数据库是一个很大的帮助。

我已经构build了一个地址parsing系统,它将input的文本作为input并从中提取地址。 我使用的数据来自openaddresses.io (在一个开放的许可下)。
API在这里: geocode.xyz (目前只适用于西class牙)

例如:

input: 我需要一个地方停留在西class牙El Prat de Llobregat 08820 Plaza Volateria 3 Parque de Negocios Mas Blau附近几天

输出:

 <geodata> <latt>41.3189957000</latt> <longt>2.0746469000</longt> <standard> <stnumber>3</stnumber> <staddress>VOLATERIA Plaza</staddress> <city>EL PRAT DE LLOBREGAT</city> <prov>ES</prov> <confidence>0.8</confidence> </standard> </geodata> 

(它可能有点慢,它运行在只有1G内存和一个CPU的Amazon Micro实例上,如果这对你来说太慢,请使用提供的AMI来获得自己的服务器。

对于美国,墨西哥和加拿大,请参阅geocoder.ca 。

例如:

input: 在主要和亚瑟交界处附近发生的事情杀死纽约

输出:

 <geodata> <latt>40.5123510000</latt> <longt>-74.2500500000</longt> <AreaCode>347,718</AreaCode> <TimeZone>America/New_York</TimeZone> <standard> <street1>main</street1> <street2>arthur kill</street2> <stnumber/> <staddress/> <city>STATEN ISLAND</city> <prov>NY</prov> <postal>11385</postal> <confidence>0.9</confidence> </standard> </geodata> 

您也可以在Web界面中查看结果,或以Json或Jsonp格式输出。 例如。 我正在纽约123大街附近找餐馆

如果你想依靠OSM数据, libpostal是非常强大的,并处理了很多地址input最常见的警告。

在我们的一个项目中,我们使用了下面的地址parsing器。 它精确地parsing了世界上大多数国家的地址。

http://address-parser.net/

它可以作为独立的库或作为一个活的API。