So analysieren Sie Freiform-Straßen- / Postanschriften aus Text heraus und in Komponenten


136

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

  • Die vom Benutzer eingegebene Adresse ist möglicherweise nicht korrekt oder hat ein Standardformat
  • Die Adresse muss in Teile (Straße, Stadt, Bundesland usw.) unterteilt werden, um Kreditkartenzahlungen verarbeiten zu können
  • Benutzer können mehr als nur ihre Adresse eingeben (wie ihren Namen oder ihre Firma damit)
  • Google kann dies tun, aber die Nutzungsbedingungen und Abfragegrenzen sind unerschwinglich, insbesondere bei einem knappen Budget

Anscheinend ist dies eine häufige Frage:

Gibt es eine Möglichkeit, eine Adresse vom Text zu isolieren und in Teile zu zerlegen? Gibt es einen regulären Ausdruck zum Analysieren von Adressen?


Die folgenden Antworten sind nützlicher, da sie das globale Problem nicht ignorieren - diese Adressen passen nicht zu einem gemeinsamen Muster.
Marc Maxmeister

Antworten:


289

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

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

Adressen sind nicht regelmäßig

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 entsprechen, bis zu diesem:

/ \ 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 | fahren | dr | Spur | ln | Straße | 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 | OR | PA | RI | SC | SD | TN | TX | UT | VA | VI | VT | WA | WI | WV | WY) ([\ s |, |.] +)? (\ S + \ d {5})? ([\ S |, |.] +) / i

... auf diese , wo eine 900+ Line-Klasse - Datei auf der Fliege einen super regulären Ausdruck erzeugt noch mehr entsprechen. Ich empfehle diese nicht (zum Beispiel hier eine Geige der obigen Regex, die viele Fehler macht ). Es gibt keine einfache Zauberformel, um dies zum Laufen zu bringen. In der Theorie und von der Theorie ist es nicht möglich , Adressen mit einem regulären Ausdruck übereinstimmen.

Die USPS-Publikation 28 dokumentiert die vielen möglichen Adressformate mit all ihren Schlüsselwörtern und Variationen. Am schlimmsten ist, dass Adressen oft 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 ist?)

Sie benötigen einen Code, der Adressen wirklich versteht, und wenn dieser Code vorhanden ist, ist er ein Geschäftsgeheimnis. Aber Sie könnten wahrscheinlich Ihre eigenen rollen, wenn Sie wirklich darauf stehen.

Adressen gibt es 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. Interpunktion und Zeilenumbrüche sind nicht garantiert. Folgendes ist los:

  1. Nummer 1 ist vollständig, da sie eine Adresse sowie eine Stadt und ein Bundesland enthält. Mit diesen Informationen ist die Adresse ausreichend identifiziert und kann als "lieferbar" angesehen werden (mit einigen Standardisierungen).

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

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

  4. Nummer 4 ist auch vollständig, da die Postleitzahl eindeutig ist , was bedeutet, dass eine private Einrichtung oder ein Unternehmen diesen Adressraum gekauft hat. Eine eindeutige Postleitzahl gilt für großvolumige oder konzentrierte Lieferräume. Alles, was an die Postleitzahl 12345 adressiert ist, geht an General Electric in Schenectady, NY. Dieses Beispiel wird niemanden besonders erreichen, aber der USPS könnte es trotzdem liefern.

  5. Nummer 5 ist auch vollständig, ob Sie es glauben oder nicht. Mit nur diesen Nummern kann die vollständige Adresse ermittelt werden, wenn sie anhand einer Datenbank aller möglichen Adressen analysiert wird. Das Ausfüllen der fehlenden Anweisungen, 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 Apt 14

Beverly Hills CA 90210-5221

Adressdaten sind nicht Ihre eigenen

In den meisten Ländern, die lizenzierten Anbietern offizielle Adressdaten zur Verfügung stellen, gehören die Adressdaten selbst der zuständigen Behörde. In den USA besitzt der USPS die Adressen. Gleiches gilt für Canada Post, Royal Mail und andere, obwohl jedes Land das Eigentum ein wenig anders erzwingt oder definiert. Dies zu wissen ist wichtig, da es normalerweise das Reverse Engineering der Adressdatenbank verbietet. Sie müssen vorsichtig sein, wie Sie die Daten erfassen, speichern und verwenden.

