Come analizzare l'indirizzo postale / postale a forma libera dal testo e nei componenti


136

Operiamo principalmente negli Stati Uniti e stiamo cercando di migliorare l'esperienza dell'utente combinando tutti i campi degli indirizzi in un'unica area di testo. Ma ci sono alcuni problemi:

  • L'indirizzo digitato dall'utente potrebbe non essere corretto o in un formato standard
  • L'indirizzo deve essere separato in parti (via, città, stato, ecc.) Per elaborare i pagamenti con carta di credito
  • Gli utenti possono inserire più di un semplice indirizzo (come il loro nome o azienda con esso)
  • Google può farlo, ma i Termini di servizio e i limiti di query sono proibitivi, soprattutto con un budget limitato

Apparentemente, questa è una domanda comune:

C'è un modo per isolare un indirizzo dal testo attorno ad esso e dividerlo in pezzi? Esiste un'espressione regolare per analizzare gli indirizzi?


Le risposte di seguito sono più utili perché non ignorano il problema globale: gli indirizzi non si adattano a un modello comune.
Marc Maxmeister,

Risposte:


290

Ho visto questa domanda molto quando ho lavorato per una società di verifica degli indirizzi. Sto pubblicando la risposta qui per renderla più accessibile ai programmatori che stanno cercando la stessa domanda. La compagnia in cui lavoravo miliardi di indirizzi e abbiamo imparato molto nel processo.

Innanzitutto, dobbiamo capire alcune cose sugli indirizzi.

Gli indirizzi non sono regolari

Ciò significa che le espressioni regolari sono fuori. Ho visto tutto, dalle semplici espressioni regolari che abbinano gli indirizzi in un formato molto specifico, a questo:

/ \ S + (\ d {2,5} \ s +) ([a | p]?! M \ b) (([a-zA-Z | \ s +] {1,5}) {1,2}) ? ([\ s |, |.] +) (([a-zA-Z | \ s +] {1,30}) {1,4})? (tribunale | ct | via | st | auto | dr | corsia | ln | stradali | 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 | KY | LA | MA | MD | ME | MI | MN | MO | MS | MT | CN | ND | NE | NH | NJ | nm | NV | NY | OH | OK | O | PA | RI | SC | SD | TN | TX | UT | VA | VI | VT | WA | WI | WV | WY) ([\ s |, |.] +) (\ s + \ d {5}) ([\ s |, |.]? +) / i

... a ciò in cui un file di classe superiore a 900+ genera un'espressione regolare supermassiva al volo per abbinare ancora di più. Non li consiglio (ad esempio, ecco un violino della regex sopra, che fa molti errori ). Non esiste una formula magica facile per farlo funzionare. In teoria e in teoria, non è possibile abbinare indirizzi con un'espressione regolare.

La pubblicazione USPS 28 documenta i numerosi formati di indirizzi possibili, con tutte le loro parole chiave e variatori. Peggio ancora, gli indirizzi sono spesso ambigui. Le parole possono significare più di una cosa ("St" può essere "Saint" o "Street") e ci sono parole che sono abbastanza sicuro che abbiano inventato. (Chi sapeva che "Stravenue" era un suffisso stradale?)

Avresti bisogno di un codice che capisca davvero gli indirizzi, e se quel codice esiste, è un segreto commerciale. Ma probabilmente potresti farlo da solo se ti piace davvero.

Gli indirizzi sono disponibili in forme e dimensioni inaspettate

Ecco alcuni indirizzi inventati (ma completi):

1)  102 main street
    Anytown, state

2)  400n 600e #2, 52173

3)  p.o. #104 60203

Anche questi sono probabilmente validi:

4)  829 LKSDFJlkjsdflkjsdljf Bkpw 12345

5)  205 1105 14 90210

Ovviamente, questi non sono standardizzati. Punteggiatura e interruzioni di riga non garantite. Ecco cosa sta succedendo:

  1. Il numero 1 è completo perché contiene un indirizzo e una città e uno stato. Con tali informazioni, è sufficiente identificare l'indirizzo e può essere considerato "consegnabile" (con una certa standardizzazione).

  2. Il numero 2 è completo perché contiene anche un indirizzo (con numero secondario / unità) e un codice postale di 5 cifre, che è sufficiente per identificare un indirizzo.

  3. Il numero 3 è un formato di casella postale completo, in quanto contiene un codice postale.

  4. Il numero 4 è completo anche perché il codice postale è univoco , il che significa che un'entità privata o una società ha acquistato quello spazio degli indirizzi. Un codice postale unico è per gli spazi di consegna concentrati o ad alto volume. Qualsiasi cosa indirizzata al codice postale 12345 va alla General Electric di Schenectady, New York. Questo esempio non raggiungerà nessuno in particolare, ma l'USPS sarebbe comunque in grado di fornirlo.

  5. Anche il numero 5 è completo, che ci crediate o no. Con solo quei numeri, l'indirizzo completo può essere scoperto quando analizzato su un database di tutti gli indirizzi possibili. Compilare i direzionali mancanti, il designatore secondario e il codice ZIP + 4 è banale quando vedi ogni numero come un componente. Ecco come appare, completamente espanso e standardizzato:

