it-swarm.com.de

Wie wird die Freiform-Straße/Post-Adresse aus dem Text und in Komponenten analysiert?

Wir sind hauptsächlich in den USA tätig und versuchen die Benutzererfahrung zu verbessern, indem wir alle Adressfelder in einem einzigen Textbereich zusammenfassen. Es gibt jedoch einige Probleme:

  • Die Adresse, die der Benutzer eingibt, ist möglicherweise nicht korrekt oder hat ein Standardformat
  • Die Adresse muss zur Verarbeitung von Kreditkartenzahlungen in Teile (Straße, Stadt, Bundesland usw.) unterteilt werden
  • Benutzer können mehr als nur ihre Adresse eingeben (wie Name oder Firma)
  • Google kann dies tun, aber die Nutzungsbedingungen und Abfragebeschränkungen sind untragbar, insbesondere bei einem knappen Budget

Anscheinend ist dies eine häufige Frage:

Gibt es eine Möglichkeit, eine Adresse vom Text um sie herum zu isolieren und in Stücke zu zerlegen? Gibt es einen regulären Ausdruck, um Adressen zu analysieren?

108
Matt

Ich habe diese Frage oft gesehen, als ich für eine Adressprüfungsfirma gearbeitet habe. Ich poste die Antwort hier, um sie Programmierern zugänglicher zu machen, die mit der gleichen Frage suchen. Die Firma, in der ich gearbeitet habe, hat Milliarden von Adressen verarbeitet, und wir haben dabei viel gelernt.

Zunächst müssen wir ein paar Dinge über Adressen verstehen.

Adressen sind nicht regulär

Dies bedeutet, dass reguläre Ausdrücke nicht vorhanden sind. Ich habe alles gesehen, von einfachen regulären Ausdrücken, die Adressen in einem ganz bestimmten Format zuordnen:

/\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}) (Gericht | ct | Straße | st | Antrieb | dr | lane | ln | road | 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 | NC | ND | NE | NH | NJ | NM | NV | NY | OH | OK | ODER | PA | RI | SC | SD | TN | TX | UT | VA | VI | VT | WA | WI | WV | WY) ([\ s | \, |.] +)? (\ S +\d {5})? ([\ S | \, |.] +)/i

... bis this wobei eine Datei mit mehr als 900 Zeilen einen supermassiven regulären Ausdruck generiert, der noch besser passt. Ich empfehle diese nicht (zum Beispiel hier ist eine Fiedel der obigen Regex, die viele Fehler macht ). Es gibt keine einfache Zauberformel, um dies zum Laufen zu bringen. In der Theorie und durch Theorie ist es nicht möglich, Adressen mit einem regulären Ausdruck abzugleichen.

SPS Publication 28 dokumentiert die vielen möglichen Adressformate mit all ihren Schlüsselwörtern und Variationen. Am schlimmsten ist, dass Adressen häufig nicht eindeutig sind. Wörter können mehr als eine Sache bedeuten ("St" kann "Saint" oder "Street" sein) und es gibt Wörter, von denen ich mir ziemlich sicher bin, dass sie erfunden wurden. (Wer wusste, dass "Stravenue" ein Straßensuffix war?)

Sie benötigen einen Code, der Adressen wirklich versteht. Wenn dieser Code existiert, ist er ein Geschäftsgeheimnis. Aber du könntest wahrscheinlich deine eigene Rolle spielen, wenn du wirklich darauf stehst.

Adressen kommen in unerwarteten Formen und Größen

Hier sind einige erfundene (aber vollständige) Adressen:

1)  102 main street
    Anytown, state

2)  400n 600e #2, 52173

3)  p.o. #104 60203

Auch diese sind möglicherweise gültig:

4)  829 LKSDFJlkjsdflkjsdljf Bkpw 12345

5)  205 1105 14 90210