Google Maps ist eine häufige Anlaufstelle für schnelle Adresskorrekturen, aber die Nutzungsbedingungen sind eher 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), 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 existiert, wird es noch zeigen Ihnen , wo die Adresse wäre , wenn es tat exist (versuchen Sie es auf Ihrem eigenen Straße, verwenden Sie eine Hausnummer , dass Sie wissen , ist nicht vorhanden). Dies ist manchmal nützlich, aber seien Sie sich dessen bewusst.

Die Nutzungsrichtlinien von Nominatim sind ähnlich einschränkend, insbesondere für großvolumige und kommerzielle Zwecke. Die Daten stammen größtenteils aus freien Quellen und sind daher nicht so gut gepflegt (wie dies bei offenen Projekten der Fall ist). Dies kann jedoch weiterhin zutreffen Deine Bedürfnisse. Es wird von einer großartigen Community unterstützt.

Der USPS selbst verfügt über eine API, die jedoch stark abnimmt und weder Garantien noch Unterstützung bietet . Es könnte auch schwer zu bedienen sein. Einige Leute verwenden es sparsam und ohne Probleme. Es ist jedoch leicht zu übersehen, dass der USPS verlangt, dass Sie seine API nur zur Bestätigung von Adressen verwenden, um sie zu versenden.

Die Leute erwarten, dass Adressen schwierig sind

Leider haben wir unsere Gesellschaft darauf konditioniert, dass Adressen kompliziert sein werden. Es gibt Dutzende guter UX-Artikel im Internet darüber. Wenn Sie jedoch ein Adressformular mit einzelnen Feldern haben, erwarten die Benutzer dies, auch wenn es für Edge-Case-Adressen, die nicht zu den Adressen passen, schwieriger ist 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 ablegen sollen.

Ich könnte heutzutage immer wieder über die schlechte UX von Checkout-Formularen sprechen, aber stattdessen möchte ich nur sagen, dass das Kombinieren der Adressen in einem einzigen Feld eine willkommene Änderung ist - die Leute können ihre Adresse so eingeben, wie sie es für richtig halten , anstatt zu versuchen, Ihre lange Form herauszufinden. Diese Änderung ist jedoch unerwartet und kann für Benutzer zunächst etwas unangenehm sein. Sei dir dessen einfach bewusst.

Ein Teil dieser Schmerzen kann gelindert werden, indem das Feld vor die Adresse gestellt wird. Wenn sie zuerst das Länderfeld ausfüllen, wissen Sie, wie Sie Ihr Formular anzeigen können. Vielleicht haben Sie eine gute Möglichkeit, mit US-Adressen mit einem Feld umzugehen. Wenn sie also USA auswählen, können Sie Ihr Formular auf ein einzelnes Feld reduzieren, andernfalls werden die Komponentenfelder angezeigt. Nur Dinge zum Nachdenken!

Jetzt wissen wir, warum es schwer ist; Was können Sie dagegen tun?

Der USPS lizenziert Anbieter über einen Prozess namens CASS ™ -Zertifizierung, um Kunden verifizierte 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 wegen "Werbung" Flak bekomme, 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 in keiner dieser Firmen. Es ist keine Werbung.


1
Was ist mit südamerikanischen (Uruguay) Adressen? : D
Bart Calixto

11
@Brian - Vielleicht, weil der Benutzer viele nützliche Informationen für diejenigen bereitgestellt hat, die die Frage und Antwort lesen, unabhängig davon, ob er das Produkt seines Unternehmens verwendet oder nicht.
Zarepheth

7
@Brian Diese Seiten sind Content Scraper. Sie mischen Inhalte, um SERP-Rankings zu erhalten. Ich habe sie noch nie gesehen. Ich habe diesen Inhalt noch nie zuvor oder nachher irgendwo anders gepostet.
Matt

2
@khuderm Ich habe gerade bemerkt, als ich Ihren Kommentar gelesen habe, dass alle abweichenden Kommentare verschwunden sind. Ich bin mir nicht sicher, wie / wann das passiert ist. Wenn Sie jedoch den Bearbeitungsverlauf meiner Antwort lesen, finden Sie einen direkten Verweis auf einen US-Adressenextraktor, der Ihnen möglicherweise hilft. Ich habe es erstellt, als ich bei meinem letzten Job gearbeitet habe, aber es ist proprietärer Code, sodass ich ihn nicht teilen kann ... aber sie existieren. Hoffentlich ist hilfreich.
Matt

