Tüm yazılar
Pazarlama 6 dk okuma

Smart Lead Discovery Toolkit: Google Maps ile Lead Generation

Google Maps lead generation: Places API ile segment ve ilçe bazlı firma listesi çıkarma, sayfalama, alan maskesiyle maliyet kontrolü ve KVKK sınırları.

#google-maps-api #lead-generation #python #excel #automation

Yeni bir pazara girerken ya da mevcut ürünü yeni bir segmente satarken ilk ihtiyaç hep aynı: kime satacağımızı bilmek. Google Maps lead generation tam bu noktada devreye giriyor; çünkü Türkiye'de küçük bir kafeden büyük bir lojistik firmasına kadar hemen her işletmenin bir Maps kaydı var. Doğru sorgularla bu kayıtları sistematik biçimde toplayıp temiz bir firma listesi çıkarmak, satış öncesi araştırma sürecinin en somut ilk adımı.

Bu yazıda kendi projelerimde kullandığım yaklaşımı anlatacağım: Google Places API ile segment ve coğrafya bazlı hedef firma listesi çıkarmak, sonuçları Excel/CSV'ye yazmak ve süreci tekrarlanabilir hâle getirmek. Amacım kopyala-yapıştır bir betik vermek değil; maliyet kontrolü ve hukuki sınırlar dâhil bütün resmi göstermek. Deneyimimde bu tür projelerin takıldığı yer kod değil, bu iki konu oluyor.

Yazının sonunda elinizde şunlar olacak: Text Search ile sayfalamayı doğru yöneten bir toplama betiği, Place Details ile kayıtları zenginleştirip Excel'e yazan ikinci bir betik ve bütün bunları tekrarlanabilir kılan bir sorgu ızgarası yaklaşımı. Kod örnekleri Python ile; requests ve openpyxl dışında egzotik hiçbir bağımlılık yok.

Text Search mi, Nearby Search mü?

Places API'de firma aramak için iki temel uç nokta var ve hangisini seçtiğiniz, listenizin kalitesini doğrudan etkiliyor:

  • Text Search serbest metinli sorgu alır: "Kadıköy muhasebeci" ya da "Bursa Nilüfer kırtasiye" gibi. Segment adıyla coğrafi adı tek sorguda birleştirebildiğiniz için hedefli liste çıkarmada ilk tercihim genellikle bu.
  • Nearby Search ise koordinat ve yarıçapla çalışır: bir merkez noktası verirsiniz, çevresindeki işletmeleri tür filtresiyle döker. Adres bazlı düşünmek yerine haritayı dairelere bölüp taramak istediğinizde kullanışlıdır.

Tür filtresi konusunda beklentiyi doğru kurmak lazım: Google'ın işletme tür listesi geniş ama Türkiye'deki her sektörü birebir karşılamıyor. Bu yüzden ben tür filtresine yaslanmak yerine Text Search'te sektörün Türkçe adını sorguya yazmayı tercih ediyorum; "nalbur", "emlak ofisi" gibi yerel terimler tipik olarak tür filtresinden daha isabetli sonuç veriyor.

İki uçta da kritik bir sınır var: tek bir sorgu, sayfalama dâhil sınırlı sayıda sonuç döndürür. Yani "İstanbul restoran" gibi geniş bir sorguyla şehrin tamamını çekemezsiniz. Bu sınır, birazdan anlatacağım segment×ilçe ızgarasının da varlık sebebi: sorguyu coğrafi olarak küçülttükçe o bölgedeki kapsama oranınız artar.

Sayfalama: jetonu bekleyen kazanır

Tek yanıt genellikle yirmi civarı kayıt taşır; devamı için yanıtla birlikte gelen sayfa jetonunu (klasik API'de next_page_token, yeni Places API'de nextPageToken) bir sonraki isteğe eklersiniz.

Burada en sık düşülen tuzak şu: jeton yanıtla birlikte gelse de hemen geçerli olmayabiliyor. Klasik API'de jetonun aktifleşmesi için birkaç saniye beklemek gerekiyordu; ben yeni API'de de sayfalar arasına kısa bir time.sleep koymayı alışkanlık hâline getirdim — hem bu sorunu çözüyor hem de kota tüketimini nazikleştiriyor.

Aşağıdaki örnek, bir sorgunun tüm sayfalarını gezip sonucu CSV'ye yazıyor. Dikkat edin: API anahtarı koda gömülü değil, ortam değişkeninden okunuyor — anahtarınızı asla betiğin içine veya bir Git deposuna yazmayın.

import os
import csv
import time
import requests

