使用正则表达式在PHP中提取字符串中的地址

3

我的问题

我试图爬取美国众议院网站上的各个链接,以查找所有列出人员的华盛顿地址。问题是,华盛顿地址的格式时而变化。有时会有项目符号、管道符号、换行和断行标记,这使得匹配变得困难。


我试图爬取许多页面以检索大致相似的地址:

忽略奇怪的空格。这只是为了显示字符串部分的相似之处

    1433 Longworth House Office Building Washington,  D.C. 20515
     332 Cannon HOB                      Washington   DC   20515
    1641 LONGWORTH HOUSE OFFICE BUILDING WASHINGTON,  DC   20515
    1238 Cannon H.O.B. (line return)
    Washington, DC 20515
    8293 Longworth House Office Building • Washington DC • 20515
    8293 Longworth House Office Building | Washington DC | 20515

每个地址都将单独返回,并被大量其他文本和 HTML 标签所包围。地址甚至可能在地址本身中包含 <br> 或 <br/>。

我想要做的是从源字符串中捕获第一个匹配项,并将其设置为变量的值。据我所知,这最好通过正则表达式来实现。

更新:

在了解到这些地址可能出现的各种方式后,我决定采用一种不那么严格的表达式。这些地址出现时会有项目符号、管道符号和换行符。也许一种传达以下内容的表达式会更好:

[数字][任何字符]["华盛顿"][任何字符][DC|D.C.][任何字符][五个数字]

显然,这太宽松了。当我只想允许一些字符的任意内容时,“任何内容”块会带来段落。

到目前为止,我未能成功匹配以下网站上找到的地址(这只是众多网站中的几个)


由于每个人的地址格式完全不同,这变得很困难。我认为最好的方法是先从输入中删除所有的HTML标签,然后应用下面我回答中提到的正则表达式。那样应该效果更好。我不懂PHP,所以无法告诉你如何删除HTML标签,但这肯定在SO上已经有人回答过了。 - undefined
当然不是一个答案,但以下的阅读可能会引起你的兴趣,因为你正在第一手经历地址问题:http://www.mjt.me.uk/posts/falsehoods-programmers-believe-about-addresses/ - undefined
5个回答

2
编辑:看起来在第一组数字和“华盛顿”之间的 [anything] 数据需要更加严格才能正常工作。[anything] 部分不应包含任何数字,因为数字是我们用来分隔地址的起始位置的符号。这适用于您提供的三个网站。
我建议的最佳第一步是剥离所有 HTML 标签并替换 ' ' 字符实体。
$input = strip_tags($input);
$input = preg_replace("/&nbsp;/"," ",$input);

如果地址与您指定的格式匹配(接近),则执行以下操作:
$results= array();
preg_match("/[0-9]+\s+[^0-9]*?\s+washington,?\s*D\.?C\.?[^0-9]+[0-9]{5}/si",$input,$results);
foreach($result[0] as $addr){
    echo "$addr<br/>";
}

对于您提供的三个示例,这将起作用,$results[0] 应包含找到的每个地址。

但是,如果地址中有“公寓2号”等内容,则此方法将不起作用,因为它假定最接近“华盛顿特区”的数字标记了地址的开头。

以下脚本匹配每个测试用例:

<?php
    $input = "
        1433&nbsp;Longworth House Office Building Washington,  D.C. 20515
         332 Cannon HOB                      Washington   DC   20515
        1641 LONGWORTH HOUSE OFFICE BUILDING WASHINGTON,  DC   20515
        1238 Cannon H.O.B.
        Washington, DC 20515
        8293 Longworth House Office Building • Washington DC • 20515
        8293 Longworth House Office Building | Washington DC | 20515
    ";
    $input = strip_tags($input);
    $input = preg_replace("/&nbsp;/"," ",$input);

    $results= array();
    preg_match_all("/[0-9]+\s+[^0-9]*?washington,?\s*D\.?C\.?[^0-9]*?[0-9]{5}/si",$input,$results);
    foreach($results[0] as $addr){
        echo "$addr<br/>";
    }

不必要将整个正则表达式用括号括起来。它会被捕获在$matches[0]中。 - undefined
我已经更新了原始问题,请查看更改内容。 - undefined

1

编辑:

看了你提到的网站后,我认为以下方法应该可行。假设你已经将爬取到的页面内容存储在一个名为$page的变量中,那么你可以使用以下代码:

$subject = strip_tags($page)

去除页面上的所有HTML标记;然后应用正则表达式

(\d+)\s*(.*?)\s*washington.{0,5}(DC|D.C.).{0,5}(\d{5})

RegexBuddy 为此生成以下代码(我不知道 PHP):

if (preg_match('/(\d+)\s*(.*?)\s*washington.{0,5}(DC|D.C.).{0,5}(\d{5})/si', $subject, $regs)) {
    $result = $regs[0];
} else {
    $result = "";
}

$regs [1] 将包含第一个捕获括号(数字)的内容,以此类推。

请注意使用/si修饰符使点与换行匹配,并使正则表达式不区分大小写。


差不多了,但是这些“任何东西”可能应该限制在5个字符以内。现在,这个正则表达式会将符合[任何东西]块条件的段落引入进来。不过这是我的错,因为我表达得太含糊了。 - undefined
没问题,只需将.*?替换为.{0,5} - 我已相应地编辑了我的回答。 - undefined
以下似乎不再匹配地址:/(\d+).{1,5}washington.{1,5}(DC|D.C.).{1,5}(\d{5})/si - undefined
啊是的,在你的示例中,第一个 "anything" 包含了比 5 个字符多得多的内容:LONGWORTH HOUSE OFFICE BUILDING,例如。因此,我将它改回了 .*?。如果你需要捕获这里的文本,那么用括号括起来,像 (.*?) 这样。 - undefined
糟糕,说得对。这仍然不匹配 http://giffords.house.gov 上找到的地址。目前我有: /(\d+).{1,35}\swashington.{1,5}(DC|D.C.).{1,5}(\d{5})/si - undefined

