Analisar endereço residencial, cidade, estado, código postal de uma string

Problema: Eu tenho um campo de endereço de um database do Access que foi convertido para o Sql Server 2005. Esse campo tem tudo em um único campo. Eu preciso analisar as seções individuais do endereço em seus campos apropriados em uma tabela normalizada. Eu preciso fazer isso para aproximadamente 4.000 registros e ele precisa ser repetitivo.

Premissas:

  1. Suponha um endereço nos EUA (por enquanto)

  2. suponha que a string de input às vezes contenha um destinatário (a pessoa que está sendo endereçada) e / ou um segundo endereço de rua (ou seja, Suíte B)

  3. estados podem ser abreviados

  4. CEP pode ser padrão de 5 dígitos ou zip + 4

  5. existem erros de digitação em alguns casos

UPDATE: Em resposta às questões colocadas, os padrões não foram universalmente seguidos, eu preciso armazenar os valores individuais, não apenas geocodificar e erros significa erro de digitação (corrigido acima)

Dados de amostra:

  • AP Croll & Filho 2299 Lewes-Georgetown Hwy, Georgetown, DE 19947

  • 11522 Shawnee Road, Greenwood DE 19950

  • 144 Kings Highway, SW Dover, DE 19901

  • Const integrado. Serviços 2 Penns Way Suíte 405 New Castle, DE 19720

  • Humes Realty 33 Tribunal Bridle Ridge, Lewes, DE 19958

  • Escavação de Nichols 2742 Pulaski Hwy Newark, DE 19711

  • 2284 Bryn Zion Road, Smyrna, DE 19904

  • VEI Dover Crossroads, LLC 1500 Serpentine Road, Suíte 100 Baltimore MD 21

  • 580 North Dupont Highway Dover, DE 19901

  • Caixa postal 778 Dover, DE 19903

Eu trabalhei muito nesse tipo de análise. Como existem erros, você não terá 100% de precisão, mas há algumas coisas que você pode fazer para obter a maior parte do caminho e fazer um teste visual de BS. Aqui está o caminho geral para fazer isso. Não é código, porque é bastante acadêmico para escrevê-lo, não há estranheza, apenas um monte de manipulação de strings.

