Serbest biçimli sokak / posta adresini metinden ve bileşenlere ayrıştırma


136

Büyük ölçüde Amerika Birleşik Devletleri'nde iş yapıyoruz ve tüm adres alanlarını tek bir metin alanında birleştirerek kullanıcı deneyimini geliştirmeye çalışıyoruz. Ancak birkaç sorun var:

  • Kullanıcı türlerinin adresi doğru veya standart biçimde olmayabilir
  • Kredi kartı ödemelerini işlemek için adres bölümlere (sokak, şehir, eyalet vb.) Ayrılmalıdır.
  • Kullanıcılar adreslerinden daha fazlasını girebilirler (isimleri veya şirketleriyle birlikte)
  • Google bunu yapabilir, ancak Hizmet Şartları ve sorgu sınırları özellikle sıkı bir bütçeyle yasaklanmıştır

Görünüşe göre, bu yaygın bir soru:

Bir adresi çevresindeki metinden ayırıp parçalara ayırmanın bir yolu var mı? Adresleri ayrıştırmak için düzenli bir ifade var mı?


Aşağıdaki yanıtlar daha yararlıdır çünkü küresel sorunu görmezden gelmezler - adresler ortak bir desene uymaz.
Marc Maxmeister

Yanıtlar:


290

Bir adres doğrulama şirketi için çalışırken bu soruyu çok gördüm. Aynı soru ile arama yapan programcıların daha erişilebilir olmasını sağlamak için cevabı buraya gönderiyorum. Milyarlarca adres işlediğim şirket ve bu süreçte çok şey öğrendik.

İlk olarak, adresler hakkında birkaç şey anlamamız gerekir.

Adresler düzenli değil

Bu, düzenli ifadelerin tükendiği anlamına gelir. Adresleri çok özel bir biçimde eşleştiren basit normal ifadelerden, aşağıdakilere kadar her şeyi gördüm:

/ \ S + (\ d {2,5} \ s +) ([a | p]?! M \ b) (([a-z-Z | \ s +] {1,5}) {1,2}) ? ([\ s |, |.] +) (([a-zA-Z | \ s +] {1,30}) {1,4})? (mahkeme | ct | cadde | st | sürücü | dr | şerit | ln | yol | rd | Blvd) ([\ s |, | |;.]? +) (([a-z-Z | \ s +] {1,30}) {1,2}) ([ \ s |, |.] +) \ B (AK |? AL | AR | AZ | CA | CO | CT | DC | DE | FL | GA | GU | HI | IA | İD | IL | IN | KS | KY | LA | MA | MD | ME | MI | MN | MO | MS | MT | NC | ND | NE | NH | NJ | NM | NV | NY | OH | tamam | VEYA | PA | RI | SC | SD | TN | Teksas | UT | VA | VI | VT | WA | WI | Batı | WY) ([\ s |, |.] +) (\ s + d \ {5}) ([\ s |, |.]? +) / i

... için bu bir 900+ hat sınıf dosyası daha maç için anında bir süper kütleli normal ifade verebildigi. Bunları tavsiye etmiyorum (örneğin, yukarıdaki regex'in bir kemanı, bu da çok fazla hata yapıyor ). Bunun işe yaraması için kolay bir sihirli formül yok. Teoride ve tarafından teori, bu normal bir ifade ile adresleri eşleştirmek mümkün değildir.

USPS Yayını 28 , tüm anahtar sözcükleri ve varyasyonları ile mümkün olan birçok adres biçimini belgelemektedir. Hepsinden kötüsü, adresler genellikle belirsizdir. Kelimeler birden fazla şey anlamına gelebilir ("St" "Saint" veya "Street" olabilir) ve icat ettiklerinden emin olduğum kelimeler vardır. ("Stravenue" nun bir sokak eki olduğunu kim bilebilirdi?)

Adresleri gerçekten anlayan bir koda ihtiyacınız vardır ve eğer bu kod mevcutsa, bu bir ticari sırdır. Ama eğer gerçekten içine girerseniz, muhtemelen kendi başınıza dönebilirsiniz.

Adresler beklenmedik şekil ve boyutlarda

İşte bazı (tam) adresler:

1)  102 main street
    Anytown, state

