Google People Also Ask ve Autocomplete Kazıma Nedir?
Google People Also Ask (PAA) ve Autocomplete verilerini kazımak, SEO uzmanları ve geliştiriciler için en verimli ücretsiz anahtar kelime araştırma yöntemlerinden biridir. Üç temel veri kaynağı vardır: Autocomplete öneri endpoint'i, PAA soruları ve Related Searches. Bu rehberde her birini Python ile nasıl kazıyacağınızı, residential proxy'lerle nasıl lokalize edeceğinizi ve sonuçları nasıl kümeleyeceğinizi göstereceğiz.
Bu yöntemler, Google'ın resmi Custom Search API'sinden farklı olarak tamamen ücretsizdir ve gerçek kullanıcı sorgularını yansıtır. Ancak ölçeklendirildiğinde IP tabanlı hız sınırlarına takılır; bu yüzden residential proxy kullanımı kritik hale gelir.
Üç Ücretsiz Anahtar Kelime Altın Madeni
Google'ın arama sonuç sayfasında ve tarayıcı arama kutusunda üç farklı öneri mekanizması çalışır. Her biri farklı bir arama niyetini (search intent) temsil eder:
1. Autocomplete (Suggest) Endpoint
Google'ın Autocomplete servisi, suggestqueries.google.com/complete/search?client=chrome&q= adresine JSON döndürür. Bu endpoint, tarayıcı arama kutusuna yazılan her harfle birlikte gerçek zamanlı öneriler üretir. Kullanıcılar daha aramayı tamamlamadan ne yazacaklarını düşünür; bu yüzden bilgi arama (informational intent) yoğunludur.
Örnek: q="keto diyet" için dönen öneriler genellikle keto diyet nedir, keto diyet listesi, keto diyet zararları gibi soru kalıplarıdır.
2. People Also Ask (PAA)
PAA kutuları, arama sonuçlarında görünen ve tıklandıkça iç içe geçen soru akordeonlarıdır. Her soru genellikle bir karşılaştırma veya derinlemesine bilgi arama niyetini yansıtır. Bir PAA sorusunu tıkladığınızda Google yeni sorular yükler; bu recursive expansion olarak bilinir ve tek bir anahtar kelimeden onlarca soru çıkarabilir.
3. Related Searches (İlgili Aramalar)
Sayfanın en altında yer alan Related Searches, aramayı tamamlamış kullanıcıların sonraki adımlarını gösterir. Transactional ve navigational intent yoğunludur; örneğin keto diyet kitabı satın al gibi.
| Kaynak | Dominant Intent | Format | Veri Çekme Zorluğu |
|---|---|---|---|
| Autocomplete | Informational | JSON (client=chrome) | Düşük |
| People Also Ask | Informational + Comparison | HTML (accordion) | Orta-Yüksek |
| Related Searches | Transactional | HTML | Düşük |
Teknik Bağlam: Neden Bu Problem Var?
Google'ın öneri sistemleri, arama kutusuna her tuş basışında gerçek zamanlı olarak çalışır. Bu, tek bir IP'den saniyede onlarca istek gönderdiğinizde Google'ın bot korumasını tetikler. HTTP 429 Too Many Requests yanıtı veya CAPTCHA sayfası alırsınız.
Ayrıca Autocomplete önerileri, isteğin geldiği IP'nin coğrafi konumuna göre değişir. Berlin'den yapılan bir arama ile İstanbul'dan yapılan arama farklı öneriler döndürür. Bu locale bias, doğru lokalize veri toplamak için residential proxy'leri zorunlu kılar.
Veri merkezi (datacenter) IP'leri Google tarafından kolayca işaretlenir ve bloke edilir. SERP takibi ve öneri kazıma gibi işlemlerde residential proxy'ler çok daha yüksek başarı oranı sağlar; tipik olarak %90+ başarı oranına karşı datacenter proxy'lerde %30-50 civarı.
ProxyHat ile Residential Proxy Kurulumu
ProxyHat residential proxy'leri, ülke ve şehir seviyesinde geo-targeting destekler. Kullanıcı adı içinde bayraklar ile lokasyon belirlenir. Örneğin Berlin'den gelen istekler için:
# HTTP proxy URL formatı
http://user-country-DE-city-berlin:pass@gate.proxyhat.com:8080
# SOCKS5 proxy URL formatı
socks5://user-country-DE-city-berlin:pass@gate.proxyhat.com:1080
# Sticky session (aynı IP'de kal)
http://user-country-DE-city-berlin-session-abc123:pass@gate.proxyhat.com:8080
Mevcut lokasyonlar sayfasından desteklenen ülkeleri ve şehirleri görebilirsiniz. Autocomplete kazıma için her ülke için ayrı oturum açmak, o bölgeye özel önerileri yakalamanın en iyi yoludur.
Python ile Autocomplete Kazıma: httpx + ProxyHat
Aşağıdaki örnek, bir seed kelimeyi a-z harfleri ve yaygın modifier'larla genişleterek yüzlerce uzun kuyruklu öneri üretir. httpx ile asenkron istekler gönderir ve ProxyHat residential proxy'lerini kullanır.
import httpx
import asyncio
import json
import logging
from typing import List, Set
# Logging konfigürasyonu
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
logger = logging.getLogger(__name__)
PROXY_URL = "http://user-country-DE-city-berlin:PASSWORD@gate.proxyhat.com:8080"
AUTOCOMPLETE_URL = "https://suggestqueries.google.com/complete/search"
# Hata yeniden deneme decorator benzeri fonksiyon
async def fetch_with_retry(client: httpx.AsyncClient, params: dict, max_retries: int = 3) -> dict:
for attempt in range(max_retries):
try:
response = await client.get(AUTOCOMPLETE_URL, params=params, timeout=10.0)
if response.status_code == 200:
return response.json()
elif response.status_code == 429:
wait = 2 ** attempt
logger.warning(f"Rate limited, waiting {wait}s...")
await asyncio.sleep(wait)
else:
logger.error(f"HTTP {response.status_code}")
break
except httpx.RequestError as e:
logger.error(f"Request error: {e}")
await asyncio.sleep(1)
return {}
async def get_autocomplete_suggestions(keyword: str, client: httpx.AsyncClient) -> List[str]:
params = {
"client": "chrome",
"q": keyword,
"hl": "de", # dil parametresi
"gl": "de" # ülke parametresi
}
data = await fetch_with_retry(client, params)
# Chrome client JSON formatı: ["query", ["sug1", "sug2", ...], ...]
if data and len(data) > 1:
return data[1]
return []
def build_expanded_seeds(base_keyword: str) -> List[str]:
seeds = [base_keyword]
# a-z harf genişletme
for c in "abcdefghijklmnopqrstuvwxyz":
seeds.append(f"{base_keyword} {c}")
# Modifier prefix'leri
modifiers = ["nedir", "nasıl", "neden", "fiyat", "ücretsiz", "en iyi", "2024", "vs"]
for m in modifiers:
seeds.append(f"{base_keyword} {m}")
seeds.append(f"{m} {base_keyword}")
return seeds
async def expand_keyword(base_keyword: str) -> Set[str]:
seeds = build_expanded_seeds(base_keyword)
all_suggestions = set()
async with httpx.AsyncClient(proxy=PROXY_URL) as client:
# Semalt-style batch processing with concurrency limit
semaphore = asyncio.Semaphore(5) # Aynı anda en fazla 5 istek
async def process_seed(seed: str):
async with semaphore:
suggestions = await get_autocomplete_suggestions(seed, client)
for s in suggestions:
all_suggestions.add(s)
logger.info(f"Seed: {seed} -> {len(suggestions)} suggestions")
await asyncio.sleep(0.5) # Polite delay
await asyncio.gather(*[process_seed(s) for s in seeds])
return all_suggestions
# Çalıştırma
if __name__ == "__main__":
results = asyncio.run(expand_keyword("keto diyet"))
print(f"Toplam {len(results)} benzersiz öneri bulundu")
for r in sorted(results)[:20]:
print(f" - {r}")
Bu kod tek bir seed kelimeden ~300-500 öneri üretebilir. Concurrency'yi 5'te tutmak ve her istekler arası 500ms bekleme, Google'ın rate limit'lerini tetiklememek için kritiktir.
Playwright ile PAA Akordeonlarını Genişletme
PAA soruları, sayfa yüklendiğinde sadece ilk 3-4 sorunu gösterir. Geri kalanlar, bir soruya tıklandığında dinamik olarak yüklenir. Bu recursive expansion için headless browser gerekir. Playwright Python bu iş için idealdir.
import asyncio
from playwright.async_api import async_playwright
import json
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
PROXY_SERVER = "gate.proxyhat.com:8080"
PROXY_USER = "user-country-DE-city-berlin"
PROXY_PASS = "PASSWORD"
async def scrape_paa(keyword: str, max_clicks: int = 10) -> list:
paa_questions = []
async with async_playwright() as p:
browser = await p.chromium.launch(
headless=True,
proxy={
"server": f"http://{PROXY_SERVER}",
"username": PROXY_USER,
"password": PROXY_PASS
}
)
context = await browser.new_context(
locale="de-DE",
viewport={"width": 1920, "height": 1080}
)
page = await context.new_page()
url = f"https://www.google.com/search?q={keyword}&hl=de&gl=de"
await page.goto(url, wait_until="domcontentloaded")
# PAA bloklarını bul
paa_selector = "div[jsname] div.related-question-pair"
for click_count in range(max_clicks):
try:
# Görünür PAA sorularını topla
questions = await page.query_selector_all(paa_selector)
for q in questions:
text = await q.inner_text()
if text and text.endswith("?") and text not in paa_questions:
paa_questions.append(text)
logger.info(f"PAA: {text}")
# İlk tıklanmamış PAA akordeonunu tıkla
accordion = await page.query_selector("div[jsname='Cpkphb']")
if accordion:
await accordion.click()
await page.wait_for_timeout(1500) # Dinamik içerik için bekle
else:
logger.info("Daha fazla akordeon bulunamadı")
break
except Exception as e:
logger.error(f"Tıklama hatası: {e}")
break
await browser.close()
return paa_questions
# Çalıştırma
if __name__ == "__main__":
questions = asyncio.run(scrape_paa("keto diyet", max_clicks=15))
print(f"\nToplam {len(questions)} PAA sorusu bulundu:")
for q in questions:
print(f" - {q}")
PAA Sorularından Yanıt Kaynaklarını Çıkarma
PAA sorularının her birinin bir kaynak linki vardır. Bu linkleri de çıkararak içerik planlamada kullanabileceğiniz rakip kaynakları tespit edebilirsiniz:
async def scrape_paa_with_sources(keyword: str, max_clicks: int = 10) -> list:
results = []
async with async_playwright() as p:
browser = await p.chromium.launch(
headless=True,
proxy={
"server": f"http://{PROXY_SERVER}",
"username": PROXY_USER,
"password": PROXY_PASS
}
)
context = await browser.new_context(locale="de-DE")
page = await context.new_page()
await page.goto(f"https://www.google.com/search?q={keyword}&hl=de&gl=de")
await page.wait_for_timeout(2000)
# PAA sorularını ve kaynak linklerini çıkar
paa_blocks = await page.query_selector_all("div.related-question-pair")
for block in paa_blocks:
question_el = await block.query_selector("div.Cc5ZCb span")
source_el = await block.query_selector("a[href]")
question = await question_el.inner_text() if question_el else ""
source_url = await source_el.get_attribute("href") if source_el else ""
source_text = await source_el.inner_text() if source_el else ""
if question:
results.append({
"question": question,
"source_url": source_url,
"source_domain": source_text
})
await browser.close()
return results
Related Searches Çıkarma (curl + Python)
Related Searches, sayfanın en altında basit HTML olarak gelir. Proxy ile birlikte curl kullanarak hızlıca çıkarabilirsiniz:
import httpx
import asyncio
from bs4 import BeautifulSoup
import re
PROXY_URL = "http://user-country-DE-city-berlin:PASSWORD@gate.proxyhat.com:8080"
async def get_related_searches(keyword: str) -> list[str]:
url = f"https://www.google.com/search?q={keyword}&hl=de&gl=de"
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
"Accept-Language": "de-DE,de;q=0.9"
}
async with httpx.AsyncClient(proxy=PROXY_URL, headers=headers) as client:
resp = await client.get(url, timeout=15.0)
if resp.status_code != 200:
return []
soup = BeautifulSoup(resp.text, "html.parser")
related = []
# Related searches linkleri p.bhDWb sınıfında bulunur
for a in soup.select("a"):
href = a.get("href", "")
if "/search?q=" in href and "spell=" not in href:
text = a.get_text(strip=True)
if text and len(text) > 3:
related.append(text)
return list(set(related))
# curl ile manuel test:
# curl -x http://user-country-DE-city-berlin:PASSWORD@gate.proxyhat.com:8080 \
# "https://www.google.com/search?q=keto+diyet&hl=de&gl=de" \
# -H "User-Agent: Mozilla/5.0" -H "Accept-Language: de-DE"
Dedup, Intent Kümeleme ve CSV Dışa Aktarma
Üç kaynaktan gelen verileri birleştirip intent'e göre kümelemek, içerik takvimini planlamanın anahtarıdır. Aşağıdaki kod, tüm önerileri toplar, tekrarları kaldırır ve basit kural tabanlı intent sınıflaması yapar:
import csv
import re
from collections import defaultdict
def classify_intent(query: str) -> str:
q = query.lower()
# Informational
if any(w in q for w in ["nedir", "nasıl", "neden", "kim", "ne zaman", "ne demek"]):
return "informational"
# Transactional
if any(w in q for w in ["satın al", "fiyat", "ücret", "indirim", "kampanya", "bedava"]):
return "transactional"
# Commercial investigation
if any(w in q for w in ["en iyi", "karşılaştır", "vs", "hangi", "review", "yorum"]):
return "commercial"
# Navigational
navigational if any(w in q for w in ["giriş", "kaydol", "resmi site", "login"]):
return "navigational"
return "other"
def cluster_by_topic(queries: list[str]) -> dict:
clusters = defaultdict(list)
for q in queries:
# İlk 2 kelimeyi topic key olarak kullan
words = q.split()[:2]
topic = " ".join(words) if len(words) >= 2 else q
clusters[topic].append(q)
return dict(clusters)
def export_to_csv(all_queries: set[str], filename: str = "keyword_research.csv"):
# Dedup
unique = list(set(all_queries))
# Intent sınıflandır
classified = [(q, classify_intent(q)) for q in unique]
# Topic clustering
clusters = cluster_by_topic(unique)
with open(filename, "w", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
writer.writerow(["query", "intent", "cluster_topic", "word_count"])
for q, intent in classified:
topic = " ".join(q.split()[:2])
wc = len(q.split())
writer.writerow([q, intent, topic, wc])
print(f"{len(unique)} sorgu {filename} dosyasına kaydedildi")
# Intent dağılımı
intent_counts = defaultdict(int)
for _, intent in classified:
intent_counts[intent] += 1
print("\nIntent dağılımı:")
for intent, count in sorted(intent_counts.items(), key=lambda x: -x[1]):
print(f" {intent}: {count} ({100*count/len(unique):.1f}%)")
# Örnek kullanım
if __name__ == "__main__":
all_queries = set()
# Autocomplete, PAA ve Related results'ları birleştir
# all_queries.update(autocomplete_results)
# all_queries.update(paa_results)
# all_queries.update(related_results)
# export_to_csv(all_queries)
Bu çıktıyı bir web scraping pipeline'ına bağlayarak haftalık otomatik keyword raporları üretebilirsiniz. ProxyHat fiyatlandırması sayfasından uygun proxy paketini seçebilirsiniz.
Yaygın Hatalar ve Edge Case'ler
- Locale mismatch:
hlveglparametreleri proxy lokasyonuyla uyuşmazsa Google karışık öneriler döndürür. Almanya proxy'si kullanıyorsanızhl=devegl=dekullanın. - Sticky session kaybı: Autocomplete genişletmede her istekte yeni IP kullanmak, Google'ın anlık rate limit'lerini tetikler.
session-abc123bayrağı ile aynı IP'de kalın. - PAA selector değişimi: Google sık sık HTML yapısını değiştirir. CSS selector'ları her hafta test edin; kırılma riskini azaltmak için
data-attributetabanlı selector'ları tercih edin. - CAPTTCHA tetikleme: Saniyede 10+ istek göndermek CAPTCHA'ya yol açar. Semaphore ile concurrency'yi 5'te tutun ve 500ms gecikme ekleyin.
- Encoding sorunları: Almanca, Türkçe ve diğer UTF-8 dillerde
qparametresiniurllib.parse.quote()ile encode edin.
Etik ve Yasal Konular
Bu veriler herkese açık öneri verileridir; ancak Google'ın Hizmet Şartları, otomatik erişimi kısıtlar. Ölçekli üretim kullanımda resmi API'leri tercih edin. Düşük hacimli araştırma için nazik throttle (saniyede <5 istek) ve robots.txt kontrolü yapın. GDPR kapsamında kişisel veri içermeyen sorgu önerileri genellikle anonimdir, ancak kullanıcı gizliliğini her zaman ön planda tutun.
Key Takeaways
- Üç kaynak, üç intent: Autocomplete = informational, PAA = comparison/deep-dive, Related Searches = transactional.
- a-z genişletme: Tek seed kelimeden 300+ öneri üretmek için harf bazında genişletme ve modifier prefix'leri kullanın.
- Residential proxy zorunlu: Locale bias ve rate limit'ler için
gate.proxyhat.com:8080üzerinden ülke/şehir geo-targeting kullanın.- Recursive PAA: Playwright ile akordeon tıklaması yaparak iç içe soruları çıkarın; tek sorgudan 20+ soru mümkün.
- Etik throttle: Concurrency 5, delay 500ms; ölçekli iş için resmi API'leri tercih edin.