205 N 1105 W Apt 14

Beverly Hills CA 90210-5221

I dati dell'indirizzo non sono tuoi

Nella maggior parte dei paesi che forniscono dati di indirizzo ufficiali ai fornitori autorizzati, i dati di indirizzo stessi appartengono all'agenzia governativa. Negli Stati Uniti, USPS possiede gli indirizzi. Lo stesso vale per Canada Post, Royal Mail e altri, sebbene ogni paese imponga o definisca la proprietà in modo leggermente diverso. Sapere questo è importante, dal momento che solitamente proibisce il reverse engineering del database degli indirizzi. Devi stare attento a come acquisire, archiviare e utilizzare i dati.

Google Maps è una soluzione comune per le correzioni rapide degli indirizzi, ma il TOS è piuttosto proibitivo; ad esempio, non è possibile utilizzare i loro dati o API senza mostrare una mappa di Google e solo per scopi non commerciali (a meno che non si paghi) e non è possibile archiviare i dati (tranne per la memorizzazione temporanea nella cache). Ha senso. I dati di Google sono tra i migliori al mondo. Tuttavia, Google Maps non verifica l'indirizzo. Se un indirizzo non esiste, sarà ancora vi mostrerà dove l'indirizzo sarebbe stato se essa ha fatto exist (provarlo sulla propria strada, utilizzare un numero civico che si sa non esiste). Questo è utile a volte, ma attenzione.

La politica di utilizzo di Nominatim è allo stesso modo limitante, specialmente per volumi elevati e uso commerciale, e i dati sono per lo più estratti da fonti gratuite, quindi non sono così ben mantenuti (tale è la natura dei progetti aperti) - tuttavia, questo potrebbe comunque adattarsi I tuoi bisogni. È supportato da una grande comunità.

Lo stesso USPS ha un'API, ma scende molto e non ha garanzie né supporto. Potrebbe anche essere difficile da usare. Alcune persone lo usano con parsimonia senza problemi. Ma è facile perdere il fatto che USPS richiede di utilizzare la propria API solo per confermare gli indirizzi da spedire attraverso di essi.

Le persone si aspettano che gli indirizzi siano difficili

Sfortunatamente, abbiamo condizionato la nostra società ad aspettarsi che gli indirizzi fossero complicati. Ci sono dozzine di buoni articoli UX su Internet su questo, ma il fatto è che se hai un modulo di indirizzo con singoli campi, questo è ciò che gli utenti si aspettano, anche se rende più difficile per gli indirizzi edge-case che non si adattano al formattare il modulo è previsto, o forse il modulo richiede un campo che non dovrebbe. O gli utenti non sanno dove inserire una determinata parte del loro indirizzo.

In questi giorni potrei continuare a parlare della cattiva UX dei moduli di pagamento, ma invece dirò solo che combinare gli indirizzi in un singolo campo sarà un gradito cambiamento - le persone saranno in grado di digitare il loro indirizzo come meglio credono , piuttosto che cercare di capire la tua forma lunga. Tuttavia, questa modifica sarà inaspettata e all'inizio gli utenti potrebbero trovarla un po 'stonata. Basta essere consapevoli di ciò.

Parte di questo dolore può essere alleviato mettendo il campo di campagna in primo piano, prima dell'indirizzo. Quando compilano prima il campo Paese, sai come far apparire il tuo modulo. Forse hai un buon modo per gestire gli indirizzi degli Stati Uniti a campo singolo, quindi se selezionano gli Stati Uniti, puoi ridurre il modulo in un singolo campo, altrimenti mostra i campi dei componenti. Solo cose a cui pensare!

Ora sappiamo perché è difficile; cosa puoi fare al riguardo?

USPS concede in licenza ai fornitori tramite un processo chiamato certificazione CASS ™ per fornire indirizzi verificati ai clienti. Questi fornitori hanno accesso al database USPS, aggiornato mensilmente. Il loro software deve essere conforme a rigorosi standard per essere certificato, e spesso non richiedono un accordo su termini limitanti come discusso sopra.

Esistono molte aziende certificate CASS che possono elaborare elenchi o disporre di API: Melissa Data, Experian QAS e SmartyStreets per citarne alcuni.

(A causa del fatto che mi sono comportato male per la "pubblicità", ho troncato la mia risposta a questo punto. Sta a te trovare una soluzione che funzioni per te.)

La verità: davvero, gente, non lavoro in nessuna di queste aziende. Non è una pubblicità.


1
Che dire degli indirizzi sudamericani (Uruguay)? : D
Bart Calixto,

11
@Brian - Forse perché l'utente ha fornito molte informazioni utili per coloro che leggono la domanda e la risposta, indipendentemente dal fatto che scelgano o meno di utilizzare il prodotto della sua azienda.
Zarepheth,

7
@Brian Quei siti sono raschiatori di contenuti. Stanno muggendo il contenuto per ottenere le classifiche SERP. Non li ho mai visti prima. Non ho mai pubblicato questo contenuto prima o dopo altrove.
Matt,