1

这个正则表达式对输入字符串的内容有更灵活的处理方式。其中“Washington,DC”部分没有被硬编码到其中。地址的不同部分是分别捕获的,整个地址将被捕获在$matches[0]中。

$input = strip_tags($input);
preg_match('/
(\d++)    # Number (one or more digits) -> $matches[1]
\s++      # Whitespace
([^,]++), # Building + City (everything up until a comma) -> $matches[2]
\s++      # Whitespace
(\S++)    # "DC" part (anything but whitespace) -> $matches[3]
\s++      # Whitespace
(\d++)    # Number (one or more digits) -> $matches[4]
/x', $input, $matches);

这个很接近,但它假设总会有一个逗号。请重新评估原问题中列出的各种格式。 - undefined

1

有一些工具和API可以完成这个任务。例如,一个非常好用的工具是SmartyStreets的LiveAddress。我参与了它的开发,所以我能理解你的痛苦... 这是你在问题中提供的样例输出:

enter image description here

这是CSV输出:

ID,Start,End,Segment,Verified,Candidate,Firm,FirstLine,SecondLine,LastLine,City,State,ZIPCode,County,DpvFootnotes,DeliveryPointBarcode,Active,Vacant,CMRA,MatchCode,Latitude,Longitude,Precision,RDI,RecordType,BuildingDefaultIndicator,CongressionalDistrict,Footnotes
1,4,69,"1433&nbsp;Longworth House Office Building Washington, D.C. 20515",Y,0,,1433 Longworth House Office Building Washington D,,Washington DC 20515-0001,Washington,DC,20515,District of Columbia,AAU1,205150001330,,,,Y,38.89106,-77.01132,Zip5,Residential,S,,AL,Q#X#
2,75,134,332 Cannon HOB Washington DC 20515,Y,0,,332 Cannon Hob,,Washington DC 20515-3226,Washington,DC,20515,District of Columbia,AAU1,205153226996,,,,Y,38.89106,-77.01132,Zip5,Residential,H,Y,AL,H#Q#
3,139,199,"1641 LONGWORTH HOUSE OFFICE BUILDING WASHINGTON, DC 20515",Y,0,,1641 Longworth House Office Building,,Washington DC 20515-0001,Washington,DC,20515,District of Columbia,AAU1,205150001411,,,,Y,38.89106,-77.01132,Zip5,Residential,S,,AL,Q#X#
4,204,247,"1238 Cannon H.O.B.
Washington, DC 20515",Y,0,,1238 Cannon H O B,,Washington DC 20515-0001,Washington,DC,20515,District of Columbia,AAU1,205150001385,,,,Y,38.89106,-77.01132,Zip5,Residential,S,,AL,Q#X#
5,252,316,8293 Longworth House Office Building • Washington DC • 20515,Y,0,,8293 Longworth House Office Building,,Washington DC 20515-0001,Washington,DC,20515,District of Columbia,AAU1,205150001934,,,,Y,38.89106,-77.01132,Zip5,Residential,S,,AL,Q#X#
6,321,381,8293 Longworth House Office Building | Washington DC | 20515,Y,0,,8293 Longworth House Office Building,,Washington DC 20515-0001,Washington,DC,20515,District of Columbia,AAU1,205150001934,,,,Y,38.89106,-77.01132,Zip5,Residential,S,,AL,Q#X#

大约需要2秒钟。这个API在一定程度上是免费的,可能还有其他类似的选择;我鼓励您四处寻找最适合您的选项......我保证它会比编写自己的正则表达式好(提示:这个代码背后不是基于正则表达式的)。

有人知道有没有PHP类,或者其他免费的替代方案可以取代LiveAddress吗?LA效果很好,但对我正在进行的项目来说成本太高了。 - undefined
1
@pXdty 嗯...你需要它用于注册的非盈利用途吗?如果是的话,你可以免费获得无限制的LiveAddress。否则,我会保持关注,并在找到相应库时告诉你。 - undefined
1
@pXdty 你能稍微解释一下你正在进行的项目吗?这可能有助于筛选出可能的解决方案。总结一下,听起来你想找一个能够解析数据源以查找、纠正和验证地址(使用USPS最新数据)的服务,并且你希望这个服务非常快速、高度准确 - 同时也要具有侵略性,并且同时花费很少或者几乎没有费用。我对你的需求做了准确的总结吗? - undefined
@Jeffrey:我们正在构建一个用于验证本地搜索列表的工具,将免费使用。是的,我们希望能够解析包含地址的大型字符串,并向用户呈现有效的地址。我目前正在使用我自己编写的一些非常不完善的方法来实现这一点,但它并不像liveaddress那样可靠。 - undefined
@Matt:我们正在为一个非盈利机构编写这个。不确定他们是否注册,但我可以查一下。我们需要展示什么来获得无限制的访问权限? - undefined
1
@pXdty 只需使用此表格注册或联系SmartyStreets即可。如果您有更多问题,可以直接与SS联系(这是离题的内容)。您只需提供一个链接和/或告诉其他人相关信息即可。 - undefined

0
你的问题对我来说不太清楚,但如果我理解正确的话,我猜你可以使用DOM解析器来匹配p标签,然后检查其中是否有包含"Washington"这个词或者电话号码是否与华盛顿地区相匹配。

源代码不一定都有p标签。从我所了解的情况来看,这必须基于正则表达式。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接