Offensichtlich sind diese nicht standardisiert. Satzzeichen und Zeilenumbrüche sind nicht garantiert. Folgendes ist los:

  1. Nummer 1 ist vollständig, da sie eine Straße, eine Stadt und ein Bundesland enthält. Mit diesen Informationen gibt es genug, um die Adresse zu identifizieren, und sie kann als "lieferbar" angesehen werden (mit einer gewissen Standardisierung).

  2. Nummer 2 ist vollständig, da sie auch eine Straße (mit Sekundär-/Einheitennummer) und eine 5-stellige Postleitzahl enthält, die zur Identifizierung einer Straße ausreicht Adresse.

  3. Nummer 3 ist ein vollständiges Postfachformat, da es eine Postleitzahl enthält.

  4. Nummer 4 ist ebenfalls vollständig, da die Postleitzahl ist eindeutig bedeutet, dass eine private Einheit oder ein Unternehmen diesen Adressraum gekauft hat. Eine eindeutige Postleitzahl steht für großvolumige oder konzentrierte Zustellräume. Alles, was an die Postleitzahl 12345 adressiert ist, geht an General Electric in Schenectady, NY. Dieses Beispiel wird niemanden erreichen, aber der USPS wäre immer noch in der Lage, es zu liefern.

  5. Nummer 5 ist auch vollständig, ob Sie es glauben oder nicht. Mit genau diesen Zahlen kann die vollständige Adresse ermittelt werden, wenn sie anhand einer Datenbank mit allen möglichen Adressen analysiert wird. Das Ausfüllen der fehlenden Richtungsangaben, des sekundären Bezeichners und der Postleitzahl + 4 ist trivial, wenn Sie jede Nummer als Komponente sehen. So sieht es aus, vollständig erweitert und standardisiert:

205 N 1105 W Fewo 14

Beverly Hills CA 90210-5221

Adressdaten sind nicht Ihre eigenen

In den meisten Ländern, in denen lizenzierte Anbieter offizielle Adressdaten erhalten, gehören die Adressdaten selbst der zuständigen Behörde. In den USA besitzt der USPS die Adressen. Das Gleiche gilt für Canada Post, Royal Mail und andere, obwohl jedes Land das Eigentum ein wenig anders durchsetzt oder definiert. Es ist wichtig zu wissen, dass die Adressdatenbank normalerweise nicht rückentwickelt werden darf. Sie müssen vorsichtig sein, wie Sie Daten erfassen, speichern und verwenden.

Google Maps ist eine häufige Adresse für schnelle Adresskorrekturen, aber das TOS ist ziemlich unerschwinglich. Beispielsweise können Sie ihre Daten oder APIs nicht ohne Anzeige einer Google Map und nur für nichtkommerzielle Zwecke verwenden (es sei denn, Sie zahlen dafür), und Sie können die Daten nicht speichern (außer für temporäres Caching). Macht Sinn. Die Daten von Google gehören zu den besten der Welt. Google Maps überprüft die Adresse jedoch nicht . Wenn eine Adresse nicht vorhanden ist, wird weiterhin angezeigt, wo sich die Adresse befinden würde , wenn sie vorhanden wäre (versuchen Sie es mit Ihrer eigene Straße; verwenden Sie eine Hausnummer, von der Sie wissen, dass sie nicht existiert). Das ist manchmal nützlich, aber sei dir dessen bewusst.

Die Verwendungsrichtlinie von Nominatim ist in ähnlicher Weise einschränkend, insbesondere für den großvolumigen und kommerziellen Gebrauch, und die Daten stammen größtenteils aus freien Quellen, weshalb sie nicht so gut gepflegt sind (wie dies bei offenen Projekten der Fall ist). - Dies entspricht jedoch möglicherweise noch Ihren Anforderungen. Es wird von einer tollen Community unterstützt.