2
@khuderm Ho notato proprio ora quando ho letto il tuo commento che tutti i commenti dissenzienti sono scomparsi; non so come / quando è successo. Ma comunque, vedi la cronologia delle modifiche della mia risposta e troverai un riferimento diretto a un estrattore di indirizzi statunitense che potrebbe aiutarti. L'ho costruito quando ho lavorato al mio ultimo lavoro, ma è un codice proprietario quindi non posso condividerlo ... ma esistono. Speriamo sia utile.
Matt,

2
Ops. Siamo spiacenti @Matt. Bene, ho iniziato a seguirti attraverso le tue domande e anche Github. Sei davvero impressionante.
Sayka,

28

libpostal: una libreria open source per l'analisi degli indirizzi, formazione con dati da OpenStreetMap, OpenAddresses e OpenCage.

https://github.com/openvenues/libpostal ( maggiori informazioni a riguardo )

Altri strumenti / servizi:


13

Esistono molti parser di indirizzi stradali. Sono disponibili in due versioni di base: quelle che dispongono di database di nomi di luoghi e nomi di strade e quelle che non lo fanno.

Un parser di indirizzi di espressioni regolari può ottenere un tasso di successo di circa il 95% senza troppi problemi. Quindi inizi a colpire i casi insoliti. Quello Perl in CPAN, "Geo :: StreetAddress :: US", è abbastanza buono. Esistono porte Python e Javascript, tutte open source. Ho una versione migliorata in Python che aumenta leggermente il tasso di successo gestendo più casi. Per ottenere l'ultimo 3% giusto, tuttavia, sono necessari database per aiutare con chiarimento delle ambiguità.

Un database con codici postali a 3 cifre e nomi e abbreviazioni degli Stati Uniti è di grande aiuto. Quando un parser vede un codice postale e un nome di stato coerenti, può iniziare a bloccare il formato. Questo funziona molto bene per gli Stati Uniti e il Regno Unito.

La corretta analisi dell'indirizzo stradale inizia dalla fine e funziona all'indietro. Ecco come lo fanno i sistemi USPS. Gli indirizzi sono meno ambigui alla fine, dove nomi di paesi, nomi di città e codici postali sono relativamente facili da riconoscere. I nomi delle strade possono di solito essere isolati. Le posizioni sulle strade sono le più complesse da analizzare; lì incontri cose come "Fifth Floor" e "Staples Pavillion". Questo è quando un database è di grande aiuto.


Esiste anche il modulo CPAN Lingua: EN :: AddressParse. Mentre è più lento di "Geo :: StreetAddress :: US, offre un tasso di successo più elevato.
Kim Ryan,

8

AGGIORNAMENTO: Geocode.xyz ora funziona in tutto il mondo. Per esempi vedere https://geocode.xyz

Per Stati Uniti, Messico e Canada, consultare geocoder.ca .

Per esempio:

Input: qualcosa che sta succedendo vicino all'incrocio tra main e arthur kill new york

Produzione:

<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>

Puoi anche controllare i risultati nell'interfaccia web o ottenere l'output come Json o Jsonp. per esempio. Sto cercando ristoranti intorno a 123 Main Street, New York


Come hai implementato il sistema di analisi degli indirizzi usando openaddress? Stai usando la strategia della forza bruta?
Nithin K Anil,

1
Cosa intendi con "forza bruta"? La scomposizione del testo in tutte le possibili combinazioni di possibili stringhe di indirizzi e il confronto di ciascuna con un database di indirizzi non è pratico e richiederà molto più tempo per fornire una risposta rispetto a questo sistema. Gli indirizzi aperti sono una delle fonti di dati per la creazione di un "set di formazione" di formati di indirizzi per l'algoritmo. Usa queste informazioni per analizzare gli indirizzi dal testo non strutturato.
Ervin Ruci,

2
Un altro sistema simile è Geo :: libpostal ( perltricks.com/article/announcing-geo--libpostal ) Usano anche openstreetmap e sembra che indirizzi aperti, per costruire al volo modelli di indirizzi
Ervin Ruci,

Ho appena testato il geoparser di geocode.xyz (invia testo, torna indietro) su centinaia di indirizzi reali. Dato un lato dell'API di google map e un insieme globale di indirizzi, geocode.xyzil scantextmetodo ha fallito la maggior parte del tempo. Ha sempre scelto "Ginevra, USA" piuttosto che "Ginevra, Svizzera" ed era generalmente di parte USA.
Marc Maxmeister,

Dipende dal contesto. geocode.xyz/?scantext=Geneva,%20La Svizzera produrrà: Posizione della partita Ginevra, Svizzera, Punteggio di confidenza CH: 0,8 mentre geocode.xyz/?scantext=Geneva,%20USA produrrà Posizione della partita Ginevra, Punteggio di confidenza USA: 1,0 Inoltre, puoi differenziare la
Ervin Ruci,

4

Nessun codice? Per vergogna!

