如何将地址解析为其各个组件?

时间:2014-04-25 17:25:10

标签: c# regex parsing html-agility-pack

我的任务是构建一个解析特定网页的解析器,以便我们的员工可以将他们的用户数据批量导入我们公司的网站。

我已经使用 HtmlAgilityPack 来解析页面,我已将table rowtable data与我Map类中的属性相关联

然而,一个专栏让我感到很悲伤。由于各种原因,地址列是我身边的荆棘。

示例数据:

6313 SW 203rd Ave <br> Portland, OR 97224
16600 Lomita Way <br> El Dorado Hills, CA 95762
PO Box #42 <br> Hampton Bays, NY 11946

这些地址中的每一个都是这样包装的(显然地址可能因我们导入用户的客户而异):

<tr>
     <td> 6313 SW 203rd Ave <br> Portland, OR 97224 </td>
</tr>

我正在尝试实现正则表达式将其拆分到适当的区域,因此可以将其分配给相关属性:

public string Unit { get; set; }
public string Street { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Zip { get; set; }

然而,这些地址并没有提供太多的东西:

问题一: 如果我锚定<br>,那么我只是将线分开。没有完全分裂成适当的部分。

第二期: 与单个逗号相同的问题。

第三期: 如果我锚定到数值,则Zip可能对加拿大无效,并且可能根据街道名称错误地拆分。

分隔地址项目的最佳方法是什么?使用Regex?

4 个答案:

答案 0 :(得分:5)

解析地址很难;真的很难。 地址没有真正统一的格式,特别是跨国界。 您使用单一RegEx进行此操作的可能性极小。

有关一些示例和更深入的解释,请参阅此其他帖子。 How to parse freeform street/postal address out of text, and into components

答案 1 :(得分:2)

正则表达式可以做什么是有限的,但是这里有一个假设您的地址始终遵循此格式的示例。如果您无法确保您的地址符合特定格式(由您的域强制执行),您将不得不依赖一些更复杂的解决方案,例如在其他答案中讨论的内容。

另请查看Parse usable Street Address, City, State, Zip from a string

编辑:对不起,我忘了这是一个C#问题......但你明白了。

var parseAddress = (function (rx) {
  return function parseAddress(html) { 
      var matches = html.match(rx);
      return {
          unit: matches[1],
          street: matches[2],
          city: matches[3],
          state: matches[4],
          zip: matches[5]
      };
  };
})(/^(\d*)\s*(.+?)\s*<br>\s*(.+?),\s*(.+?)\s*(\d+)$/);

parseAddress('6313 SW 203rd Ave <br> Portland, OR 97224');
//Object {unit: "6313", street: "SW 203rd Ave", city: "Portland", state: "OR", zip: "97224"}

答案 2 :(得分:0)

好的,所以解析Address字段非常痛苦。但是我确实设法根据我的特定要求解析数据。

  • Address总是在街道和广告之间有一个<br>。市。

所以我做了以下事情:

var splitBasedOnHTML = Regex.Split(column[2], @"\br<br>");

column[]包含index two中的地址。因此,在通话结束后,它会自动将我的单位和街道定位在Index Zero。 City,State和Zip将位于Index One

所以我做了另一次分手,打破了City,State和Zip这样:

var splitBasedOnSpace = splitBasedOnHtml[1].Split(' ');

之后我现在最终得到以下内容:

6313 SW 203rd Ave // splitBasedOnHtml[0]
Portland, // splitBasedonSpace[0]
OR // splitBasedOnSpace[1]
97224 // splitBasedOnSpace[2]

所以我只是将我的属性映射到那些单独的数组索引。

这个解决方案使假设该单位是街道的一部分,随着数据被导入另一个网站并且以后可以被特定的人修改,这成为一个好的牺牲。

这就是我解决解析问题的方法,这个解决方案对于这艘船上的其他人来说可能并不可行,但希望这是一个不错的选择或指向一个好的方向。方法是什么样的:

    public static Map AddressMapper(IList<string> column)
    {
        var map = new Map();
        var splitBasedOnHTML = Regex.Split(column[2], @"\b<br>");
        var splitBasedOnSpace = splitBasedOnHTML[1].Split(' ');

        map.Street = splitBasedOnHTML[0];
        map.City = splitBasedOnSpace[0].Replace(@",", " ");
        map.State = splitBasedOnSpace[1];
        map.Zip = spliteBasedOnSpace[2];

        return map;
    }

答案 3 :(得分:0)

如果您摆脱了html标签,就会有一个功能强大的开源库libpostal,非常适合此用例。有绑定到不同的编程语言。 Libpostal是一个C库,用于使用统计NLP和开放数据来解析/规范世界各地的街道地址。该项目的目标是在世界各地理解每种语言的基于位置的字符串。

我用Python绑定pypostal创建了一个简单的Docker映像,您可以将其剥离并轻松尝试pypostal-docker