API_KEY = os.environ["GOOGLE_MAPS_API_KEY"]  # anahtar ortam degiskeninden okunur
URL = "https://places.googleapis.com/v1/places:searchText"
ALAN_MASKESI = "places.id,places.displayName,places.formattedAddress,nextPageToken"

def firma_ara(sorgu):
    headers = {
        "X-Goog-Api-Key": API_KEY,
        "X-Goog-FieldMask": ALAN_MASKESI,
    }
    govde = {"textQuery": sorgu, "languageCode": "tr"}
    sonuclar = []
    while True:
        yanit = requests.post(URL, json=govde, headers=headers, timeout=30)
        yanit.raise_for_status()
        veri = yanit.json()
        sonuclar.extend(veri.get("places", []))
        token = veri.get("nextPageToken")
        if not token:
            break
        govde = {"textQuery": sorgu, "languageCode": "tr", "pageToken": token}
        time.sleep(2)  # jetonun aktiflesmesi ve kota nezaketi icin bekle
    return sonuclar

satirlar = firma_ara("muhasebeci Kadikoy Istanbul")
with open("ham_liste.csv", "w", newline="", encoding="utf-8-sig") as f:
    yazici = csv.writer(f)
    yazici.writerow(["place_id", "firma", "adres"])
    for p in satirlar:
        yazici.writerow([
            p.get("id"),
            p.get("displayName", {}).get("text"),
            p.get("formattedAddress"),
        ])

Gerçek projede bu döngüye basit bir hata yönetimi de eklemekte fayda var: kota aşımı yanıtlarında bekleyip yeniden denemek, gün sonunda eksik sayfalarla uğraşmaktan iyidir.

Place Details ve alan maskesiyle maliyet kontrolü

Arama sonuçları ad, adres ve place_id gibi temel alanları verir; telefon, web sitesi, çalışma saatleri gibi satış ekibinin asıl istediği alanlar için her kaydı Place Details ile ayrıca sorgulamak gerekir.

Tam bu noktada işin maliyet boyutu devreye giriyor. Places API ücretli bir servis ve faturayı belirleyen şey istek sayısı kadar, istediğiniz alanlardır. Google alanları farklı fiyat kademelerine ayırıyor; X-Goog-FieldMask başlığıyla hangi alanları istediğinizi açıkça bildiriyorsunuz ve o kapsam üzerinden ücretlendiriliyorsunuz. Temel kuralım şu: alan maskesine ihtiyacınız olmayan hiçbir alanı yazmayın. Kesin rakam vermiyorum, çünkü fiyatlandırma alan maskesine ve zamana göre değişiyor; projeye başlamadan güncel fiyat listesine mutlaka bakın.

Hangi alanlar gerçekten gerekli?

Kendi projelerimde satış listesi için tipik olarak şu çekirdek küme yetiyor:

  • id ve displayName — tekilleştirme ve firmanın adı için.
  • formattedAddress — bölge bazlı filtreleme ve saha planlaması için.
  • nationalPhoneNumber ve websiteUri — ilk temas ve web'den doğrulama için.
  • rating ve userRatingCount — önceliklendirmede kaba bir sinyal olarak; yorum sayısı çok düşük kayıtları genellikle ayrı ele alıyorum.

Aşağıdaki örnek, elimizdeki place_id listesini detaylarla zenginleştirip openpyxl ile Excel'e yazıyor. Telefon ve web sitesi geldikten sonra listeyi veri zenginleştirme pipeline'ı ile e-posta, sektör kodu gibi ek katmanlarla beslemek de mümkün; Maps çıktısını ben genellikle o sürecin ilk halkası olarak kullanıyorum.

import os
import time
import requests
from openpyxl import Workbook

API_KEY = os.environ["GOOGLE_MAPS_API_KEY"]
DETAY_MASKESI = "id,displayName,nationalPhoneNumber,websiteUri,rating,userRatingCount"

def detay_getir(place_id):
    url = f"https://places.googleapis.com/v1/places/{place_id}"
    headers = {"X-Goog-Api-Key": API_KEY, "X-Goog-FieldMask": DETAY_MASKESI}
    yanit = requests.get(url, headers=headers, timeout=30)
    yanit.raise_for_status()
    return yanit.json()

def listeyi_zenginlestir(place_id_listesi, dosya_adi="firmalar.xlsx"):
    wb = Workbook()
    ws = wb.active
    ws.append(["place_id", "Firma", "Telefon", "Web", "Puan", "Yorum sayisi"])
    gorulenler = set()
    for pid in place_id_listesi:
        if pid in gorulenler:
            continue  # ayni firma birden fazla sorgudan gelmis olabilir
        gorulenler.add(pid)
        d = detay_getir(pid)
        ws.append([
            d.get("id", ""),
            d.get("displayName", {}).get("text", ""),
            d.get("nationalPhoneNumber", ""),
            d.get("websiteUri", ""),
            d.get("rating", ""),
            d.get("userRatingCount", ""),
        ])
        time.sleep(0.2)  # kota ve nezaket arasi
    wb.save(dosya_adi)