Ecco un semplice parser di indirizzi JavaScript. È abbastanza terribile per ogni singolo motivo che Matt dà nella sua tesi di cui sopra (che sono quasi al 100% d'accordo: gli indirizzi sono tipi complessi e gli umani commettono errori; meglio esternalizzare e automatizzare questo - quando te lo puoi permettere).

Ma piuttosto che piangere, ho deciso di provare:

Questo codice funziona correttamente per l'analisi della maggior parte dei risultati di EsrifindAddressCandidatee anche con alcuni altri geocoder (inversi) che restituiscono un indirizzo a riga singola in cui via / città / stato sono delimitati da virgole. È possibile estendere se si desidera o scrivere parser specifici per Paese. O semplicemente usalo come caso di studio su quanto possa essere difficile questo esercizio o su quanto sono pessimo in JavaScript. Ammetto di aver speso solo una trentina di minuti in questo (future iterazioni potrebbero aggiungere cache, convalida zip e ricerche di stato, nonché il contesto della posizione dell'utente), ma ha funzionato per il mio caso d'uso: l'utente finale vede il modulo che analizza la risposta della ricerca geocodice in 4 caselle di testo. Se l'analisi dell'indirizzo risulta errata (il che è raro a meno che i dati di origine non fossero scadenti) non è un grosso problema - l'utente può verificarlo e risolverlo! (Ma per le soluzioni automatizzate è possibile scartare / ignorare o contrassegnare come errore in modo che dev possa supportare il nuovo formato o correggere i dati di origine.)

/* 
address assumptions:
- US addresses only (probably want separate parser for different countries)
- No country code expected.
- if last token is a number it is probably a postal code
-- 5 digit number means more likely
- if last token is a hyphenated string it might be a postal code
-- if both sides are numeric, and in form #####-#### it is more likely
- if city is supplied, state will also be supplied (city names not unique)
- zip/postal code may be omitted even if has city & state
- state may be two-char code or may be full state name.
- commas: 
-- last comma is usually city/state separator
-- second-to-last comma is possibly street/city separator
-- other commas are building-specific stuff that I don't care about right now.
- token count:
-- because units, street names, and city names may contain spaces token count highly variable.
-- simplest address has at least two tokens: 714 OAK
-- common simple address has at least four tokens: 714 S OAK ST
-- common full (mailing) address has at least 5-7:
--- 714 OAK, RUMTOWN, VA 59201
--- 714 S OAK ST, RUMTOWN, VA 59201
-- complex address may have a dozen or more:
--- MAGICICIAN SUPPLY, LLC, UNIT 213A, MAGIC TOWN MALL, 13 MAGIC CIRCLE DRIVE, LAND OF MAGIC, MA 73122-3412
*/

var rawtext = $("textarea").val();
var rawlist = rawtext.split("\n");

function ParseAddressEsri(singleLineaddressString) {
  var address = {
    street: "",
    city: "",
    state: "",
    postalCode: ""
  };

  // tokenize by space (retain commas in tokens)
  var tokens = singleLineaddressString.split(/[\s]+/);
  var tokenCount = tokens.length;
  var lastToken = tokens.pop();
  if (
    // if numeric assume postal code (ignore length, for now)
    !isNaN(lastToken) ||
    // if hyphenated assume long zip code, ignore whether numeric, for now
    lastToken.split("-").length - 1 === 1) {
    address.postalCode = lastToken;
    lastToken = tokens.pop();
  }

  if (lastToken && isNaN(lastToken)) {
    if (address.postalCode.length && lastToken.length === 2) {
      // assume state/province code ONLY if had postal code
      // otherwise it could be a simple address like "714 S OAK ST"
      // where "ST" for "street" looks like two-letter state code
      // possibly this could be resolved with registry of known state codes, but meh. (and may collide anyway)
      address.state = lastToken;
      lastToken = tokens.pop();
    }
    if (address.state.length === 0) {
      // check for special case: might have State name instead of State Code.
      var stateNameParts = [lastToken.endsWith(",") ? lastToken.substring(0, lastToken.length - 1) : lastToken];

      // check remaining tokens from right-to-left for the first comma
      while (2 + 2 != 5) {
        lastToken = tokens.pop();
        if (!lastToken) break;
        else if (lastToken.endsWith(",")) {
          // found separator, ignore stuff on left side
          tokens.push(lastToken); // put it back
          break;
        } else {
          stateNameParts.unshift(lastToken);
        }
      }
      address.state = stateNameParts.join(' ');
      lastToken = tokens.pop();
    }
  }

  if (lastToken) {
    // here is where it gets trickier:
    if (address.state.length) {
      // if there is a state, then assume there is also a city and street.
      // PROBLEM: city may be multiple words (spaces)
      // but we can pretty safely assume next-from-last token is at least PART of the city name
      // most cities are single-name. It would be very helpful if we knew more context, like
      // the name of the city user is in. But ignore that for now.
      // ideally would have zip code service or lookup to give city name for the zip code.
      var cityNameParts = [lastToken.endsWith(",") ? lastToken.substring(0, lastToken.length - 1) : lastToken];

      // assumption / RULE: street and city must have comma delimiter
      // addresses that do not follow this rule will be wrong only if city has space
      // but don't care because Esri formats put comma before City
      var streetNameParts = [];

      // check remaining tokens from right-to-left for the first comma
      while (2 + 2 != 5) {
        lastToken = tokens.pop();
        if (!lastToken) break;
        else if (lastToken.endsWith(",")) {
          // found end of street address (may include building, etc. - don't care right now)
          // add token back to end, but remove trailing comma (it did its job)
          tokens.push(lastToken.endsWith(",") ? lastToken.substring(0, lastToken.length - 1) : lastToken);
          streetNameParts = tokens;
          break;
        } else {
          cityNameParts.unshift(lastToken);
        }
      }
      address.city = cityNameParts.join(' ');
      address.street = streetNameParts.join(' ');
    } else {
      // if there is NO state, then assume there is NO city also, just street! (easy)
      // reasoning: city names are not very original (Portland, OR and Portland, ME) so if user wants city they need to store state also (but if you are only ever in Portlan, OR, you don't care about city/state)
      // put last token back in list, then rejoin on space
      tokens.push(lastToken);
      address.street = tokens.join(' ');
    }
  }
  // when parsing right-to-left hard to know if street only vs street + city/state
  // hack fix for now is to shift stuff around.
  // assumption/requirement: will always have at least street part; you will never just get "city, state"  
  // could possibly tweak this with options or more intelligent parsing&sniffing
  if (!address.city && address.state) {
    address.city = address.state;
    address.state = '';
  }
  if (!address.street) {
    address.street = address.city;
    address.city = '';
  }

  return address;
}

