Birkaç yıldan uzun süredir adres verisi toplayan her kuruluş aynı sorunla karşılaşır: kalitesi bilinmeyen, büyük ve dağınık bir adres tablosu. Web formlarına dikkatsiz kullanıcıların girdiği adresler. Girişleri doğrulamayan eski bir CRM'den taşınan kayıtlar. Bir iş ortağının uyumsuz formattaki Excel dosyasından toplu aktarılan veriler. Veri toplandıktan sonra taşınmış işletmeler.
Hatalı adres verisiyle yürütülen lojistik başarısız teslimatlara yol açar. Aynı veriyle yapılan pazarlama, doğrudan posta bütçesini boşa harcar. Bu veriye dayanan emlak değerlemeleri de yanlış etki alanı analizleri üretir. Bu işlemlerden herhangi biri başlamadan önce adreslerin temizlenip doğrulanması gerekir.
Bunu büyük ölçekte yapmanın en etkili yolu geocoding'dir. Geocoding API'ye ham bir adres metni gönderin, karşılığında yapılandırılmış adres bileşenleri, coğrafi koordinatlar ve bir güven puanı alın. Yüksek güven puanıyla sorunsuz geocode edilen adresler neredeyse kesinlikle geçerlidir. Düşük güven puanı dönen ya da hiç eşleşme bulunamayan adresler insan incelemesi gerektirir.
Bu eğitimde, bir CSV dosyasındaki adresleri okuyan, her birini MapAtlas Geocoding API ile geocode eden, sonuçları güven puanlarıyla birlikte yazan ve düşük güvenli kayıtları manuel inceleme için işaretleyen eksiksiz bir Python script'i yazıyoruz. Script; rate limiting'i, geçici ağ hatalarını ve ABD odaklı geocoding eğitimlerinin atladığı AB adres formatı farklılıklarını da ele alıyor.
Adres verisi neden bozulur
Adres kalitesi öngörülebilir nedenlerle düşer:
Manuel giriş hataları. Web formu kullanıcıları hızlı yazar, otomatik düzeltme sokak adlarını bozar ve boş olmayan her metni kabul eden doğrulama kuralları hatalı verinin içeri girmesine izin verir. Baymard Institute'un büyük ölçekli ödeme testlerinde, elle girilen teslimat adreslerinin %9'u yazım hatası içeriyordu.
İşletme taşınmaları. İşletmeler sürekli taşınır, birleşir ve kapanır. Eurostat, AB'de 2023 yılı işletme kapanma oranını %8,5 olarak veriyor ve bu rakama taşınmalar dahil değil. Bu nedenle iki yıl önce toplanmış bir B2B veritabanında, artık faal bir işletmeyle eşleşmeyen adreslerin payı azımsanmayacak düzeydedir.
Format tutarsızlıkları. Farklı kaynaklardan toplanan veriler farklı kurallar kullanır: tam ülke adları ve ISO kodları, "Sok." ve "Sokak", sokak adından önce ya da sonra gelen kapı numarası, "daire", "apt" ve "wohnung" gibi. Bir geocoder bunların hepsini yapılandırılmış bir çıktıya normalleştirir.
Eski sistemlerden veri taşıma. Birden fazla veritabanı sütununa bölünmüş adres alanları taşıma sırasında çoğu zaman birleştirilir ve yapılarını kaybeder. Eski sistemlerdeki serbest metin adres alanları, gerçek adresin parçası olmayan notlar, referanslar veya biçimlendirmeler içerebilir.
Geocoding yaklaşımı bunların hepsiyle başa çıkar, çünkü metin eşleştirmesine değil coğrafi eşleştirmeye dayanır. Yanlış biçimlendirilmiş bir adres, altındaki konum verisi eşleştiği sürece yine doğru şekilde geocode edilebilir.
Güven puanlarını anlamak
MapAtlas Geocoding API, her feature için 0.0 ile 1.0 arasında değişen bir confidence özelliği döndürür. Bu değer, format farklılıkları, kısaltmalar ve belirsizlik hesaba katıldığında dönen sonucun giriş sorgusuyla ne kadar örtüştüğünü gösterir.
| Güven puanı | Yorum | Önerilen işlem |
|---|---|---|
| 0.90 ile 1.00 | Tam veya tama yakın eşleşme | Otomatik kabul et |
| 0.85 ile 0.89 | Güçlü eşleşme, küçük format farkları | Kayıt tutarak kabul et |
| 0.60 ile 0.84 | Kısmi eşleşme, sokak bulundu ama kapı numarası belirsiz | Manuel inceleme için işaretle |
| 0.40 ile 0.59 | Belirsiz, yerleşim yeri eşleşti ama adresin kendisi eşleşmedi | Reddet veya üst kademeye ilet |
| 0.00 ile 0.39 | Anlamlı bir eşleşme yok | Reddet, büyük olasılıkla geçersiz |
Bu eşikler bir başlangıç noktasıdır. Başarısız teslimatların pahalıya patladığı bir lojistik operasyonunda otomatik kabul eşiğini 0.92 ve üzerine çekin. Manuel incelemenin maliyetinin birkaç hatalı adresin maliyetini aştığı bir pazarlama posta listesinde ise eşiği 0.80'e gevşetin.
API ayrıca bir match_type özelliği döndürür: exact (adresin tamamı eşleşti), interpolated (bilinen kapı numaraları arasında tahmin edilen konum) veya fallback (adresin tamamı bulunamadı ve onun yerine daha geniş bir seviye eşleşti). layer özelliği bunun hangi seviye olduğunu söyler, örneğin address, street veya locality. accuracy ise noktanın kesin bir point mi yoksa bir centroid mi olduğunu belirtir.
Python doğrulama script'i
Bağımlılıkları yükleyin:
pip install requests pandas tqdm
Script, address sütunu (veya ayrı street, city, country sütunları) içeren bir CSV dosyasını okur, her satırı geocode eder ve doğrulama sonuçlarının eklendiği yeni bir CSV dosyası yazar.
#!/usr/bin/env python3
"""
bulk_geocode.py, Validate a CSV of addresses using the MapAtlas Geocoding API.
Input CSV must have either:
- An 'address' column (full address string), or
- 'street', 'city', and 'country' columns (will be concatenated)
Outputs a new CSV with added columns:
geocoded_label, geocoded_lat, geocoded_lng, confidence, match_type, status
"""
import csv
import time
import logging
import requests
import pandas as pd
from tqdm import tqdm
from pathlib import Path
# ── Yapılandırma ───────────────────────────────────────────────────────────────
API_KEY = 'YOUR_API_KEY'
API_BASE = 'https://gateway.mapmetrics-atlas.net/forward-geocode/'
INPUT_CSV = 'addresses.csv'
OUTPUT_CSV = 'addresses_validated.csv'
RATE_LIMIT_RPS = 5 # Saniyedeki istek sayısı (planınızın sınırları içinde kalın)
RETRY_ATTEMPTS = 3 # Geçici hatalarda yeniden deneme sayısı
RETRY_DELAY_S = 2.0 # Yeniden denemeler arasındaki saniye
# Güven puanı eşikleri
ACCEPT_THRESHOLD = 0.85
REVIEW_THRESHOLD = 0.60
logging.basicConfig(level=logging.INFO, format='%(levelname)s %(message)s')
log = logging.getLogger(__name__)
# ── Geocoding fonksiyonu ───────────────────────────────────────────────────────
def geocode_address(address_str: str) -> dict:
"""
Geocode a single address string. Returns a dict with result fields.
Retries on network errors and 429 rate-limit responses.
"""
params = {
'text': address_str,
'token': API_KEY,
'size': 1,
}
for attempt in range(RETRY_ATTEMPTS):
try:
resp = requests.get(API_BASE, params=params, timeout=10)
if resp.status_code == 429:
# Rate limit aşıldı, bekle ve yeniden dene
wait = float(resp.headers.get('Retry-After', RETRY_DELAY_S * (attempt + 1)))
log.warning(f'Rate limited. Waiting {wait:.1f}s before retry {attempt + 1}.')
time.sleep(wait)
continue
resp.raise_for_status()
data = resp.json()
if not data.get('features'):
return {'geocoded_label': '', 'geocoded_lat': None, 'geocoded_lng': None,
'confidence': 0.0, 'match_type': 'no_match', 'status': 'reject'}
feature = data['features'][0]
props = feature['properties']
coords = feature['geometry']['coordinates'] # [lng, lat]
confidence = round(float(props.get('confidence', 0.0)), 4)
match_type = props.get('match_type', 'unknown')
if confidence >= ACCEPT_THRESHOLD:
status = 'accept'
elif confidence >= REVIEW_THRESHOLD:
status = 'review'
else:
status = 'reject'
return {
'geocoded_label': props.get('label', ''),
'geocoded_lat': round(coords[1], 6),
'geocoded_lng': round(coords[0], 6),
'confidence': confidence,
'match_type': match_type,
'status': status,
}
except requests.exceptions.RequestException as exc:
log.warning(f'Network error on attempt {attempt + 1}: {exc}')
if attempt < RETRY_ATTEMPTS - 1:
time.sleep(RETRY_DELAY_S * (attempt + 1))
# Tüm yeniden denemeler tükendi
return {'geocoded_label': '', 'geocoded_lat': None, 'geocoded_lng': None,
'confidence': 0.0, 'match_type': 'error', 'status': 'error'}
# ── Ana işlem döngüsü ──────────────────────────────────────────────────────────
def main():
df = pd.read_csv(INPUT_CSV, dtype=str).fillna('')
# Mevcut sütunlardan adres metnini oluştur
if 'address' in df.columns:
df['_query'] = df['address']
elif all(c in df.columns for c in ['street', 'city', 'country']):
df['_query'] = df['street'] + ', ' + df['city'] + ', ' + df['country']
else:
raise ValueError("CSV must have 'address' or 'street'+'city'+'country' columns.")
results = []
sleep_interval = 1.0 / RATE_LIMIT_RPS
for query in tqdm(df['_query'], desc='Geocoding', unit='addr'):
result = geocode_address(query.strip())
results.append(result)
time.sleep(sleep_interval)
results_df = pd.DataFrame(results)
output_df = pd.concat([df.drop(columns=['_query']), results_df], axis=1)
output_df.to_csv(OUTPUT_CSV, index=False, quoting=csv.QUOTE_NONNUMERIC)
# Özet
total = len(output_df)
accept = (output_df['status'] == 'accept').sum()
review = (output_df['status'] == 'review').sum()
reject = (output_df['status'] == 'reject').sum()
errors = (output_df['status'] == 'error').sum()
log.info(f'\n── Results ────────────────────────────────')
log.info(f'Total processed : {total:,}')
log.info(f'Accept (≥{ACCEPT_THRESHOLD}) : {accept:,} ({accept/total:.1%})')
log.info(f'Review : {review:,} ({review/total:.1%})')
log.info(f'Reject : {reject:,} ({reject/total:.1%})')
log.info(f'Errors : {errors:,} ({errors/total:.1%})')
log.info(f'Output written to {OUTPUT_CSV}')
if __name__ == '__main__':
main()
Çalıştırın:
python bulk_geocode.py
10.000 adreslik bir CSV dosyasında script, saniyede 5 istekle yalnızca rate limit beklemesine en az 33 dakika harcar. Buna istek başına gecikme eklenince toplam süre genellikle 35-50 dakika olur. tqdm ile gösterilen ilerleme çubuğu, anlık işlem hızını ve tahmini bitiş süresini gösterir.
Çıktıyı değerlendirin
Çıktı CSV dosyası, giriş verilerinize altı sütun ekler:
address, ..., geocoded_label, geocoded_lat, geocoded_lng, confidence, match_type, status
Üç ayrı çıktı dosyası oluşturmak için duruma göre filtreleyin:
# main() çalıştıktan sonra kabul seviyelerine göre ayır:
df = pd.read_csv('addresses_validated.csv')
df[df['status'] == 'accept'].to_csv('addresses_clean.csv', index=False)
df[df['status'] == 'review'].to_csv('addresses_review.csv', index=False)
df[df['status'] == 'reject'].to_csv('addresses_reject.csv', index=False)
print(f"Clean: {len(df[df['status']=='accept']):,} addresses ready for use")
print(f"Review: {len(df[df['status']=='review']):,} addresses for manual check")
print(f"Reject: {len(df[df['status']=='reject']):,} addresses to discard or fix")
İnsan incelemesi gerektiren dosya addresses_review.csv dosyasıdır. Tipik inceleme örüntüleri:
streetkatmanına fallback (güven puanı 0.65 ile 0.80): Sokak bulundu ama kapı numarası bulunamadı. Büyük olasılıkla yeni bir bina, verinin seyrek olduğu kırsal bir adres ya da kapı numarasında bir yazım hatası söz konusudur. Orijinal kaynağı kontrol edin.localitykatmanına fallback (güven puanı 0.50 ile 0.65): Yalnızca şehir eşleşti. Sokak adı büyük olasılıkla yanlış yazılmış ya da veride yok. Adresi bir posta rehberinde arayın.- Açıkça geçerli görünen adreslerde düşük güven puanı: Ülke veya dil uyuşmazlığı olup olmadığını kontrol edin. Ülke kısıtlaması olmadan sorgulanan Hollandaca bir adres, benzer isimli bir Alman ya da Belçika kasabasına geocode edilebilir.
Bilmeniz gereken AB adres farklılıkları
ABD pazarı için yazılmış toplu geocoding rehberleri, AB veri setlerinde sorun çıkaran format farklılıklarını atlar. En yaygın sorunlar şunlardır:
Almanya, kapı numarası sırası. Alman adresleri {street} {number} biçimini kullanır: Berliner Straße 42. Birçok CRM, Birleşik Krallık ve ABD kurallarına göre geliştirildiği için adresleri {number} {street} biçiminde saklar. Alman adresleriniz düşük güven puanıyla geocode ediliyorsa, göndermeden önce sorgu metninde numara ile sokak adının yerini değiştirmeyi deneyin.
Fransa, arrondissement'lar. Paris adresleri posta kodunun bir parçası olarak bir arrondissement (1. ile 20. arası) içerir: 75001 ile 75020 arası. Arrondissement'ı atlayıp yalnızca Paris kullanan sorgular belirli bir bölgeye değil, şehrin merkez noktasına geocode edilir. Bu da address eşleşmesi yerine düşük güven puanlı bir locality eşleşmesi olarak görünür.
Hollanda, posta kodu biçimi. Hollanda posta kodları katı bir DDDD LL düzenine uyar (4 rakam, boşluk, 2 büyük harf). Boşluksuz (1012 LG yerine 1012LG) ya da küçük harfle saklanan kodlar doğru şekilde geocode edilir. Ancak posta kodu biçimini ayrıca doğruluyorsanız, kodları boşluklu ve büyük harfli biçime normalleştirin.
Belçika, dil belirsizliği. Bazı Belçika belediyelerinin Fransızca ve Hollandaca adları farklıdır (Liège/Luik, Gent/Gand). API her ikisini de tanır, ancak veri setinizdeki tutarsız adlandırma (bazı kayıtlarda Fransızca, bazılarında Hollandaca) farklı güven puanları üretebilir. Geocoding'den önce her bölge için tek bir dile normalleştirin.
İspanya ve İtalya, sokak ön eki farklılıkları. "Calle", "Carrer", "Via" ve "Viale" geçerli sokak türü ön ekleridir ve kayıtlarda kısaltılmış biçimde ("C/") görünebilir. Geocoder yaygın kısaltmaları tanır, ancak eski sistemlerden gelen alışılmadık kısaltmalar için bir normalleştirme adımı gerekebilir.
Eşzamanlı isteklerle işlem hızını artırın
Sıralı script güvenli ve basittir ama yavaştır. Daha büyük veri setleri için sıralı döngüyü eşzamanlı bir executor ile değiştirin:
from concurrent.futures import ThreadPoolExecutor, as_completed
def main_concurrent(max_workers=10):
df = pd.read_csv(INPUT_CSV, dtype=str).fillna('')
# ... (önceki kurulumla aynı) ...
results = [None] * len(df)
with ThreadPoolExecutor(max_workers=max_workers) as executor:
future_to_idx = {
executor.submit(geocode_address, row['_query'].strip()): idx
for idx, row in df.iterrows()
}
for future in tqdm(as_completed(future_to_idx), total=len(df), desc='Geocoding'):
idx = future_to_idx[future]
results[idx] = future.result()
# ... (önceki çıktı yazma adımıyla aynı) ...
10 eşzamanlı worker ile işlem hızını bir bekleme süresi değil, API gecikmesi ve planınızın rate limit'i belirler: saniyede 50 istekle 10.000 adres 4 dakikadan kısa sürede işlenir. Eşzamanlı sürümde yerleşik bir hız sınırlayıcı yoktur, bu yüzden planınız saniyedeki istek sayısını sınırlıyorsa bir sınırlayıcı ekleyin ya da max_workers değerini düşürün. max_workers değerini artırmadan önce MapAtlas planınızın eşzamanlı istek sınırını kontrol edin.
Adres doğrulamayı düzenli bir işlem olarak yürüten kuruluşlar için, örneğin yeni CRM aktarımlarını her hafta temizleyenler ya da teslimat adreslerini her gece doğrulayanlar için, MapAtlas entegrasyonlarının operasyonel iş akışlarına nasıl oturduğunu görmek üzere Lojistik ve teslimat çözümleri sayfasına bakın.
Hatalı verinin sisteminize baştan girmesini önlemek için adres otomatik tamamlama geliştiriyorsanız (hataları toplu temizlik yerine kaynağında yakalamak için), Adres otomatik tamamlama API'si: tek bir alan ödeme dönüşümünü nasıl artırır yazısı frontend uygulamasını anlatıyor.
Koordinat çıktısıyla çalışın
Doğrulanmış çıktı, kabul edilen her adres için geocoded_lat ve geocoded_lng değerlerini içerir. Bu koordinatlar, ham adres metinleriyle mümkün olmayan analizlerin önünü açar:
- Mesafe hesaplamaları. Kargo maliyeti kademelerini tahmin etmek için depo ile her teslimat adresi arasındaki kuş uçuşu mesafeyi hesaplayın.
- Etki alanı analizi. Talebin coğrafi olarak nerede yoğunlaştığını görmek için doğrulanmış müşteri konumlarını haritada gösterin.
- Teslimat bölgesi ataması. Koordinatların bir bölge poligonunun içine düşüp düşmediğini test ederek her adresi bir teslimat bölgesine atayın.
- Mükerrer kayıt tespiti. Adres metinleri biçim olarak farklı olsa bile aynı koordinatlara (birkaç metre içinde) sahip iki kayıt büyük olasılıkla mükerrerdir.
NAP (ad, adres, telefon) tutarlılığı ve bunun özellikle yerel işletme adres veritabanları için AI arama görünürlüğüne etkisi hakkında AI araması için NAP tutarlılığı: uyumsuz adresler ChatGPT görünürlüğünüzü neden yok eder yazısı, geocoding ile doğrulanmış adreslerin yapılandırılmış veri işaretlemesi için neden doğru temel olduğunu açıklıyor.
Özet
MapAtlas Geocoding API ile toplu geocoding size şunları sağlar:
- Doğrulanmış adresler ve güven puanları: hangi kayıtlara otomatik olarak güvenebileceğinizi, hangilerinin incelenmesi gerektiğini bilirsiniz.
- Yapılandırılmış bileşenler (sokak, kapı numarası, posta kodu, şehir, ülke): her türlü giriş biçiminden normalleştirilmiş olarak.
- Coğrafi koordinatlar: kabul edilen her adres için, mekânsal analiz ve rota planlamayı mümkün kılar.
- AB adres formatı desteği: API'nin içinde yerleşik olarak gelir, ön işleme adımında sizin ele almanız gerekmez.
Python script'i sıralı modda saniyede 5 adres, eşzamanlı worker'larla saniyede 50 adrese kadar işler. 10.000 adres için eşzamanlılığa ve gecikmeye bağlı olarak 4 ile 50 dakika arasında süre ayırın. Çıktı, üç kademeli temiz bir CSV dosyasıdır: kabul, inceleme, ret.
Başlamak için ücretsiz MapAtlas API anahtarı almak üzere kaydolun. Geocoding API tüm planlarda toplu sorguları destekler. İstek başına ücretler ve aylık ücretsiz kullanım sınırları için fiyatlandırma sayfasına bakın.
Sıkça Sorulan Sorular
MapAtlas API ile 10.000 adresi geocode etmek ne kadar sürer?
Saniyede 5 istekle 10.000 adres, yalnızca rate limit beklemesi için en az 33 dakika sürer ve buna her isteğin ağ gecikmesi eklenir, bu yüzden 35-50 dakika ayırın. Saniyede yaklaşık 50 istekle çalışan 10 eşzamanlı worker ile, planınızın rate limit'i izin verdiği sürece bu süre 5 dakikanın altına iner. Bu eğitimdeki Python betiği hem sıralı hem de eşzamanlı seçenekleri içerir.
Adres doğrulamada hangi güven puanı eşiğini kullanmalıyım?
Üç kademeli bir sistem pratikte iyi çalışır: güven puanı 0,85'in üzerindeki adresleri kabul edin, 0,60 ile 0,85 arasındakileri manuel incelemeye işaretleyin, 0,60'ın altındakileri reddedin. Kesin eşikler, adres doğruluğunun kullanım senaryonuz için ne kadar kritik olduğuna bağlıdır: lojistik operasyonları pazarlama kampanyalarından daha sıkı eşikler kullanmalıdır.
MapAtlas Geocoding API AB'ye özgü adres formatlarını destekliyor mu?
Evet. API; Alman (ev numarası sokak adından sonra), Fransız (ev numarası sokak adından önce) ve Hollanda (4+2 posta kodu) adresleri dahil AB ülkelerinin adres kurallarını doğru biçimde ayrıştırır. Yerel dilde yazılan sorgular, harf çevirisi yapılmış veya İngilizce formatlı sorgulardan daha iyi sonuç döndürür.