Der USPS selbst hat eine API, aber es geht viel runter und kommt ohne Garantien oder Unterstützung. Es könnte auch schwer zu bedienen sein. Einige Leute setzen es sparsam und ohne Probleme ein. Es ist jedoch leicht zu übersehen, dass der USPS verlangt, dass Sie die API nur zur Bestätigung der Adressen verwenden, um über diese zu versenden.

Die Leute erwarten, dass Adressen schwierig sind

Leider haben wir unsere Gesellschaft darauf konditioniert, dass Adressen kompliziert werden. Es gibt Dutzende guter UX-Artikel über das Internet, aber wenn Sie ein Adressformular mit einzelnen Feldern haben, erwarten dies die Benutzer, auch wenn es für Edge-Case-Adressen schwieriger ist, die nicht in das Adressbuch passen Format, das das Formular erwartet, oder möglicherweise erfordert das Formular ein Feld, das es nicht sollte. Oder Benutzer wissen nicht, wo sie einen bestimmten Teil ihrer Adresse platzieren sollen.

Ich könnte mich heutzutage immer wieder mit der schlechten Benutzeroberfläche von Checkout-Formularen befassen, aber stattdessen sage ich nur, dass das Kombinieren der Adressen in einem einzigen Feld eine willkommene Änderung sein wird - Die Benutzer können ihre Adresse so eingeben, wie sie es für richtig halten, anstatt zu versuchen, Ihr langwieriges Formular herauszufinden. Diese Änderung ist jedoch unerwartet und wird den Benutzern möglicherweise zunächst etwas unangenehm erscheinen. Sei dir dessen einfach bewusst.

Ein Teil dieser Schmerzen kann gelindert werden, indem Sie das Feld vor der Adresse nach vorne stellen. Wenn sie das Feld "Land" zuerst ausfüllen, wissen Sie, wie Sie Ihr Formular anzeigen. Möglicherweise haben Sie eine gute Möglichkeit, mit US-Adressen in Einzelfeldern umzugehen. Wenn Sie also "USA" auswählen, können Sie Ihr Formular auf ein einziges Feld reduzieren, andernfalls werden die Komponentenfelder angezeigt. Nur Dinge zum Nachdenken!

Jetzt wissen wir, warum es schwer ist; was kannst du dagegen tun

Der USPS lizenziert Anbieter über einen Prozess namens CASS ™ -Zertifizierung, um Kunden überprüfte Adressen bereitzustellen. Diese Anbieter haben Zugriff auf die USPS-Datenbank, die monatlich aktualisiert wird. Ihre Software muss strengen Standards entsprechen, um zertifiziert zu werden, und sie erfordern nicht oft die Zustimmung zu den oben beschriebenen einschränkenden Bedingungen.

Es gibt viele CASS-zertifizierte Unternehmen, die Listen verarbeiten können oder über APIs verfügen: Melissa Data, Experian QAS und SmartyStreets, um nur einige zu nennen.

(Da ich nach "Werbung" Ausschau gehalten habe, habe ich meine Antwort an dieser Stelle abgeschnitten. Es liegt an Ihnen, eine Lösung zu finden, die für Sie funktioniert.)

Die Wahrheit: Wirklich, Leute, ich arbeite bei keiner dieser Firmen. Es ist keine Werbung.

256
Matt

libpostal: eine Open-Source-Bibliothek zum Parsen von Adressen, Training mit Daten aus OpenStreetMap, OpenAddresses und OpenCage.

https://github.com/openvenues/libpostal ( weitere Informationen dazu )

Andere Tools/Dienste:

10

Es gibt viele Straßenadressen-Parser. Es gibt zwei grundlegende Varianten: eine mit Datenbanken von Ortsnamen und Straßennamen und solche, die dies nicht tun. 

Ein Straßenadressen-Parser für reguläre Ausdrücke kann ohne großen Aufwand eine Erfolgsquote von etwa 95% erreichen. Dann schlagen Sie die ungewöhnlichen Fälle an. Das Perl One in CPAN, "Geo :: StreetAddress :: US", geht es darum. Es gibt Python- und Javascript-Ports, alles Open Source. Ich habe eine verbesserte Version in Python, die die Erfolgsquote durch die Behandlung von mehr Fällen etwas erhöht. Um die letzten 3% richtig zu machen, benötigen Sie jedoch Datenbanken, die bei der Disambiguierung helfen. 