// get list of objects with discrete address properties
var addresses = rawlist
  .filter(function(o) {
    return o.length > 0
  })
  .map(ParseAddressEsri);
$("#output").text(JSON.stringify(addresses));
console.log(addresses);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<textarea>
27488 Stanford Ave, Bowden, North Dakota
380 New York St, Redlands, CA 92373
13212 E SPRAGUE AVE, FAIR VALLEY, MD 99201
1005 N Gravenstein Highway, Sebastopol CA 95472
A. P. Croll &amp; Son 2299 Lewes-Georgetown Hwy, Georgetown, DE 19947
11522 Shawnee Road, Greenwood, DE 19950
144 Kings Highway, S.W. Dover, DE 19901
Intergrated Const. Services 2 Penns Way Suite 405, New Castle, DE 19720
Humes Realty 33 Bridle Ridge Court, Lewes, DE 19958
Nichols Excavation 2742 Pulaski Hwy, Newark, DE 19711
2284 Bryn Zion Road, Smyrna, DE 19904
VEI Dover Crossroads, LLC 1500 Serpentine Road, Suite 100 Baltimore MD 21
580 North Dupont Highway, Dover, DE 19901
P.O. Box 778, Dover, DE 19903
714 S OAK ST
714 S OAK ST, RUM TOWN, VA, 99201
3142 E SPRAGUE AVE, WHISKEY VALLEY, WA 99281
27488 Stanford Ave, Bowden, North Dakota
380 New York St, Redlands, CA 92373
</textarea>
<div id="output">
</div>


disclaimer: i miei clienti possiedono i loro dati di indirizzo ed eseguono i propri server Esri. Se raccogli dati da Google, OSM, ArcGisOnline o ovunque, assicurati che sia OK archiviarli e utilizzarli (molti servizi hanno restrizioni su come archiviare e per quanto tempo)
nulla è necessario

La prima risposta sopra fa un caso convincente che questo problema è irrisolvibile con regex se hai a che fare con un elenco di indirizzi globale. 200 paesi hanno troppe eccezioni. Nei miei test, puoi determinare il paese da una stringa in modo piuttosto affidabile, quindi cercare una regex specifica per ciascun paese, che è probabilmente il modo in cui funzionano le API migliori.
Marc Maxmeister,


2

Un'altra opzione per gli indirizzi negli Stati Uniti è YAddress (realizzato dalla società per cui lavoro).

Molte risposte a questa domanda suggeriscono come soluzione gli strumenti di geocodifica. È importante non confondere l'analisi degli indirizzi e la geocodifica; non sono gli stessi. Mentre i geocoder possono scomporre un indirizzo in componenti come vantaggio collaterale, di solito si basano su insiemi di indirizzi non standard. Ciò significa che un indirizzo analizzato dal geocoder potrebbe non essere uguale all'indirizzo ufficiale. Ad esempio, ciò che l'API di geocodifica di Google chiama "6th Ave" a Manhattan, USPS chiama "Avenue of the Americas".


2

Per l'analisi degli indirizzi negli Stati Uniti,

Preferisco usare il pacchetto usaddress che è disponibile in pip solo per usaddress

python3 -m pip install usaddress

Documentazione
PyPi

Questo ha funzionato bene per me negli Stati Uniti.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# address_parser.py
import sys
from usaddress import tag
from json import dumps, loads