(Agora que você postou alguns dados de amostra, fiz algumas pequenas alterações)

  1. Trabalhe para trás. Comece pelo código postal, que estará perto do fim e em um dos dois formatos conhecidos: XXXXX ou XXXXX-XXXX. Se isso não aparecer, você pode assumir que está na cidade, parte do estado, abaixo.
  2. A próxima coisa, antes do zip, será o estado, e será em um formato de duas letras, ou como palavras. Você sabe o que será, também – há apenas 50 deles. Além disso, você pode soar as palavras para ajudar a compensar os erros de ortografia.
  3. antes disso é a cidade, e provavelmente está na mesma linha do estado. Você pode usar um database de CEP para verificar a cidade e o estado com base no CEP, ou pelo menos usá-lo como um detector de BS.
  4. O endereço da rua geralmente será de uma ou duas linhas. A segunda linha geralmente será o número da suíte, se houver, mas também pode ser uma checkbox postal.
  5. Vai ser quase impossível detectar um nome na primeira ou segunda linha, mas se não for prefixado com um número (ou se é prefixado com um “attn:” ou “atenção para:”, pode dar uma dica como para saber se é um nome ou uma linha de endereço.

Eu espero que isso ajude de algum jeito.

Acho que terceirizar o problema é a melhor aposta: enviá-lo para o geocodificador do Google (ou Yahoo). O geocoder retorna não apenas o lat / long (que não é de interesse aqui), mas também uma análise detalhada do endereço, com campos preenchidos que você não enviou (incluindo ZIP + 4 e condado).

Por exemplo, analisando “1600 Amphitheatre Parkway, Mountain View, CA”

{ "name": "1600 Amphitheatre Parkway, Mountain View, CA, USA", "Status": { "code": 200, "request": "geocode" }, "Placemark": [ { "address": "1600 Amphitheatre Pkwy, Mountain View, CA 94043, USA", "AddressDetails": { "Country": { "CountryNameCode": "US", "AdministrativeArea": { "AdministrativeAreaName": "CA", "SubAdministrativeArea": { "SubAdministrativeAreaName": "Santa Clara", "Locality": { "LocalityName": "Mountain View", "Thoroughfare": { "ThoroughfareName": "1600 Amphitheatre Pkwy" }, "PostalCode": { "PostalCodeNumber": "94043" } } } } }, "Accuracy": 8 }, "Point": { "coordinates": [-122.083739, 37.423021, 0] } } ] } 

Agora isso é parseable!

É provável que o pôster original tenha passado muito tempo, mas tentei portar o módulo Perl Geo :: StreetAddress: US usado pelo geocoder.us para o C #, descartá-lo no CodePlex e pensar que as pessoas que se deparam com essa questão no futuro podem ache útil:

Analisador de endereço nos EUA

Na home page do projeto, tento falar sobre suas limitações (muito reais). Uma vez que não é apoiado pelo database USPS de endereços válidos, a análise pode ser ambígua e não pode confirmar nem negar a validade de um determinado endereço. Pode apenas tentar extrair dados da string.

Ele é indicado para o caso em que você precisa obter um dataset principalmente nos campos certos ou deseja fornecer um atalho para a input de dados (permitindo que os usuários colem um endereço em uma checkbox de texto em vez de tabular entre vários campos). Não se destina a verificar a capacidade de entrega de um endereço.

Ele não tenta analisar nada acima da linha da rua, mas é provável que alguém se atreva a obter algo razoavelmente próximo – eu provavelmente acabaria com o número da casa.

Eu fiz isso no passado.

Faça isso manualmente (construa um bom gui que ajude o usuário a fazê-lo rapidamente) ou faça com que ele seja automatizado e verifique um database de endereços recente (você precisa comprar isso) e manipule erros manualmente.

O manuseio manual levará cerca de 10 segundos cada, o que significa que você pode fazer 3600/10 = 360 por hora, então 4000 deve levar aproximadamente de 11 a 12 horas. Isso lhe dará uma alta taxa de precisão.

Para automação, você precisa de um database de endereços dos EUA recente e ajustar suas regras contra isso. Eu sugiro não ir fantasia na regex (difícil manter a longo prazo, tantas exceções). Vá para 90% do database, faça o resto manualmente.

Obtenha uma cópia dos Padrões de Endereçamento Postal (USPS) em http://pe.usps.gov/cpim/ftp/pubs/Pub28/pub28.pdf e observe que ela tem mais de 130 páginas. Regexes para implementar isso seria louco.

Para endereços internacionais, todas as apostas estão desativadas. Os trabalhadores baseados nos EUA não poderiam validar.

Como alternativa, use um serviço de dados. Não tenho, no entanto, recomendações.

Além disso: quando você manda o material pelo correio (é para isso, certo?), Certifique-se de colocar “correção de endereço solicitada” no envelope (no lugar certo) e atualize o database. (Fizemos um simples gui para a pessoa da recepção fazer isso; a pessoa que realmente classifica pelo correio)

Finalmente, quando você tiver dados limpos, procure por duplicatas.

Eu tenho trabalhado no domínio de processamento de endereço por cerca de 5 anos agora, e realmente não há uma bala de prata. A solução correta vai depender do valor dos dados. Se não é muito valioso, jogue-o através de um analisador como as outras respostas sugerem. Se é mesmo um pouco valioso, você definitivamente precisa ter um humano para avaliar / corrigir todos os resultados do analisador. Se você estiver procurando por uma solução totalmente automatizada e repetível, provavelmente desejará falar com um fornecedor de correção de endereço, como Group1 ou Trillium.

Após o conselho aqui, planejei a seguinte function no VB que cria dados utilizáveis, embora nem sempre perfeitos (se um nome de empresa e uma linha de suíte forem dados, ele combina a suíte e a cidade). Por favor, sinta-se livre para comentar / refatorar / gritar comigo por quebrar uma das minhas próprias regras, etc .:

 Public Function parseAddress(ByVal input As String) As Collection input = input.Replace(",", "") input = input.Replace(" ", " ") Dim splitString() As String = Split(input) Dim streetMarker() As String = New String() {"street", "st", "st.", "avenue", "ave", "ave.", "blvd", "blvd.", "highway", "hwy", "hwy.", "box", "road", "rd", "rd.", "lane", "ln", "ln.", "circle", "circ", "circ.", "court", "ct", "ct."} Dim address1 As String Dim address2 As String = "" Dim city As String Dim state As String Dim zip As String Dim streetMarkerIndex As Integer zip = splitString(splitString.Length - 1).ToString() state = splitString(splitString.Length - 2).ToString() streetMarkerIndex = getLastIndexOf(splitString, streetMarker) + 1 Dim sb As New StringBuilder For counter As Integer = streetMarkerIndex To splitString.Length - 3 sb.Append(splitString(counter) + " ") Next counter city = RTrim(sb.ToString()) Dim addressIndex As Integer = 0 For counter As Integer = 0 To streetMarkerIndex If IsNumeric(splitString(counter)) _ Or splitString(counter).ToString.ToLower = "po" _ Or splitString(counter).ToString().ToLower().Replace(".", "") = "po" Then addressIndex = counter Exit For End If Next counter sb = New StringBuilder For counter As Integer = addressIndex To streetMarkerIndex - 1 sb.Append(splitString(counter) + " ") Next counter address1 = RTrim(sb.ToString()) sb = New StringBuilder If addressIndex = 0 Then If splitString(splitString.Length - 2).ToString() <> splitString(streetMarkerIndex + 1) Then For counter As Integer = streetMarkerIndex To splitString.Length - 2 sb.Append(splitString(counter) + " ") Next counter End If Else For counter As Integer = 0 To addressIndex - 1 sb.Append(splitString(counter) + " ") Next counter End If address2 = RTrim(sb.ToString()) Dim output As New Collection output.Add(address1, "Address1") output.Add(address2, "Address2") output.Add(city, "City") output.Add(state, "State") output.Add(zip, "Zip") Return output End Function Private Function getLastIndexOf(ByVal sArray As String(), ByVal checkArray As String()) As Integer Dim sourceIndex As Integer = 0 Dim outputIndex As Integer = 0 For Each item As String In checkArray For Each source As String In sArray If source.ToLower = item.ToLower Then outputIndex = sourceIndex If item.ToLower = "box" Then outputIndex = outputIndex + 1 End If End If sourceIndex = sourceIndex + 1 Next sourceIndex = 0 Next Return outputIndex End Function 

Passando a function parseAddress “AP Croll & Filho 2299 Lewes-Georgetown Hwy, Georgetown, DE 19947” retorna:

 2299 Lewes-Georgetown Hwy AP Croll & Son Georgetown DE 19947 

O SmartyStreets possui um novo recurso que extrai endereços de strings de input arbitrárias. (Nota: não trabalho no SmartyStreets.)

Extraiu com sucesso todos os endereços da input de amostra dada na pergunta acima. (A propósito, apenas 9 desses 10 endereços são válidos.)

Aqui está um pouco da saída: insira a descrição da imagem aqui

E aqui está a saída formatada em CSV dessa mesma solicitação:

 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,32,79,"2299 Lewes-Georgetown Hwy, Georgetown, DE 19947",N,,,,,,,,,,,,,,,,,,,,,, 2,81,119,"11522 Shawnee Road, Greenwood DE 19950",Y,0,,11522 Shawnee Rd,,Greenwood DE 19950-5209,Greenwood,DE,19950,Sussex,AABB,199505209226,Y,N,N,Y,38.82865,-75.54907,Zip9,Residential,S,,AL,N# 3,121,160,"144 Kings Highway, SW Dover, DE 19901",Y,0,,144 Kings Hwy,,Dover DE 19901-7308,Dover,DE,19901,Kent,AABB,199017308444,Y,N,N,Y,39.16081,-75.52377,Zip9,Commercial,S,,AL,L# 4,190,232,"2 Penns Way Suite 405 New Castle, DE 19720",Y,0,,2 Penns Way Ste 405,,New Castle DE 19720-2407,New Castle,DE,19720,New Castle,AABB,197202407053,Y,N,N,Y,39.68332,-75.61043,Zip9,Commercial,H,,AL,N# 5,247,285,"33 Bridle Ridge Court, Lewes, DE 19958",Y,0,,33 Bridle Ridge Cir,,Lewes DE 19958-8961,Lewes,DE,19958,Sussex,AABB,199588961338,Y,N,N,Y,38.72749,-75.17055,Zip7,Residential,S,,AL,L# 6,306,339,"2742 Pulaski Hwy Newark, DE 19711",Y,0,,2742 Pulaski Hwy,,Newark DE 19702-3911,Newark,DE,19702,New Castle,AABB,197023911421,Y,N,N,Y,39.60328,-75.75869,Zip9,Commercial,S,,AL,A# 7,341,378,"2284 Bryn Zion Road, Smyrna, DE 19904",Y,0,,2284 Bryn Zion Rd,,Smyrna DE 19977-3895,Smyrna,DE,19977,Kent,AABB,199773895840,Y,N,N,Y,39.23937,-75.64065,Zip7,Residential,S,,AL,A#N# 8,406,450,"1500 Serpentine Road, Suite 100 Baltimore MD",Y,0,,1500 Serpentine Rd Ste 100,,Baltimore MD 21209-2034,Baltimore,MD,21209,Baltimore,AABB,212092034250,Y,N,N,Y,39.38194,-76.65856,Zip9,Commercial,H,,03,N# 9,455,495,"580 North Dupont Highway Dover, DE 19901",Y,0,,580 N DuPont Hwy,,Dover DE 19901-3961,Dover,DE,19901,Kent,AABB,199013961803,Y,N,N,Y,39.17576,-75.5241,Zip9,Commercial,S,,AL,N# 10,497,525,"PO Box 778 Dover, DE 19903",Y,0,,PO Box 778,,Dover DE 19903-0778,Dover,DE,19903,Kent,AABB,199030778781,Y,N,N,Y,39.20946,-75.57012,Zip5,Residential,P,,AL, 

Eu era o desenvolvedor que originalmente escreveu o serviço. O algoritmo que implementamos é um pouco diferente de qualquer resposta específica aqui, mas cada endereço extraído é verificado na API de busca de endereço, então você pode ter certeza se é válido ou não. Cada resultado verificado é garantido, mas sabemos que os outros resultados não serão perfeitos porque, como ficou claro neste tópico, os endereços são imprevisíveis, mesmo para humanos às vezes.

Isso não resolverá seu problema, mas se você precisar apenas de dados lat / long para esses endereços, a API do Google Maps analisará bem os endereços não formatados.

Boa sugestão, como alternativa, você pode executar uma solicitação de CURL para cada endereço para o Google Maps e ele retornará o endereço formatado corretamente. A partir disso, você pode regexar o conteúdo do seu coração.

+ 1 sobre a solução sugerida por James A. Rosen, pois funcionou bem para mim, no entanto, para os especialistas, este site é uma leitura fascinante e a melhor tentativa que já vi em documentar endereços em todo o mundo: http://www.columbia.edu/kermit /postal.html

Existe algum padrão na maneira como os endereços são registrados? Por exemplo:

  1. Há sempre vírgulas ou novas linhas que separam street1 de street2 de city de state de zip?
  2. Os tipos de endereço (estrada, rua, avenida etc) são sempre explicitados? sempre abreviado? Alguns de cada um?
  3. Definir “erro”.

Minha resposta geral é uma série de expressões regulares, embora a complexidade disso dependa da resposta. E se não houver nenhuma consistência, então você só conseguirá obter sucesso parcial com um Regex (ou seja, filtrar código postal e estado) e terá que fazer o resto manualmente (ou pelo menos passar pelo resto muito cuidadosamente para ter certeza de identificar os erros).

Outra solicitação para dados de amostra.

Como foi mencionado, eu trabalharia para trás a partir do zip.

Uma vez que você tenha um zip, eu consultaria um database zip, armazenaria os resultados e os removeria e o zip da string.

Isso vai deixar você com a bagunça de endereço. Os endereços MAIS (Todos?) Começarão com um número, para encontrar a primeira ocorrência de um número na string restante e pegar tudo dela para o final (novo) da string. Esse será seu endereço. Qualquer coisa à esquerda desse número é provavelmente um destinatário.

Agora você deve ter a Cidade, Estado e Zip armazenados em uma tabela e, possivelmente, duas seqüências, destinatário e endereço. Para o endereço, verifique a existência de “Suite” ou “Apt”. etc. e dividir isso em dois valores (linhas de endereço 1 e 2).

Para o destinatário gostaria de punt e pegar a última palavra dessa seqüência como o último nome e colocar o resto no campo do primeiro nome. Se você não quiser fazer isso, você precisará verificar a saudação (Sr., Sra., Dr., etc.) no início e fazer algumas suposições com base no número de espaços de como o nome é decidir.

Eu não acho que você possa analisar 100% de precisão.

Tente http://www.address-parser.com . Usamos o serviço da web, que você pode testar on-line

Com base nos dados da amostra:

  1. Eu começaria no final da string. Analise um código postal (formato). Leia o final para o primeiro espaço. Se nenhum CEP foi encontrado Erro.

  2. Apare o fim então para espaços e caracteres especiais (vírgulas)

  3. Em seguida, vá para State, use novamente o espaço como delimitador. Talvez use uma lista de pesquisa para validar códigos de estado de 2 letras e nomes de estado completos. Se nenhum estado válido for encontrado, erro.

  4. Apare espaços e vírgulas do final novamente.

  5. A cidade fica complicada, eu usaria uma vírgula aqui, correndo o risco de obter muitos dados na cidade. Procure a vírgula ou o começo da linha.

  6. Se você ainda tiver caracteres deixados na string, insira tudo isso em um campo de endereço.

Isso não é perfeito, mas deve ser um bom ponto de partida.

Se forem dados inseridos por humanos, você gastará muito tempo tentando codificar as exceções.

Experimentar:

  1. Expressão regular para extrair o código postal

  2. Pesquisa de CEP (por meio do DB do governo apropriado) para obter o endereço correto

  3. Peça a um interno para verificar manualmente se os novos dados correspondem ao antigo

Isso não resolverá seu problema, mas se você precisar apenas de dados lat / long para esses endereços, a API do Google Maps analisará bem os endereços não formatados.

RecogniContact é um object COM do Windows que analisa endereços dos EUA e da Europa. Você pode experimentá-lo direito em http://www.loquisoft.com/index.php?page=8

Você pode querer checar isso!! http://jgeocoder.sourceforge.net/parser.html Trabalhou como um encanto para mim.

Esse tipo de problema é difícil de resolver devido às ambigüidades subjacentes nos dados.

Aqui está uma solução baseada em Perl que define uma tree de gramática de descendência recursiva baseada em expressões regulares para analisar muitas combinações válidas de endereços de rua: http://search.cpan.org/~kimryan/Lingua-EN-AddressParse-1.20/lib/Lingua /EN/AddressParse.pm . Isso inclui subpropriedades dentro de um endereço como: 12 1st Avenue N Suite # 2 em algum lugar CA 12345 EUA

É semelhante a http://search.cpan.org/~timb/Geo-StreetAddress-US-1.03/US.pm mencionado acima, mas também funciona para endereços que não são dos EUA, como o Reino Unido, Austrália e Canadá.

Aqui está a saída para um dos seus endereços de amostra. Note que a seção de nomes precisaria ser removida primeiro de “AP Croll & Son 2299 Lewes- Georgetown Hwy, Georgetown, DE 19947” para reduzi-la a “2299 Lewes-Georgetown Hwy, Georgetown, DE 19947”. Isso é facilmente obtido removendo todos os dados até o primeiro número encontrado na string.

 Non matching part '' Error '0' Error descriptions '' Case all '2299 Lewes-Georgetown Hwy Georgetown DE 19947' COMPONENTS '' country '' po_box_type '' post_box '' post_code '19947' pre_cursor '' property_identifier '2299' property_name '' road_box '' street 'Lewes-Georgetown' street_direction '' street_type 'Hwy' sub_property_identifier '' subcountry 'DE' suburb 'Georgetown' 

Como há chance de erro na palavra, pense em usar o SOUNDEX combinado com o algoritmo LCS para comparar strings, isso ajudará muito!

usando o google API

 $d=str_replace(" ", "+", $address_url); $completeurl ="http://maps.googleapis.com/maps/api/geocode/xml?address=".$d."&sensor=true"; $phpobject = simplexml_load_file($completeurl); print_r($phpobject); 

Para desenvolvedores Ruby ou Rails, há uma boa gem disponível, chamada street_address . Eu tenho usado isso em um dos meus projetos e ele faz o trabalho que eu preciso.

O único problema que tive foi sempre que um endereço é neste formato PO Box 1410 Durham, NC 27702 que retornou nulo e, portanto, eu tive que replace “PO Box” com “” e depois disso, foi capaz de analisá-lo.

Existem serviços de dados que, dado um código postal, fornecerão uma lista de nomes de ruas nesse código postal.

Use um regex para extrair o CEP ou o estado da cidade – encontre o correto ou se um erro obtiver os dois. puxar a lista de ruas de uma fonte de dados Corrija a cidade e o estado e, em seguida, o endereço da rua. Depois de obter uma linha de endereço 1, cidade, estado e CEP válidos, você pode fazer suposições na linha de endereço 2..3

Eu não sei o quão acessível isso seria, mas eu não vi isso mencionado, então eu pensei em ir em frente e sugerir isso:

Se você está estritamente nos EUA … obtenha um enorme database de todos os códigos postais, estados, cidades e ruas. Agora olhe para estes em seus endereços. Você pode validar o que você encontra testando se, digamos, a cidade que você encontrou existe no estado que você encontrou, ou verificando se a rua que você encontrou existe na cidade que você encontrou. Se não, as chances são que John não é para a rua de John, mas é o nome do destinatário … Basicamente, obtenha o máximo de informações possível e verifique seus endereços em relação a ele. Um exemplo extremo seria obter UMA LISTA DE TODOS OS ENDEREÇOS NOS EUA DE e, em seguida, descobrir qual deles tem a correspondência mais relevante para cada um dos seus endereços …

Há uma porta javascript do pacote perl Geo :: StreetAddress :: US: https://github.com/hassansin/parse-address . É baseado em regex e funciona razoavelmente bem.