2)  400n 600e #2, 52173

3)  p.o. #104 60203

Bunlar bile muhtemelen geçerlidir:

4)  829 LKSDFJlkjsdflkjsdljf Bkpw 12345

5)  205 1105 14 90210

Açıkçası, bunlar standart değildir. Noktalama işaretleri ve satır kesmeleri garanti edilmez. Neler oluyor:

  1. Bir sokak adresi, bir şehir ve eyalet içerdiği için 1 numara tamamlandı. Bu bilgi ile adresi yeterince tanımlamak yeterlidir ve "teslim edilebilir" olarak kabul edilebilir (bazı standartlaştırmalarla).

  2. Bir adres tanımlamak için yeterli olan bir sokak adresi (ikincil / birim numarası ile) ve 5 haneli bir posta kodu içerdiğinden, 2 sayısı tamamlanmıştır.

  3. Sayı 3 , posta kodu içerdiği için eksiksiz bir posta kutusu biçimidir.

  4. Posta kodu benzersiz olduğu için 4 numara da tamamlanmıştır , yani özel bir kuruluş veya şirket bu adres alanını satın almıştır. Benzersiz bir ZIP kodu, yüksek hacimli veya konsantre dağıtım alanları içindir. Posta kodu 12345'e gönderilen her şey Schenectady, NY'daki General Electric'e gider. Bu örnek özellikle kimseye ulaşmayacak, ancak USPS yine de sunabilecektir.

  5. 5 numara da tamamlandı, ister inanın ister inanmayın. Sadece bu numaralarla, tam adres, olası tüm adreslerin bir veritabanına ayrıştırıldığında bulunabilir. Eksik sayıları, ikincil işaretleyiciyi ve ZIP + 4 kodunu doldurmak, her sayıyı bir bileşen olarak gördüğünüzde önemsizdir. İşte tam olarak genişletilmiş ve standartlaştırılmış gibi görünüyor:

205 N 1105 W Apt 14

Beverly Hills CA 90210-5221

Adres verileri size ait değil

Lisanslı satıcılara resmi adres verileri sağlayan çoğu ülkede, adres verilerinin kendisi yönetim ajansına aittir. ABD'de USPS adreslerin sahibidir. Aynı durum Kanada Postası, Royal Mail ve diğerleri için de geçerlidir, ancak her ülke sahipliği biraz farklı uygular veya tanımlar. Bunu bilmek önemlidir, çünkü genellikle adres veritabanının tersine mühendislikini yasaklar. Verileri nasıl alacağınıza, saklayacağınıza ve kullanacağınıza dikkat etmelisiniz.

Google Haritalar, hızlı adres düzeltmeleri için yaygın bir yöntemdir, ancak Hizmet Şartları oldukça engelleyicidir; örneğin, verilerini veya API'larını bir Google Haritası göstermeden ve yalnızca ticari olmayan amaçlarla (ödeme yapmadığınız sürece) kullanamazsınız ve verileri depolayamazsınız (geçici önbellekleme hariç). Mantıklı. Google'ın verileri dünyanın en iyilerinden. Ancak, Google Maps yok değil adresini doğrulayın. Bir adres yoksa adresi nerede, hala gösterecektir ediyorum o eğer olmak did (kendi sokakta denemek; mevcut değil bildiğim bir ev numarası kullanın) mevcuttur. Bu bazen yararlıdır, ancak bunun farkında olun.

Nominatim'in kullanım politikası , özellikle yüksek hacimli ve ticari kullanım için benzer şekilde sınırlayıcıdır ve veriler çoğunlukla ücretsiz kaynaklardan alınmıştır, bu yüzden iyi korunmaz (bu açık projelerin doğasıdır) - ancak bu yine de uygun olabilir ihtiyaçlarınızı. Büyük bir topluluk tarafından desteklenir.