if __name__ == '__main__':
    tag_mapping = {
        'Recipient': 'recipient',
        'AddressNumber': 'addressStreet',
        'AddressNumberPrefix': 'addressStreet',
        'AddressNumberSuffix': 'addressStreet',
        'StreetName': 'addressStreet',
        'StreetNamePreDirectional': 'addressStreet',
        'StreetNamePreModifier': 'addressStreet',
        'StreetNamePreType': 'addressStreet',
        'StreetNamePostDirectional': 'addressStreet',
        'StreetNamePostModifier': 'addressStreet',
        'StreetNamePostType': 'addressStreet',
        'CornerOf': 'addressStreet',
        'IntersectionSeparator': 'addressStreet',
        'LandmarkName': 'addressStreet',
        'USPSBoxGroupID': 'addressStreet',
        'USPSBoxGroupType': 'addressStreet',
        'USPSBoxID': 'addressStreet',
        'USPSBoxType': 'addressStreet',
        'BuildingName': 'addressStreet',
        'OccupancyType': 'addressStreet',
        'OccupancyIdentifier': 'addressStreet',
        'SubaddressIdentifier': 'addressStreet',
        'SubaddressType': 'addressStreet',
        'PlaceName': 'addressCity',
        'StateName': 'addressState',
        'ZipCode': 'addressPostalCode',
    }
    try:
        address, _ = tag(' '.join(sys.argv[1:]), tag_mapping=tag_mapping)
    except:
        with open('failed_address.txt', 'a') as fp:
            fp.write(sys.argv[1] + '\n')
        print(dumps({}))
    else:
        print(dumps(dict(address)))

Esecuzione di address_parser.py

 python3 address_parser.py 9757 East Arcadia Ave. Saugus MA 01906
 {"addressStreet": "9757 East Arcadia Ave.", "addressCity": "Saugus", "addressState": "MA", "addressPostalCode": "01906"}

0

In uno dei nostri progetti abbiamo utilizzato il seguente parser di indirizzi. Analizza gli indirizzi per la maggior parte dei paesi del mondo con buona precisione.

http://address-parser.net/

È disponibile come libreria autonoma o come API live.


1
Ma è un prodotto a pagamento.
Jeremy Thompson,

0

Sono in ritardo alla festa, ecco uno script VBA di Excel che ho scritto anni fa per l'Australia. Può essere facilmente modificato per supportare altri Paesi. Ho creato un repository GitHub del codice C # qui. L'ho ospitato sul mio sito e puoi scaricarlo qui: http://jeremythompson.net/rocks/ParseAddress.xlsm

Strategia

Per ogni paese con un codice postale numerico o che può essere abbinato a un RegEx la mia strategia funziona molto bene:

  1. In primo luogo rileviamo il primo e il cognome che si presume siano la linea superiore. È facile saltare il nome e iniziare con l'indirizzo deselezionando la casella di controllo (chiamata "Il nome è la riga superiore" come mostrato di seguito).

  2. Successivamente è sicuro aspettarsi che l'indirizzo costituito da Street e Number venga prima del sobborgo e St, Pde, Ave, Av, Rd, Cres, loop, ecc. È un separatore.

  3. Rilevare il sobborgo rispetto allo stato e persino al paese può ingannare i parser più sofisticati in quanto possono esserci conflitti. Per ovviare a questo, utilizzo un codice PostCode in base al fatto che dopo aver rimosso i numeri di via e appartamento / unità nonché PoBox, telefono, fax , cellulare ecc., Rimarrà solo il numero PostCode. Questo è facile da abbinare a un regEx per cercare i sobborghi e il paese.

Il servizio postale nazionale fornirà un elenco di codici postali con periferia e Stati gratuiti che è possibile memorizzare in un foglio Excel, in una tabella db, in un file di testo / json / xml, ecc.

  1. Infine, poiché alcuni codici postali hanno più sobborghi, controlliamo quale sobborgo appare nell'indirizzo.

Esempio

inserisci qui la descrizione dell'immagine

Codice VBA

DISCLAIMER, so che questo codice non è perfetto, o anche scritto bene, tuttavia è molto facile da convertire in qualsiasi linguaggio di programmazione ed eseguire in qualsiasi tipo di applicazione. La strategia è la risposta a seconda del tuo paese e delle regole, prendi questo codice come esempio :

Option Explicit

Private Const TopRow As Integer = 0

Public Sub ParseAddress()
Dim strArr() As String
Dim sigRow() As String
Dim i As Integer
Dim j As Integer
Dim k As Integer
Dim Stat As String
Dim SpaceInName As Integer
Dim Temp As String
Dim PhExt As String

On Error Resume Next

Temp = ActiveSheet.Range("Address")

'Split info into array
strArr = Split(Temp, vbLf)

'Trim the array
For i = 0 To UBound(strArr)
strArr(i) = VBA.Trim(strArr(i))
Next i

'Remove empty items/rows    
ReDim sigRow(LBound(strArr) To UBound(strArr))
For i = LBound(strArr) To UBound(strArr)
    If Trim(strArr(i)) <> "" Then
        sigRow(j) = strArr(i)
        j = j + 1
    End If
Next i
ReDim Preserve sigRow(LBound(strArr) To j)

'Find the name (MUST BE ON THE FIRST ROW UNLESS CHECKBOX UNTICKED)
i = TopRow
If ActiveSheet.Shapes("chkFirst").ControlFormat.Value = 1 Then

SpaceInName = InStr(1, sigRow(i), " ", vbTextCompare) - 1

