Comment analyser une rue / adresse postale de forme libre hors texte et en composants

136

Nous faisons des affaires principalement aux États-Unis et essayons d'améliorer l'expérience utilisateur en combinant tous les champs d'adresse dans une seule zone de texte. Mais il y a quelques problèmes:

  • L'adresse saisie par l'utilisateur peut ne pas être correcte ou dans un format standard
  • L'adresse doit être séparée en parties (rue, ville, état, etc.) pour traiter les paiements par carte de crédit
  • Les utilisateurs peuvent saisir plus que leur adresse (comme leur nom ou leur entreprise)
  • Google peut le faire, mais les conditions d'utilisation et les limites de requêtes sont prohibitives, en particulier avec un budget serré

Apparemment, c'est une question courante:

Existe-t-il un moyen d'isoler une adresse du texte qui l'entoure et de la diviser en morceaux? Existe-t-il une expression régulière pour analyser les adresses?

Mat
la source
Les réponses ci-dessous sont plus utiles car elles n'ignorent pas le problème global - les adresses ne correspondent pas à un modèle commun.
Marc Maxmeister

Réponses:

290

J'ai beaucoup vu cette question lorsque je travaillais pour une entreprise de vérification d'adresse. Je poste la réponse ici pour la rendre plus accessible aux programmeurs qui recherchent avec la même question. L'entreprise dans laquelle je travaillais a traité des milliards d'adresses et nous avons beaucoup appris au cours du processus.

Tout d'abord, nous devons comprendre quelques éléments concernant les adresses.

Les adresses ne sont pas régulières

Cela signifie que les expressions régulières sont supprimées. J'ai tout vu, des simples expressions régulières qui correspondent aux adresses dans un format très spécifique, à ceci:

/ \ 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}) (cour | ct | rue | st | lecteur | dr | voie | ln | route | rd | boulevard) ([\ 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

... à ceci où un fichier de classe de ligne de plus de 900 lignes génère une expression régulière supermassive à la volée pour correspondre encore plus. Je ne les recommande pas (par exemple, voici un violon de l'expression régulière ci-dessus, qui fait beaucoup d'erreurs ). Il n'y a pas de formule magique facile pour que cela fonctionne. En théorie et en théorie, il n'est pas possible de faire correspondre des adresses avec une expression régulière.

La publication USPS 28 documente les nombreux formats d'adresses possibles, avec tous leurs mots-clés et variatons. Pire encore, les adresses sont souvent ambiguës. Les mots peuvent signifier plus d'une chose ("St" peut être "Saint" ou "Street") et il y a des mots que je suis sûr qu'ils ont inventés. (Qui savait que "Stravenue" était un suffixe de rue?)

Vous auriez besoin d'un code qui comprend vraiment les adresses, et si ce code existe, c'est un secret commercial. Mais vous pourriez probablement rouler vous-même si vous aimez vraiment ça.

Les adresses se présentent sous des formes et des tailles inattendues

Voici quelques adresses artificielles (mais complètes):

1)  102 main street
    Anytown, state

2)  400n 600e #2, 52173

3)  p.o. #104 60203

Même ceux-ci sont peut-être valables:

4)  829 LKSDFJlkjsdflkjsdljf Bkpw 12345

5)  205 1105 14 90210