USPS'nin bir API'si var, ancak çok azalıyor ve hiçbir garanti veya destek yok. Ayrıca kullanımı zor olabilir. Bazı insanlar bunu sorunsuz bir şekilde kullanmaktadır. Ancak USPS'nin API'larını yalnızca adresleri gönderilecek adresleri onaylamak için kullanmanızı gerektirdiğini kaçırmak kolaydır.

İnsanlar adreslerin zor olmasını bekliyor

Ne yazık ki, toplumumuzu adreslerin karmaşık olmasını beklemeye şartlandırdık. İnternette bu konuda düzinelerce iyi UX makalesi var, ancak gerçek şu ki, bireysel alanlara sahip bir adres formunuz varsa, kullanıcıların beklediği şey, bu, biçimin beklediği biçim, ya da biçimin olmaması gereken bir alan gerektiriyor olabilir. Veya kullanıcılar adreslerinin belirli bir bölümünü nereye koyacaklarını bilmiyorlar.

Bu günlerde ödeme formlarının kötü UX'i hakkında devam edebilirdim, ancak bunun yerine adresleri tek bir alanda birleştirmenin hoş bir değişiklik olacağını söyleyeceğim - insanlar adreslerini uygun gördükleri şekilde yazabilecekler uzun formunuzu anlamaya çalışmak yerine. Ancak, bu değişiklik beklenmedik olacak ve kullanıcılar başlangıçta biraz sarsıcı bulabilir. Sadece bunun farkında ol.

Bu ağrının bir kısmı, ülke alanını adresin önüne çıkararak hafifletilebilir. Önce ülke alanını doldurduklarında, formunuzu nasıl göstereceğinizi biliyorsunuzdur. Belki de tek alanlı ABD adresleriyle başa çıkmanın iyi bir yoluna sahipsiniz, bu yüzden ABD'yi seçtiyse formunuzu tek bir alana azaltabilir, aksi takdirde bileşen alanlarını gösterebilirsiniz. Düşünmeniz gereken şeyler!

Şimdi neden zor olduğunu biliyoruz; bu konuda ne yapabilirsin?

USPS, satıcılara, müşterilere doğrulanmış adresler sağlamak için CASS ™ Sertifikasyon adı verilen bir süreç aracılığıyla lisans verir. Bu satıcıların aylık olarak güncellenen USPS veritabanına erişimi vardır. Yazılımları sertifikalandırılacak titiz standartlara uymak zorundadır ve genellikle yukarıda tartışıldığı gibi sınırlayıcı şartlar üzerinde mutabakat gerektirmezler.

Listeleri işleyebilen veya API'leri kullanabilen birçok CASS Sertifikalı şirket vardır: Melissa Data, Experian QAS ve SmartyStreets bunlardan birkaçı.

("Reklamcılık" için pullanma nedeniyle bu noktada cevabımı kestim. Sizin için çalışan bir çözüm bulmak size kalmış.)

Gerçek: Gerçekten millet, bu şirketlerin hiçbirinde çalışmıyorum. Bu bir reklam değil.


1
Güney Amerika (Uruguay) adresleri ne olacak? : D
Bart Calixto

11
@Brian - Belki de kullanıcı, şirketinin ürününü kullanmayı tercih edip etmediklerinden bağımsız olarak, soru ve cevapları okuyanlar için birçok yararlı bilgi sağladığından.
Zarepheth

7
@Brian Bu siteler içerik kazıyıcılarıdır. SERP sıralaması almak için içerik tutuyorlar. Onları daha önce hiç görmedim. Bu içeriği daha önce veya başka bir yerde yayınlamadım.
Matt