If ActiveSheet.Shapes("chkConfirm").ControlFormat.Value = 0 Then
ActiveSheet.Range("FirstName") = VBA.Left(sigRow(i), SpaceInName)
Else
 If MsgBox("First Name: " & VBA.Mid$(sigRow(i), 1, SpaceInName), vbQuestion + vbYesNo, "Confirm Details") = vbYes Then ActiveSheet.Range("FirstName") = VBA.Left(sigRow(i), SpaceInName)
End If

If ActiveSheet.Shapes("chkConfirm").ControlFormat.Value = 0 Then
ActiveSheet.Range("Surname") = VBA.Mid(sigRow(i), SpaceInName + 2)
Else
  If MsgBox("Surame: " & VBA.Mid(sigRow(i), SpaceInName + 2), vbQuestion + vbYesNo, "Confirm Details") = vbYes Then ActiveSheet.Range("Surname") = VBA.Mid(sigRow(i), SpaceInName + 2)
End If
sigRow(i) = ""
End If

'Find the Street by looking for a "St, Pde, Ave, Av, Rd, Cres, loop, etc"
For i = 1 To UBound(sigRow)
If Len(sigRow(i)) > 0 Then
    For j = 0 To 8
    If InStr(1, VBA.UCase(sigRow(i)), Street(j), vbTextCompare) > 0 Then

    'Find the position of the street in order to get the suburb
    SpaceInName = InStr(1, VBA.UCase(sigRow(i)), Street(j), vbTextCompare) + Len(Street(j)) - 1

    'If its a po box then add 5 chars
    If VBA.Right(Street(j), 3) = "BOX" Then SpaceInName = SpaceInName + 5

    If ActiveSheet.Shapes("chkConfirm").ControlFormat.Value = 0 Then
    ActiveSheet.Range("Street") = VBA.Mid(sigRow(i), 1, SpaceInName)
    Else
      If MsgBox("Street Address: " & VBA.Mid(sigRow(i), 1, SpaceInName), vbQuestion + vbYesNo, "Confirm Details") = vbYes Then ActiveSheet.Range("Street") = VBA.Mid(sigRow(i), 1, SpaceInName)
    End If
    'Trim the Street, Number leaving the Suburb if its exists on the same line
    sigRow(i) = VBA.Mid(sigRow(i), SpaceInName) + 2
    sigRow(i) = Replace(sigRow(i), VBA.Mid(sigRow(i), 1, SpaceInName), "")

    GoTo PastAddress:
    End If
    Next j
End If
Next i
PastAddress:

'Mobile
For i = 1 To UBound(sigRow)
If Len(sigRow(i)) > 0 Then
    For j = 0 To 3
    Temp = Mb(j)
        If VBA.Left(VBA.UCase(sigRow(i)), Len(Temp)) = Temp Then
        If ActiveSheet.Shapes("chkConfirm").ControlFormat.Value = 0 Then
        ActiveSheet.Range("Mobile") = VBA.Mid(sigRow(i), Len(Temp) + 2)
        Else
          If MsgBox("Mobile: " & VBA.Mid(sigRow(i), Len(Temp) + 2), vbQuestion + vbYesNo, "Confirm Details") = vbYes Then ActiveSheet.Range("Mobile") = VBA.Mid(sigRow(i), Len(Temp) + 2)
        End If
    sigRow(i) = ""
    GoTo PastMobile:
    End If
    Next j
End If
Next i
PastMobile:

'Phone
For i = 1 To UBound(sigRow)
If Len(sigRow(i)) > 0 Then
    For j = 0 To 1
    Temp = Ph(j)
        If VBA.Left(VBA.UCase(sigRow(i)), Len(Temp)) = Temp Then

            'TODO: Detect the intl or national extension here.. or if we can from the postcode.
            If ActiveSheet.Shapes("chkConfirm").ControlFormat.Value = 0 Then
            ActiveSheet.Range("Phone") = VBA.Mid(sigRow(i), Len(Temp) + 3)
            Else
              If MsgBox("Phone: " & VBA.Mid(sigRow(i), Len(Temp) + 3), vbQuestion + vbYesNo, "Confirm Details") = vbYes Then ActiveSheet.Range("Phone") = VBA.Mid(sigRow(i), Len(Temp) + 3)
            End If

        sigRow(i) = ""
        GoTo PastPhone:
        End If
    Next j
End If
Next i
PastPhone:


'Email
For i = 1 To UBound(sigRow)
    If Len(sigRow(i)) > 0 Then
        'replace with regEx search
        If InStr(1, sigRow(i), "@", vbTextCompare) And InStr(1, VBA.UCase(sigRow(i)), ".CO", vbTextCompare) Then
        Dim email As String
        email = sigRow(i)
        email = Replace(VBA.UCase(email), "EMAIL:", "")
        email = Replace(VBA.UCase(email), "E-MAIL:", "")
        email = Replace(VBA.UCase(email), "E:", "")
        email = Replace(VBA.UCase(Trim(email)), "E ", "")
        email = VBA.LCase(email)

            If ActiveSheet.Shapes("chkConfirm").ControlFormat.Value = 0 Then
            ActiveSheet.Range("Email") = email
            Else
              If MsgBox("Email: " & email, vbQuestion + vbYesNo, "Confirm Details") = vbYes Then ActiveSheet.Range("Email") = email
            End If
        sigRow(i) = ""
        Exit For
        End If
    End If