2
Hoppla. Entschuldigung @Matt. Nun, ich habe angefangen, dir durch deine Fragen und auch durch Github zu folgen. Sehr beeindruckend bist du.
Sayka

27

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

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

Andere Tools / Dienstleistungen:


13

Es gibt viele Parser für Straßenadressen. Sie gibt es in zwei Grundvarianten: solche mit Datenbanken mit Orts- und Straßennamen und solche ohne.

Ein Straßenadress-Parser mit regulären Ausdrücken kann ohne große Probleme eine Erfolgsrate von bis zu 95% erreichen. Dann fangen Sie an, die ungewöhnlichen Fälle zu treffen. Das Perl in CPAN, "Geo :: StreetAddress :: US", ist ungefähr so ​​gut. Es gibt Python- und Javascript-Ports, alle Open Source. Ich habe eine verbesserte Version in Python, die die Erfolgsrate leicht erhöht, indem mehr Fälle behandelt werden. Um die letzten 3% richtig zu machen, benötigen Sie Datenbanken, um bei der Disambiguierung zu helfen.

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

Die richtige Analyse der Straßenadressen beginnt am Ende und funktioniert rückwärts. So machen es die USPS-Systeme. Adressen sind am Ende am wenigsten mehrdeutig, da Ländernamen, Städtenamen und Postleitzahlen relativ leicht zu erkennen sind. Straßennamen können normalerweise isoliert werden. Orte auf Straßen sind am komplexesten zu analysieren. Dort begegnet man Dingen wie "Fifth Floor" und "Staples Pavillion". Dann ist eine Datenbank eine große Hilfe.


Es gibt auch das CPAN-Modul Lingua: EN :: AddressParse. Es ist zwar langsamer als "Geo :: StreetAddress :: US", bietet aber eine höhere Erfolgsquote.
Kim Ryan

8

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

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

Beispielsweise:

Input: Etwas in der Nähe der Kreuzung von Main und Arthur Kill Rd 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 nach Restaurants in der 123 Main Street, New York


Wie haben Sie das Adressanalyse-System mit openaddress implementiert? Verwenden Sie eine Brute-Force-Strategie?
Nithin K Anil

1
Was meinst du mit "Brute Force"? Das Aufteilen von Text in alle möglichen Kombinationen möglicher Adresszeichenfolgen und das Vergleichen jeder einzelnen mit einer Adressdatenbank ist nicht praktikabel und benötigt viel mehr Zeit, um eine Antwort zu geben, als dieses System. Offene Adressen sind eine der Datenquellen für die Erstellung eines "Trainingssatzes" von Adressformaten für den Algorithmus. Diese Informationen werden verwendet, um Adressen aus unstrukturiertem Text zu analysieren.
Ervin Ruci

2
Ein anderes ähnliches System ist Geo :: libpostal ( perltricks.com/article/announcing-geo--libpostal ). Sie verwenden anscheinend auch openstreetmap und openaddresses, um Adressvorlagen im laufenden Betrieb zu erstellen
Ervin Ruci

Ich habe gerade den Geoparser von geocode.xyz (Text einsenden, Standort wiederherstellen) an Hunderten von tatsächlichen Adressen getestet. Angesichts der Seite an Seite mit der API von Google Map und eines globalen Satzes von Adressen schlug geocode.xyzdie scantextMethode die meiste Zeit fehl. Es wählte immer "Genf, USA" gegenüber "Genf, Schweiz" und war im Allgemeinen US-voreingenommen.
Marc Maxmeister

Das hängt vom Kontext ab. geocode.xyz/?scantext=Geneva,% 20Schweiz wird produzieren: Match Location Genf, Schweiz, CH Konfidenz-Score: 0,8, während geocode.xyz/?scantext=Geneva,% 20USA Match-Location Genf, US-Konfidenz-Score: 1,0 produziert. Sie können Region Bias wie folgt: geocode.xyz/?scantext=Geneva,%20USA®ion=CH
Ervin Ruci

4

Kein Code? Zum Schämen!

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

Aber anstatt zu weinen, beschloss ich zu versuchen:

Dieser Code funktioniert zum Parsen der meisten Esri-Ergebnisse fürfindAddressCandidateund auch mit einigen anderen (umgekehrten) Geocodierern, die eine einzeilige Adresse zurückgeben, bei der Straße / Stadt / Bundesland durch Kommas getrennt sind. Sie können erweitern, wenn Sie länderspezifische Parser möchten oder schreiben. Oder verwenden Sie dies einfach als Fallstudie, wie herausfordernd diese Übung sein kann oder wie mies ich bei JavaScript bin. Ich gebe zu, dass ich nur etwa 30 Minuten damit verbracht habe (zukünftige Iterationen könnten Caches, Zip-Validierung und Status-Lookups sowie den Kontext des Benutzerstandorts hinzufügen), aber es hat für meinen Anwendungsfall funktioniert: Der Endbenutzer sieht ein Formular, das die Geocode-Suchantwort in 4 analysiert Textfelder. Wenn die Adressanalyse falsch ist (was selten vorkommt, es sei denn, die Quelldaten waren schlecht), ist dies keine große Sache - der Benutzer kann sie überprüfen und beheben! (Bei automatisierten Lösungen kann dies jedoch entweder verworfen / ignoriert oder als Fehler gekennzeichnet werden, sodass Entwickler entweder das neue Format unterstützen oder Quelldaten korrigieren können.)

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


Haftungsausschluss: Meine Kunden besitzen ihre Adressdaten und betreiben ihre eigenen Esri-Server. Wenn Sie Daten von Google greifen, OSM, ArcGisOnline, oder wo auch immer, stellen Sie sicher , dass es OK zu speichern und verwenden es (viele Dienste haben Beschränkungen, wie Sie speichern können, und wie lange)
nothingisnecessary

Die erste Antwort oben macht einen überzeugenden Fall, dass dieses Problem mit regulären Ausdrücken nicht lösbar ist, wenn Sie mit einer globalen Adressliste arbeiten. 200 Länder haben zu viele Ausnahmen. In meinen Tests können Sie das Land anhand einer Zeichenfolge ziemlich zuverlässig bestimmen und dann für jedes Land einen bestimmten regulären Ausdruck nachschlagen - wahrscheinlich funktionieren die besseren APIs so.
Marc Maxmeister


2

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

Viele Antworten auf diese Frage schlagen Geokodierungswerkzeuge als Lösung vor. Es ist wichtig, das Parsen und Geokodieren von Adressen nicht zu verwechseln. Sie sind nicht gleich. Während Geocodierer 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 Geocoder analysierte Adresse möglicherweise nicht mit der offiziellen Adresse übereinstimmt. Was die Google-Geokodierungs-API beispielsweise in Manhattan "6th Ave" nennt, nennt USPS "Avenue of the Americas".


2

Für das Parsen von US-Adressen

Ich bevorzuge die Verwendung eines 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 __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)))

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

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.


1
Aber es ist ein bezahltes Produkt.
Jeremy Thompson

0

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 Website gehostet und Sie können es hier herunterladen: http://jeremythompson.net/rocks/ParseAddress.xlsm

Strategie

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

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

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

  3. Das Erkennen des Vorortes gegenüber dem Staat und sogar dem Land kann die anspruchsvollsten Parser austricksen, da es zu Konflikten kommen kann. Um dies zu überwinden verwende ich einen PLZ Look basierte auf der Tatsache , dass nach der Strippe Straße und Wohnung / Einheit Zahlen sowie das PoBox, Ph, Fax , Handy usw., nur die Postleitzahl Zahl bleiben wird. Dies ist einfach mit einem regEx abzugleichen, um dann die Vororte und das Land nachzuschlagen.

Ihr nationaler Postdienst stellt Ihnen kostenlos eine Liste mit 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 schließlich, welcher Vorort in der Adresse angezeigt wird.

Beispiel

Geben Sie hier die Bildbeschreibung ein

VBA-Code

HAFTUNGSAUSSCHLUSS, ich weiß, dass dieser Code nicht perfekt oder sogar gut geschrieben ist, aber es ist sehr einfach, ihn in eine beliebige Programmiersprache zu konvertieren und in jeder Art von Anwendung auszuführen. Die Strategie ist die Antwort, abhängig von Ihrem Land und den 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
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.