2
@khuderm Yorumunuzu okuduğumda, tüm muhalif yorumların kaybolduğunu fark ettim; nasıl / ne zaman olduğundan emin değilim. Ama yine de, cevabımın düzenleme geçmişine bakın ve size yardımcı olabilecek bir ABD adres çıkarıcısına doğrudan başvuru bulacaksınız. Son işimde çalıştığımda yaptım, ancak özel kod bu yüzden paylaşamıyorum ... ama varlar. Umarım yardımcı olur.
Matt

2
Hata. Üzgünüm @Matt. Seni sorularınız ve Github aracılığıyla takip etmeye başladım. Oldukça etkileyici.
Sayka

28

libpostal: adresleri ayrıştırmak için açık kaynaklı bir kütüphane, OpenStreetMap, OpenAddresses ve OpenCage verileri ile eğitim.

https://github.com/openvenues/libpostal (hakkında daha fazla bilgi )

Diğer araçlar / hizmetler:


13

Birçok sokak adresi ayrıştırıcısı vardır. İki temel lezzette bulunurlar - yer isimleri ve sokak isimleri veritabanlarına sahip olanlar ve olmayanlar.

Normal bir ifade sokak adresi ayrıştırıcısı çok fazla sorun yaşamadan yaklaşık% 95'e kadar başarı oranına ulaşabilir. Sonra alışılmadık vakaları vurmaya başlarsınız. CPAN'daki "Geo :: StreetAddress :: US" adlı Perl, bu kadar iyi. Python ve Javascript portları var, hepsi açık kaynak. Python'da daha fazla vakayı ele alarak başarı oranını biraz yükselten gelişmiş bir versiyonum var. Yine de son% 3'ü doğru bir şekilde elde etmek için, ayrımcılığa yardımcı olacak veritabanlarına ihtiyacınız var.

3 haneli ZIP kodları ve ABD eyalet adları ve kısaltmaları içeren bir veritabanı büyük bir yardımcıdır. Bir ayrıştırıcı tutarlı bir posta kodu ve durum adı gördüğünde, formata kilitlenmeye başlayabilir. Bu, ABD ve İngiltere için çok iyi çalışıyor.

Doğru adres ayrıştırma işlemi sondan başlar ve geriye doğru çalışır. USPS sistemleri böyle yapar. Adresler, ülke adlarının, şehir adlarının ve posta kodlarının tanınmasının nispeten kolay olduğu en sonunda belirsizdir. Sokak isimleri genellikle izole edilebilir. Sokaklardaki yerler, ayrıştırılması en karmaşık yerlerdir; orada "Beşinci Kat" ve "Zımba Köşkü" gibi şeylerle karşılaşıyorsunuz. O zaman bir veritabanı büyük bir yardımdır.


CPAN modülü Lingua da vardır: EN :: AddressParse. "Geo :: StreetAddress :: US'den daha yavaş olsa da, daha yüksek bir başarı oranı verir.
Kim Ryan

8

GÜNCELLEME: Geocode.xyz artık tüm dünyada çalışıyor. Örnekler için bkz. Https://geocode.xyz

ABD, Meksika ve Kanada için geocoder.ca adresine bakın .

Örneğin:

Giriş: ana ve arthur kesişme yakın bir şey oluyor rd new york

Çıktı:

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

Ayrıca sonuçları web arayüzünden kontrol edebilir veya Json veya Jsonp olarak çıktı alabilirsiniz. Örneğin. 123 Main Street, New York çevresindeki restoranlar arıyorum


Açık adres kullanarak adres ayrıştırma sistemini nasıl uyguladınız? Kaba kuvvet stratejisi mi kullanıyorsunuz?
Nithin K Anil

1
'Kaba kuvvet' ile ne demek istiyorsun? Metni olası tüm adres dizelerinin kombinasyonlarına bölmek ve her birini bir adres veritabanıyla karşılaştırmak pratik değildir ve bu sistemin yanıtından daha fazla zaman alacaktır. Açık adresler, algoritma için bir adres formatları 'eğitim seti' oluşturmak için kullanılan veri kaynaklarından biridir. Bu bilgileri, yapılandırılmamış metinden adresleri ayrıştırmak için kullanır.
Ervin Ruci