Next i

'Now the only remaining items will be the postcode, suburb, country
'there shouldn't be any numbers (eg. from PoBox,Ph,Fax,Mobile) except for the Post Code

'Join the string and filter out the Post Code
Temp = Join(sigRow, vbCrLf)
Temp = Trim(Temp)

For i = 1 To Len(Temp)

Dim postCode As String
postCode = VBA.Mid(Temp, i, 4)

'In Australia PostCodes are 4 digits
If VBA.Mid(Temp, i, 1) <> " " And IsNumeric(postCode) Then

    If ActiveSheet.Shapes("chkConfirm").ControlFormat.Value = 0 Then
    ActiveSheet.Range("PostCode") = postCode
    Else
      If MsgBox("Post Code: " & postCode, vbQuestion + vbYesNo, "Confirm Details") = vbYes Then ActiveSheet.Range("PostCode") = postCode
    End If

    'Lookup the Suburb and State based on the PostCode, the PostCode sheet has the lookup
    Dim mySuburbArray As Range
    Set mySuburbArray = Sheets("PostCodes").Range("A2:B16670")

    Dim suburbs As String
    For j = 1 To mySuburbArray.Columns(1).Cells.Count
    If mySuburbArray.Cells(j, 1) = postCode Then
        'Check if the suburb is listed in the address
        If InStr(1, UCase(Temp), mySuburbArray.Cells(j, 2), vbTextCompare) > 0 Then

        'Set the Suburb and State
        ActiveSheet.Range("Suburb") = mySuburbArray.Cells(j, 2)
        Stat = mySuburbArray.Cells(j, 3)
        ActiveSheet.Range("State") = Stat

        'Knowing the State - for Australia we can get the telephone Ext
        PhExt = PhExtension(VBA.UCase(Stat))
        ActiveSheet.Range("PhExt") = PhExt

        'remove the phone extension from the number
        Dim prePhone As String
        prePhone = ActiveSheet.Range("Phone")
        prePhone = Replace(prePhone, PhExt & " ", "")
        prePhone = Replace(prePhone, "(" & PhExt & ") ", "")
        prePhone = Replace(prePhone, "(" & PhExt & ")", "")
        ActiveSheet.Range("Phone") = prePhone
        Exit For
        End If
    End If
    Next j
Exit For
End If
Next i

End Sub


Private Function PhExtension(ByVal State As String) As String
Select Case State
Case Is = "NSW"
PhExtension = "02"
Case Is = "QLD"
PhExtension = "07"
Case Is = "VIC"
PhExtension = "03"
Case Is = "NT"
PhExtension = "04"
Case Is = "WA"
PhExtension = "05"
Case Is = "SA"
PhExtension = "07"
Case Is = "TAS"
PhExtension = "06"
End Select
End Function

Private Function Ph(ByVal Num As Integer) As String
Select Case Num
Case Is = 0
Ph = "PH"
Case Is = 1
Ph = "PHONE"
'Case Is = 2
'Ph = "P"
End Select
End Function

Private Function Mb(ByVal Num As Integer) As String
Select Case Num
Case Is = 0
Mb = "MB"
Case Is = 1
Mb = "MOB"
Case Is = 2
Mb = "CELL"
Case Is = 3
Mb = "MOBILE"
'Case Is = 4
'Mb = "M"
End Select
End Function

Private Function Fax(ByVal Num As Integer) As String
Select Case Num
Case Is = 0
Fax = "FAX"
Case Is = 1
Fax = "FACSIMILE"
'Case Is = 2
'Fax = "F"
End Select
End Function

Private Function State(ByVal Num As Integer) As String
Select Case Num
Case Is = 0
State = "NSW"
Case Is = 1
State = "QLD"
Case Is = 2
State = "VIC"
Case Is = 3
State = "NT"
Case Is = 4
State = "WA"
Case Is = 5
State = "SA"
Case Is = 6
State = "TAS"
End Select
End Function

Private Function Street(ByVal Num As Integer) As String
Select Case Num
Case Is = 0
Street = " ST"
Case Is = 1
Street = " RD"
Case Is = 2
Street = " AVE"
Case Is = 3
Street = " AV"
Case Is = 4
Street = " CRES"
Case Is = 5
Street = " LOOP"
Case Is = 6
Street = "PO BOX"
Case Is = 7
Street = " STREET"
Case Is = 8
Street = " ROAD"
Case Is = 9
Street = " AVENUE"
Case Is = 10
Street = " CRESENT"
Case Is = 11
Street = " PARADE"
Case Is = 12
Street = " PDE"
Case Is = 13
Street = " LANE"
Case Is = 14
Street = " COURT"
Case Is = 15
Street = " BLVD"
Case Is = 16
Street = "P.O. BOX"
Case Is = 17
Street = "P.O BOX"
Case Is = 18
Street = "PO BOX"
Case Is = 19
Street = "POBOX"
End Select
End Function
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.