Eine Datenbank mit 3-stelligen Postleitzahlen und Namen und Abkürzungen von US-Bundesstaaten ist eine große Hilfe. Wenn ein Parser eine konsistente Postleitzahl und einen Statusnamen sieht, kann er sich an das Format anschließen. Dies funktioniert sehr gut für die USA und Großbritannien. 

Die korrekte Straßenadressenanalyse beginnt am Ende und arbeitet rückwärts. So machen es die USPS-Systeme. Am Ende sind die Adressen am wenigsten mehrdeutig, wobei Ländernamen, Städtenamen und Postleitzahlen relativ leicht zu erkennen sind. Straßennamen können normalerweise isoliert werden. Orte auf der Straße sind am komplexesten zu analysieren; dort trifft man auf Dinge wie "Fifth Floor" und "Staples Pavillion". Dann ist eine Datenbank eine große Hilfe. 

10
John Nagle

UPDATE: Geocode.xyz funktioniert jetzt weltweit. Beispiele finden Sie unter https://geocode.xyz

Für USA, Mexiko und Kanada siehe geocoder.ca .

Zum Beispiel:

Eingabe: etwas in der Nähe der Kreuzung von Main und Arthur Kill New York

Ausgabe:

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

Sie können die Ergebnisse auch in der Weboberfläche überprüfen oder als Json oder Jsonp ausgeben. z.B. Ich suche Restaurants in der Nähe von 123 Main Street, New York

8
Ervin Ruci

Für das Analysieren von US-Adressen

Ich bevorzuge die Verwendung des usaddress-Pakets, das in pip nur für usaddress verfügbar ist

python3 -m pip install usaddress

Dokumentation
PyPi

Dies funktionierte gut für mich für US-Adresse.

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

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

if __== '__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)))

Ausführen der Datei 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"}
1
theBuzzyCoder

Kein Code? Zum Schämen!

Hier ist ein einfacher JavaScript-Adressenparser. Es ist ziemlich schrecklich für jeden einzelnen Grund, den Matt in seiner obigen Dissertation vorgibt (womit ich fast zu 100% einverstanden bin: Adressen sind komplexe Typen, und Menschen machen Fehler; besser auslagern und automatisieren - wenn Sie es sich leisten können).

Aber anstatt zu weinen, entschied ich mich zu versuchen:

Dieser Code funktioniert in Ordnung für die Analyse der meisten Esri-Ergebnisse für findAddressCandidate und auch für andere (umgekehrte) Geocodierer, die einzeilige Adressen zurückgeben, bei denen Straße/Stadt/Bundesstaat durch Kommas getrennt werden. Sie können erweitern, wenn Sie länderspezifische Parser wollen oder schreiben möchten. Oder nutzen Sie einfach diese Fallstudie, wie schwierig diese Übung sein kann oder wie schlecht ich bei JavaScript bin. Ich gebe zu, dass ich nur etwa dreißig Minuten dafür aufgewendet habe (zukünftige Iterationen könnten Caches, Zip-Validierung und Status-Lookups sowie den Kontext des Benutzerstandorts hinzufügen), aber es funktionierte für meinen Anwendungsfall: Der Endbenutzer sieht eine Form, in der die Antwort auf Geocodesuche in 4 analysiert wird Textfelder. Wenn die Adressanalyse falsch ist (was selten ist, wenn die Quelldaten schlecht waren), ist dies keine große Sache - der Benutzer kann dies überprüfen und beheben! (Aber für automatisierte Lösungen kann entweder verworfen/ignoriert oder als Fehler markiert werden, sodass dev entweder das neue Format unterstützen oder Quelldaten korrigieren kann.)

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