Tekilleştirme ve segment×ilçe ızgarası

Sorgu sayısı arttıkça aynı firma kaçınılmaz olarak birden fazla kez gelir: komşu ilçe sorguları çakışır, bir işletme hem "kafe" hem "pastane" sorgusunda görünür. Neyse ki Google her işletmeye kalıcı bir place_id verir; tekilleştirme için bu alanı birincil anahtar olarak kullanmak yeterli. Yukarıdaki ikinci örnekteki gorulenler kümesi tam olarak bunu yapıyor.

place_id farklı ama işletme fiilen aynıysa — örneğin bir zincirin şubeleri ya da Maps'e iki kez kaydedilmiş bir firma — iş isim ve adres benzerliğine bakar; bu ayrı bir problem ve çözümünü fuzzy matching ile kayıt eşleştirme yazısında ayrıntılı anlattım.

Sürdürülebilirliğin sırrı ise sorguları kafadan atmak yerine bir ızgaraya oturtmak. Ben şöyle kuruyorum:

  • Satırlara segmentleri yazın: "muhasebeci", "diş kliniği", "yapı market" gibi, kendi hedef tanımınıza göre.
  • Sütunlara coğrafyayı koyun: il yerine ilçe, yoğun bölgelerde gerekirse mahalle düzeyi.
  • Her hücre bir Text Search sorgusuna dönüşür; betik bu matrisi döngüyle gezer.
  • Izgarayı bir YAML ya da CSV dosyasında tutun; birkaç ay sonra aynı taramayı tekrarlayıp yeni açılan firmaları yakalamak tek komutluk iş hâline gelir.

Hukuki ve etik sınırlar

Teknik kısım bu kadar; ama bu işi danışmanlık olarak yapan biri olarak en az kod kadar önemsediğim kısım burası.

  • Hizmet şartları: Google Maps Platform şartları, çektiğiniz verinin saklanmasına ve yeniden dağıtımına belirgin sınırlar koyar. Genel kural olarak place_id süresiz saklanabilir; diğer içeriklerin uzun süreli önbelleklenmesi ise kısıtlıdır. Maps verisini olduğu gibi üçüncü taraflara satmak veya ondan rakip bir veri tabanı kurmak açıkça şartlara aykırıdır. Projeye başlamadan güncel şartları okuyun.
  • KVKK: "firma verisi" dediğimiz şey her zaman anonim değildir. Şahıs işletmelerinde işletme telefonu çoğu zaman kişinin kendi telefonudur; kişi adı içeren işletme adları da kişisel veri sayılabilir. Toplarken amacınızı belirleyin, gereğinden fazlasını tutmayın ve bir saklama süresi tanımlayın.
  • Pazarlama iletişimi: listeyi çıkarmak ayrı, o listeye toplu e-posta atmak veya arama yapmak apayrı bir mevzuat konusudur. Türkiye'de ticari elektronik ileti onaya tabidir; izinsiz toplu gönderim hem hukuki risk hem de marka itibarı sorunudur. Ben bu listeleri soğuk gönderim motoru olarak değil, satış öncesi araştırma ve önceliklendirme aracı olarak konumlandırıyorum.

Kısacası: veriyi toplayabiliyor olmanız, her amaçla kullanabileceğiniz anlamına gelmez. Şüpheye düştüğünüz senaryoda hukuk görüşü almak, sonradan temizlik yapmaktan her zaman daha ucuzdur.

Sonuç

Google Places API, doğru kurgulandığında satış ekibine el ile Maps'te gezerek asla ulaşamayacağı bir düzen ve tekrarlanabilirlik sağlıyor. Özet reçetem: segment×ilçe ızgarasını kur, Text Search ile tara, sayfalamayı jeton beklemesiyle yönet, alan maskesini sıkı tut, place_id ile tekilleştir, çıktıyı Excel'e yaz — ve hukuki çerçeveyi işin başında netleştir.

Benzer bir toolkit'i kendi satış süreciniz için kurmak isterseniz veri otomasyonu çalışmalarıma göz atabilir, projenizi konuşmak için iletişim bölümünden bana yazabilirsiniz. Elinizdeki pazar tanımını birlikte bir sorgu ızgarasına çevirmek, deneyimimde ilk görüşmenin en verimli kısmı oluyor.