2
Başka bir benzer sistem Geo :: libpostal'dır ( perltricks.com/article/announcing-geo--libpostal ) Ayrıca, adres şablonlarını anında oluşturmak için openstreetmap ve openaddresses kullanıyorlar
Ervin Ruci

Ben sadece yüzlerce gerçek adres üzerinde geocode.xyz'ın geoparser'ı (metin olarak gönder, konumu geri al) test ettim. Google Haritanın API ile yan tarafını ve adresleri global dizi göz önüne alındığında, geocode.xyz'in scantextyöntemi çoğu zaman başarısız oldu. Her zaman "Cenevre, İsviçre" yerine "Cenevre, ABD" yi seçti ve genellikle ABD taraflıydı.
Marc Maxmeister

Bu koşullara bağlıdır. geocode.xyz/?scantext=Geneva,%20İsviçre üretecek: Maç Konumu Cenevre, İsviçre, CH Güven Puanı: 0.8 ise geocode.xyz/?scantext=Geneva,%20USA Maç Yeri Cenevre, ABD Güven Puanı: 1.0 Ayrıca, önyargıları aşağıdaki gibi bölge yapabilirsiniz: geocode.xyz/?scantext=Geneva,%20USA®ion=CH
Ervin Ruci

4

Kod yok? Utanç!

İşte basit bir JavaScript adres ayrıştırıcısı. Matt'in yukarıdaki tezinde verdiği her nedenden dolayı oldukça korkunç (neredeyse% 100 katılıyorum: adresler karmaşık türler ve insanlar hata yapıyor; bunu dışa aktarmak ve otomatikleştirmek daha iyidir - bunu karşılayabildiğinizde).

Ama ağlamak yerine denemeye karar verdim:

Bu kod, çoğu Esri sonucununfindAddressCandidateayrıca cadde / şehir / eyaletin virgülle sınırlandığı tek satırlık adres döndüren diğer (ters) coğrafi kodlayıcılarla. İsterseniz ülkeye özgü ayrıştırıcılar yazabilir veya yazabilirsiniz. Veya bunu, bu alıştırmanın ne kadar zor olabileceği veya JavaScript'te ne kadar berbat olduğum konusunda bir vaka çalışması olarak kullanın. Bu konuda sadece otuz dakika harcadığımı itiraf ediyorum (gelecekteki yinelemeler önbellek, zip doğrulaması ve durum aramaları ve kullanıcı konumu bağlamı ekleyebilir), ancak kullanım durumum için çalıştı: Son kullanıcı, coğrafi kod arama yanıtını 4'e ayrıştıran formu görüyor Metin kutuları. Adres ayrıştırma yanlış gelirse (kaynak veri zayıf olmadığı sürece nadirdir) önemli değildir - kullanıcı doğrulamak ve düzeltmek için alır! (Ancak otomatik çözümler için geliştirici yeni biçimi destekleyebilir veya kaynak verileri düzeltebilir.

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


yasal uyarı: müşterilerim adres verilerinin sahibidir ve kendi Esri sunucularını çalıştırırlar. Google, OSM, ArcGisOnline veya herhangi bir yerden veri alırsanız, saklamanın ve kullanmanın uygun olduğundan emin olun (birçok hizmetin nasıl saklayabileceğiniz ve ne kadar süre saklayabileceğiniz konusunda kısıtlamaları vardır)
hiçbir şey gerekli

Yukarıdaki ilk yanıt, genel bir adres listesiyle uğraşıyorsanız, bu sorunun regexes ile çözülemez olduğu konusunda ikna edici bir durumdur. 200 ülkede çok fazla istisna var. Testlerimde, ülkeyi bir dizeden oldukça güvenilir bir şekilde belirleyebilir, ardından her ülke için belirli bir normal ifadeye bakabilirsiniz - muhtemelen daha iyi API'ler bu şekilde çalışır.
Marc Maxmeister


2

ABD merkezli adresler için başka bir seçenek de YAddress (çalıştığım şirket tarafından yapılmış).

Bu sorunun birçok yanıtı, bir çözüm olarak coğrafi kodlama araçlarını önermektedir. Adres ayrıştırma ve coğrafi kodlamayı karıştırmamak önemlidir; onlar aynı değil. Coğrafi kodlayıcılar bir adresi yan fayda olarak bileşenlere ayırabilirken, genellikle standart olmayan adres kümelerine güvenirler. Bu, coğrafi kodlayıcı tarafından ayrıştırılmış bir adresin resmi adresle aynı olmayabilir. Örneğin, Google coğrafi kodlama API'sının Manhattan'daki "6. Ave" dediği USPS, "Amerika Kıtası" adını verir.


2

ABD Adres Ayrıştırma için,

Sadece usaddress için pip'te bulunan usaddress paketini kullanmayı tercih ederim

python3 -m pip install usaddress

Dokümantasyon
PyPi

ABD adresi için bu benim için iyi çalıştı.

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

Address_parser.py dosyasını çalıştırma

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

0

Projemizden birinde aşağıdaki adres ayrıştırıcısını kullandık. Dünyadaki çoğu ülkenin adreslerini iyi bir doğrulukla ayrıştırır.

http://address-parser.net/

Tek başına kitaplık veya canlı API olarak kullanılabilir.


1
Ama bu ürün için ücretli.
Jeremy Thompson

0

Partiye geç kaldım, işte yıllar önce Avustralya için yazdığım bir Excel VBA senaryosu. Diğer Ülkeleri desteklemek için kolayca değiştirilebilir. Burada bir GitHub deposu C # kodu yaptım. Sitemde barındırdım ve buradan indirebilirsiniz: http://jeremythompson.net/rocks/ParseAddress.xlsm

strateji

Sayısal olan veya bir RegEx ile eşleştirilebilen bir PostCode'a sahip herhangi bir ülke için stratejim çok iyi çalışıyor:

  1. İlk olarak, en üst satır olduğu varsayılan Adı ve Soyadı tespit ediyoruz. Onay kutusunu işaretleyerek adı atlamak ve adresle başlamak kolaydır (aşağıda gösterildiği gibi 'Ad üst satırdır' olarak adlandırılır).

  2. Daha sonra Sokak ve Numaradan oluşan Adresin Banliyöden önce gelmesini beklemek güvenlidir ve St, Pde, Ave, Av, Rd, Cres, loop, vb.

  3. Banliyösü devlete ve hatta ülkeye karşı tespit etmek, en karmaşık ayrıştırıcıları kandırabilir, çünkü çatışmalar olabilir. Bunun üstesinden gelmek için Sokak ve Apartman / Birim numaralarının yanı sıra PoBox, Ph, Faks , Mobil vb. Bu, daha sonra banliyöleri ve ülkeyi aramak için bir regEx ile eşleştirmek kolaydır.

Ulusal Postane Hizmetiniz, bir Excel sayfasında, db tablosunda, metin / json / xml dosyasında vb. Depolayabileceğiniz Banliyölerde ve Eyaletlerde ücretsiz posta kodlarının bir listesini sağlayacaktır.

  1. Son olarak, bazı Posta Kodları birden fazla Banliyö içerdiğinden, Adreste hangi banliyöün göründüğünü kontrol ederiz.

Misal

resim açıklamasını buraya girin

VBA Kodu

YASAL UYARI, bu kodun mükemmel olmadığını veya hatta iyi yazılmış olduğunu biliyorum, ancak herhangi bir programlama diline dönüştürmek ve herhangi bir uygulama türünde çalıştırmak çok kolaydır. Strateji, ülkenize ve kurallarınıza bağlı olarak cevaptır, bu kodu örnek olarak alın :

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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.