De toute évidence, ceux-ci ne sont pas normalisés. Ponctuation et sauts de ligne non garantis. Voici ce qui se passe:

  1. Le numéro 1 est complet car il contient une adresse postale et une ville et un état. Avec cette information, il y a assez d'identifier l'adresse, et elle peut être considérée comme "livrable" (avec une certaine normalisation).

  2. Le numéro 2 est complet car il contient également une adresse postale (avec numéro secondaire / d'unité) et un code postal à 5 ​​chiffres, ce qui suffit pour identifier une adresse.

  3. Le numéro 3 est un format de boîte postale complet, car il contient un code postal.

  4. Le numéro 4 est également complet car le code postal est unique , ce qui signifie qu'une entité ou une société privée a acheté cet espace d'adressage. Un code postal unique est destiné aux espaces de livraison à volume élevé ou concentrés. Tout ce qui est adressé au code postal 12345 va à General Electric à Schenectady, NY. Cet exemple n'atteindra personne en particulier, mais l'USPS serait toujours en mesure de le fournir.

  5. Le numéro 5 est également complet, croyez-le ou non. Avec juste ces nombres, l'adresse complète peut être découverte lorsqu'elle est analysée par rapport à une base de données de toutes les adresses possibles. Remplir les directives manquantes, l'indicateur secondaire et le code ZIP + 4 est trivial lorsque vous voyez chaque nombre comme un composant. Voici à quoi il ressemble, entièrement développé et standardisé:

205 N 1105 W Apt 14

Beverly Hills CA 90210-5221

Les données d'adresse ne vous appartiennent pas

Dans la plupart des pays qui fournissent des données d'adresse officielles aux fournisseurs agréés, les données d'adresse elles-mêmes appartiennent à l'agence gouvernementale. Aux États-Unis, l'USPS possède les adresses. Il en va de même pour Postes Canada, Royal Mail et d'autres, bien que chaque pays applique ou définit la propriété un peu différemment. Le savoir est important, car il interdit généralement la rétro-ingénierie de la base de données d'adresses. Vous devez faire attention à l'acquisition, au stockage et à l'utilisation des données.

Google Maps est une solution courante pour les corrections d'adresse rapides, mais le TOS est plutôt prohibitif; par exemple, vous ne pouvez pas utiliser leurs données ou API sans afficher une carte Google Map, et à des fins non commerciales uniquement (sauf si vous payez), et vous ne pouvez pas stocker les données (sauf pour la mise en cache temporaire). Logique. Les données de Google sont parmi les meilleures au monde. Cependant, Google Maps ne vérifie pas l'adresse. Si une adresse n'existe pas, il sera toujours vous montrer où l'adresse serait être si elle a fait exist (essayer sur votre propre rue, utiliser un numéro de la maison que vous savez n'existe pas). Ceci est parfois utile, mais soyez conscient de cela.

La politique d'utilisation de Nominatim est également limitative, en particulier pour un volume élevé et une utilisation commerciale, et les données sont principalement tirées de sources gratuites, donc elles ne sont pas aussi bien entretenues (telle est la nature des projets ouverts) - cependant, cela peut toujours convenir. vos besoins. Il est soutenu par une grande communauté.

L'USPS lui-même a une API, mais il descend beaucoup et n'est livré sans aucune garantie ni support. Il peut également être difficile à utiliser. Certaines personnes l'utilisent avec parcimonie et sans problème. Mais il est facile de rater que l'USPS exige que vous utilisiez son API uniquement pour confirmer les adresses à expédier par leur intermédiaire.

Les gens s'attendent à ce que les adresses soient difficiles

Malheureusement, nous avons conditionné notre société à s'attendre à ce que les adresses soient compliquées. Il y a des dizaines de bons articles UX partout sur Internet à ce sujet, mais le fait est que si vous avez un formulaire d'adresse avec des champs individuels, c'est ce à quoi les utilisateurs s'attendent, même si cela complique la tâche pour les adresses marginales qui ne correspondent pas au formatez le formulaire attendu, ou peut-être que le formulaire nécessite un champ qu'il ne devrait pas. Ou les utilisateurs ne savent pas où mettre une certaine partie de leur adresse.

Je pourrais continuer encore et encore sur le mauvais UX des formulaires de paiement ces jours-ci, mais je dirai simplement que combiner les adresses dans un seul champ sera un changement bienvenu - les gens pourront taper leur adresse comme ils l'entendent , plutôt que d'essayer de comprendre votre longue forme. Cependant, ce changement sera inattendu et les utilisateurs peuvent le trouver un peu discordant au début. Soyez conscient de cela.

Une partie de cette douleur peut être atténuée en plaçant le champ du pays au premier plan, avant l'adresse. Lorsqu'ils remplissent d'abord le champ du pays, vous savez comment faire apparaître votre formulaire. Peut-être avez-vous un bon moyen de gérer les adresses américaines à un seul champ, donc s'ils sélectionnent États-Unis, vous pouvez réduire votre formulaire à un seul champ, sinon afficher les champs du composant. Juste des choses à penser!

Maintenant, nous savons pourquoi c'est difficile; Que peux-tu y faire?

L'USPS octroie des licences aux fournisseurs via un processus appelé Certification CASS ™ pour fournir des adresses vérifiées aux clients. Ces fournisseurs ont accès à la base de données USPS, mise à jour mensuellement. Leur logiciel doit se conformer à des normes rigoureuses pour être certifié, et ils n'ont pas souvent besoin d'accepter les conditions limitatives décrites ci-dessus.

Il existe de nombreuses entreprises certifiées CASS qui peuvent traiter des listes ou avoir des API: Melissa Data, Experian QAS et SmartyStreets pour n'en nommer que quelques-unes.

(En raison de la critique pour la "publicité", j'ai tronqué ma réponse à ce stade. C'est à vous de trouver une solution qui fonctionne pour vous.)

La vérité: Vraiment, mes amis, je ne travaille dans aucune de ces entreprises. Ce n'est pas une publicité.

Mat
la source
1
Qu'en est-il des adresses sud-américaines (Uruguay)? : D
Bart Calix au
11
@Brian - Peut-être parce que l'utilisateur a fourni de nombreuses informations utiles à ceux qui lisent la question et la réponse, qu'ils choisissent ou non d'utiliser le produit de son entreprise.
Zarepheth
7
@Brian Ces sites sont des scrapers de contenu. Ils moquent du contenu pour obtenir les classements SERP. Je ne les ai jamais vus auparavant. Je n'ai jamais publié ce contenu avant ou après nulle part ailleurs.
Matt
2
@khuderm J'ai remarqué tout à l'heure en lisant votre commentaire que tous les commentaires dissidents ont disparu; je ne sais pas comment / quand cela s'est produit. Mais de toute façon, consultez l'historique des modifications de ma réponse et vous trouverez une référence directe à un extracteur d'adresses américain qui pourrait vous aider. Je l'ai construit lorsque j'ai travaillé à mon dernier travail, mais c'est du code propriétaire, donc je ne peux pas le partager ... mais ils existent. J'espère que c'est utile.
Matt
2
Oups. Désolé @Matt. Eh bien, j'ai commencé à vous suivre à travers vos questions et aussi Github. Vous êtes assez impressionnant.
Sayka le
28

libpostal: une bibliothèque open-source pour analyser les adresses, s'entraîner avec des données d'OpenStreetMap, OpenAddresses et OpenCage.

https://github.com/openvenues/libpostal ( plus d'infos à ce sujet )

Autres outils / services:

David Portabella
la source
13

Il existe de nombreux analyseurs d'adresses de rue. Ils viennent dans deux saveurs de base - ceux qui ont des bases de données de noms de lieux et de noms de rues, et ceux qui n'en ont pas.

Un analyseur d'adresse postale d'expression régulière peut atteindre un taux de réussite d'environ 95% sans trop de problèmes. Ensuite, vous commencez à frapper les cas inhabituels. Celui de Perl dans CPAN, "Geo :: StreetAddress :: US", est à peu près aussi bon. Il existe des ports Python et Javascript de cela, tous open source. J'ai une version améliorée en Python qui augmente légèrement le taux de réussite en traitant plus de cas. Cependant, pour obtenir les 3% restants, vous avez besoin de bases de données pour aider à lever l'ambiguïté.

Une base de données avec des codes postaux à 3 chiffres et des noms et abréviations des États américains est d'une grande aide. Lorsqu'un analyseur voit un code postal et un nom d'état cohérents, il peut commencer à se verrouiller sur le format. Cela fonctionne très bien pour les États-Unis et le Royaume-Uni.

L'analyse correcte des adresses civiles commence à partir de la fin et fonctionne à l'envers. C'est ainsi que les systèmes USPS procèdent. Les adresses sont les moins ambiguës à la fin, où les noms de pays, les noms de villes et les codes postaux sont relativement faciles à reconnaître. Les noms de rue peuvent généralement être isolés. Les emplacements dans les rues sont les plus complexes à analyser; là, vous rencontrez des choses telles que "Fifth Floor" et "Staples Pavillion". C'est alors qu'une base de données est d'une grande aide.

John Nagle
la source
Il existe également le module CPAN Lingua: EN :: AddressParse. Bien que plus lent que "Geo :: StreetAddress :: US, il donne un taux de réussite plus élevé.
Kim Ryan
8

MISE À JOUR: Geocode.xyz fonctionne désormais dans le monde entier. Pour des exemples, voir https://geocode.xyz

Pour les États-Unis, le Mexique et le Canada, consultez geocoder.ca .

Par exemple:

Entrée: quelque chose se passe près de l'intersection de Main et Arthur Kill Rd New York

Production:

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

Vous pouvez également vérifier les résultats dans l'interface Web ou obtenir une sortie au format Json ou Jsonp. par exemple. Je recherche des restaurants autour du 123 Main Street, New York

Ervin Ruci
la source
Comment avez-vous implémenté le système d'analyse d'adresses en utilisant openaddress? Utilisez-vous une stratégie de force brute?
Nithin K Anil
1
Qu'entendez-vous par «force brute»? Décomposer le texte en toutes les combinaisons possibles de chaînes d'adresses possibles et comparer chacune à une base de données d'adresses n'est pas pratique et prendra beaucoup plus de temps pour fournir une réponse que ce système. Les adresses ouvertes sont l'une des sources de données permettant de créer un «ensemble d'apprentissage» de formats d'adresses pour l'algorithme. Il utilise ces informations pour analyser les adresses à partir de texte non structuré.
Ervin Ruci
2
Un autre système similaire est Geo :: libpostal ( perltricks.com/article/announcing-geo--libpostal ) Ils utilisent également openstreetmap et openaddresses semble-t-il, pour créer des modèles d'adresses à la volée
Ervin Ruci
Je viens de tester le geoparser de geocode.xyz (envoyer du texte, récupérer l'emplacement) sur des centaines d'adresses réelles. Étant donné une côte à côte avec l'API de google map et un ensemble global d'adresses, geocode.xyzla scantextméthode de la méthode a échoué la plupart du temps. Il a toujours choisi «Genève, États-Unis» plutôt que «Genève, Suisse» et était généralement biaisé par les États-Unis.
Marc Maxmeister
Ça dépend du contexte. geocode.xyz/?scantext=Geneva,%20Switzerland produira: Match Location Geneva, Switzerland, CH Confidence Score: 0.8 tandis que geocode.xyz/?scantext=Geneva,%20USA produira Match Location Geneva, US Confidence Score: 1.0 Aussi, vous pouvez biaiser la région comme suit: geocode.xyz/?scantext=Geneva,%20USA®ion=CH
Ervin Ruci
4

Pas de code? Pour la honte!

Voici un analyseur d'adresse JavaScript simple. C'est assez horrible pour chaque raison donnée par Matt dans sa thèse ci-dessus (avec laquelle je suis presque à 100% d'accord: les adresses sont des types complexes et les humains font des erreurs; mieux vaut sous-traiter et automatiser cela - quand vous pouvez vous le permettre).

Mais plutôt que de pleurer, j'ai décidé d'essayer:

Ce code fonctionne correctement pour analyser la plupart des résultats Esri pourfindAddressCandidateet aussi avec d'autres géocodeurs (inversés) qui renvoient une adresse sur une seule ligne où la rue / la ville / l'état sont délimités par des virgules. Vous pouvez étendre si vous le souhaitez ou écrire des analyseurs spécifiques au pays. Ou utilisez simplement ceci comme une étude de cas sur la difficulté de cet exercice ou sur la façon dont je suis nul en JavaScript. J'admets que je n'ai passé qu'une trentaine de minutes là-dessus (les futures itérations pourraient ajouter des caches, une validation zip et des recherches d'état ainsi que le contexte de localisation de l'utilisateur), mais cela a fonctionné pour mon cas d'utilisation: l'utilisateur final voit un formulaire qui analyse la réponse de recherche de géocodage en 4 zones de texte. Si l'analyse des adresses est erronée (ce qui est rare à moins que les données source soient médiocres), ce n'est pas grave - l'utilisateur peut le vérifier et le réparer! (Mais pour les solutions automatisées, il est possible de supprimer / ignorer ou de signaler une erreur afin que le développement puisse soit prendre en charge le nouveau format, soit corriger les données source.)

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

rien n'est nécessaire
la source
avertissement: mes clients sont propriétaires de leurs données d'adresse et exécutent leurs propres serveurs Esri. Si vous récupérez des données sur Google, OSM, ArcGisOnline ou ailleurs, assurez-vous que vous pouvez les stocker et les utiliser (de nombreux services ont des restrictions sur la façon dont vous pouvez stocker et pendant combien de temps)
rien n'est nécessaire
La première réponse ci-dessus démontre de manière convaincante que ce problème est insoluble avec les expressions régulières si vous avez affaire à une liste d'adresses globale. 200 pays ont trop d'exceptions. Dans mes tests, vous pouvez déterminer le pays à partir d'une chaîne de manière assez fiable, puis rechercher une expression régulière spécifique pour chaque pays - ce qui est probablement la façon dont les meilleures API fonctionnent.
Marc Maxmeister
2

Si vous voulez vous fier aux données OSM, libpostal est très puissant et gère un grand nombre des mises en garde les plus courantes avec les entrées d'adresse.

Vitor Magalhães
la source
Je pense que votre réponse est un double de ce post. Bonne suggestion, cependant.
Michael
2

Une autre option pour les adresses basées aux États-Unis est YAddress (créé par l'entreprise pour laquelle je travaille).

De nombreuses réponses à cette question suggèrent des outils de géocodage comme solution. Il est important de ne pas confondre l'analyse des adresses et le géocodage; Ils ne sont pas les mêmes. Bien que les géocodeurs puissent décomposer une adresse en composants comme avantage secondaire, ils s'appuient généralement sur des ensembles d'adresses non standard. Cela signifie qu'une adresse analysée par géocodeur peut ne pas être la même que l'adresse officielle. Par exemple, ce que l'API de géocodage de Google appelle «6th Ave» à Manhattan, USPS appelle «Avenue of the Americas».

Michael Diomin
la source
2

Pour l'analyse d'adresses aux États-Unis,

Je préfère utiliser le package usaddress disponible dans pip pour usaddress uniquement

python3 -m pip install usaddress

Documentation
PyPi

Cela a bien fonctionné pour moi pour l'adresse américaine.

#!/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)))

Exécution de 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"}
theBuzzyCoder
la source
0

Dans l'un de nos projets, nous avons utilisé l'analyseur d'adresse suivant. Il analyse les adresses de la plupart des pays du monde avec une bonne précision.

http://address-parser.net/

Il est disponible en tant que bibliothèque autonome ou en tant qu'API en direct.

Waqas Anwar
la source
1
Mais c'est un produit payant.
Jeremy Thompson
0

Je suis en retard à la fête, voici un script Excel VBA que j'ai écrit il y a des années pour l'Australie. Il peut être facilement modifié pour prendre en charge d'autres pays. J'ai créé un référentiel GitHub du code C # ici. Je l'ai hébergé sur mon site et vous pouvez le télécharger ici: http://jeremythompson.net/rocks/ParseAddress.xlsm

Stratégie

Pour tout pays avec un PostCode numérique ou pouvant être associé à un RegEx, ma stratégie fonctionne très bien:

  1. Premièrement, nous détectons le prénom et le nom qui sont supposés être la ligne supérieure. Il est facile d'ignorer le nom et de commencer par l'adresse en décochant la case (appelée «Le nom est la première ligne», comme indiqué ci-dessous).

  2. Ensuite, il est sûr de s'attendre à ce que l'adresse composée de la rue et du numéro vienne avant la banlieue et la rue, Pde, Ave, Av, Rd, Cres, boucle, etc. est un séparateur.

  3. Détecter la banlieue par rapport à l'État et même au pays peut tromper les analyseurs les plus sophistiqués car il peut y avoir des conflits. Pour surmonter cela, j'utilise une recherche PostCode basée sur le fait qu'après avoir dépouillé les numéros de rue et d'appartement / unité ainsi que la PoBox, Ph, Fax , Mobile, etc., seul le numéro PostCode restera. Ceci est facile à associer à un regEx pour rechercher ensuite la ou les banlieues et le pays.

Votre service national de poste vous fournira gratuitement une liste des codes postaux avec les banlieues et les États que vous pouvez stocker dans une feuille Excel, une table db, un fichier texte / json / xml, etc.

  1. Enfin, comme certains codes postaux ont plusieurs banlieues, nous vérifions quelle banlieue apparaît dans l'adresse.

Exemple

entrez la description de l'image ici

Code VBA

AVERTISSEMENT, je sais que ce code n'est pas parfait, ni même bien écrit, mais il est très facile de le convertir dans n'importe quel langage de programmation et de l'exécuter dans n'importe quel type d'application.La stratégie est la réponse en fonction de votre pays et de vos règles, prenez ce code comme exemple :

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
Jeremy Thompson
la source