1

Eine weitere Option für in den USA ansässige Adressen ist YAddress (von der Firma, für die ich arbeite).

Viele Antworten auf diese Frage schlagen Geokodierungswerkzeuge als Lösung vor. Es ist wichtig, Adressanalyse und Geokodierung nicht zu verwechseln. Sie sind nicht gleich. Während Geocoder eine Adresse als Nebeneffekt in Komponenten aufteilen können, stützen sie sich normalerweise auf nicht standardmäßige Adressensätze. Dies bedeutet, dass eine vom Geocodierer analysierte Adresse möglicherweise nicht mit der offiziellen Adresse übereinstimmt. Zum Beispiel, was Google Geocoding API "6th Ave" in Manhattan nennt, nennt USPS "Avenue of the Americas".

0
Michael Diomin

Ich bin zu spät zur Party, hier ist ein Excel VBA-Skript, das ich vor Jahren für Australien geschrieben habe. Es kann leicht geändert werden, um andere Länder zu unterstützen. Ich habe hier ein GitHub-Repository des C # -Codes erstellt. Ich habe es auf meiner Site gehostet und Sie können es hier herunterladen: http://jeremythompson.net/rocks/ParseAddress.xlsm

Strategie

Für jedes Land mit einem Postcode, der numerisch ist oder mit einem RegEx abgeglichen werden kann, funktioniert meine Strategie sehr gut:

  1. Zuerst erkennen wir den Vor- und Nachnamen, von dem angenommen wird, dass er die oberste Zeile ist. Es ist einfach, den Namen zu überspringen und mit der Adresse zu beginnen, indem Sie das Kontrollkästchen deaktivieren ("Name ist oberste Zeile", wie unten gezeigt).

  2. Als nächstes ist es sicher zu erwarten, dass die Adresse, die aus der Straße und der Hausnummer besteht, vor dem Vorort steht und dass St, Pde, Ave, Av, Rd, Cres, Schleife usw. ein Trennzeichen ist.

  3. Das Erkennen des Vororts gegen den Staat und sogar das Land kann die ausgefeiltesten Parser austricksen, da es Konflikte geben kann. Um dies zu überwinden benutze ich einen PostCode-Look, der darauf basiert, dass nach dem Strippen von Straßen- und Apartment-/Unit-Nummern sowie der PoBox, Ph, Fax , Mobile etc, nur der PostCode Nummer wird bleiben. Dies ist einfach mit einem regEx abzugleichen, um dann die Vororte und das Land nachzuschlagen.

Ihr Nationaler Postdienst stellt Ihnen kostenlos eine Liste der Postleitzahlen mit Vororten und Bundesstaaten zur Verfügung, die Sie in einer Excel-Tabelle, einer DB-Tabelle, einer Text-/JSON-/XML-Datei usw. speichern können.

  1. Da einige Postleitzahlen mehrere Vororte haben, prüfen wir, welcher Vorort in der Adresse enthalten ist.

Beispiel

enter image description here

VBA-Code

HAFTUNGSAUSSCHLUSS, ich weiß, dass dieser Code nicht perfekt oder sogar gut geschrieben ist, aber er ist sehr einfach in jede Programmiersprache zu konvertieren und in jeder Art von Anwendung auszuführen. Die Strategie ist die Antwort, abhängig von Ihrem Land und Ihren Regeln. Nehmen Sie diesen Code als Beispiel :

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
0
Jeremy Thompson

Wenn Sie sich auf OSM-Daten verlassen wollen, ist libpostal sehr leistungsfähig und behandelt die häufigsten Vorbehalte mit Adresseingaben.

0

In einem unserer Projekte haben wir den folgenden Adressparser verwendet. Es analysiert Adressen für die meisten Länder der Welt mit guter Genauigkeit. 

http://address-parser.net/

Es ist als eigenständige Bibliothek oder als Live-API verfügbar. 

0
Waqas Anwar