Python ile residential proxy kullanarak Google sıralama takipçisi oluşturun — bu rehber, Google'ın Eylül 2025'te num=100 parametresini kaldırmasından ve TLS/JA3-JA4 parmak izi doğrulamasını sıkılaştırmasından sonra, SERP'leri güvenilir şekilde çekmek, organik pozisyonları ayrıştırmak ve sıralama geçmişini SQLite'ta saklamak için gereken tüm teknik adımları kod örnekleriyle açıklar.
Neden Python ile Residential Proxy Kullanarak Google Sıralama Takipçisi Oluşturun?
Sıralama takibi, SEO mühendislerinin en temel tekrarlayan işlerinden biridir. Tek seferlik bir kontrol, bir anahtar kelimenin belirli bir andaki durumunu gösterir; ancak Google sonuçları coğrafi konuma, cihaza, oturum geçmişine ve hatta günün saatine göre değişir. Günlük SERP anlık görüntüleri almak, sıralama oynaklığını, algoritma güncellemelerinin etkisini ve içerik optimizasyonlarının sonuçlarını zaman içinde izlemenizi sağlar.
Google, Eylül 2025'te num=100 parametresini tamamen kaldırdı. Artık her sayfada en fazla 10 organik sonuç görüntüleniyor ve ilk 100 sonucu görmek için start=0,10,20... ile sayfalama yapmak gerekiyor. Aynı dönemde TLS ClientHello paketinin JA3 ve JA4 hash'lerini kontrol eden anti-bot katmanı sıkılaştırıldı; bu da standart requests veya urllib ile yapılan isteklerin büyük ölçüde bloklandığı anlamına geliyor. IETF TLS parmak izi taslağı ve Wikipedia TLS fingerprinting makalesi, bu tekniklerin arka planını detaylıca açıklar.
Bu rehberde şunları yapacağız:
- Sıralama takibi için bir veri modeli tasarlamak
- Sayfalı SERP çekmek ve organik sonuçları ayrıştırmak
- curl_cffi ile Chrome parmak izi taklit etmek ve ProxyHat residential proxy ile IP rotasyonu yapmak
- Sonuçları SQLite ve CSV'de saklamak
- Üretim için tekrar deneme, CAPTCHA tespiti ve eşzamanlılık sınırları eklemek
Veri Modeli: Anahtar Kelime, Hedef Alan, Ülke, Cihaz, Pozisyon, Yakalama Zamanı
Sıralama takibinin gücü tutarlılıkta yatar. Her ölçümün en az altı alanı olmalıdır:
| Alan | Tür | Açıklama |
|---|---|---|
keyword | str | Takip edilen arama sorgusu |
target_domain | str | İzlenen alan adı (ör. example.com) |
country | str | ISO 3166-1 alpha-2 ülke kodu |
device | str | desktop veya mobile |
position | int | Organik sonuç sırası (1–100) |
captured_at | datetime | SERP'nin çekildiği UTC zaman damgası |
Günlük anlık görüntüler, tek seferlik kontrollerden üç nedenden dolayı üstündür:
- Oynaklık tespiti: Haftalık ortalama pozisyon, günlük dalgalanmaları gizler. 30 günlük bir seri, algoritma güncellemelerinin etkisini ortaya çıkarır.
- Korelasyon analizi: Trafik düşüşlerini sıralama değişimleriyle eşleştirmek için zaman damgalı veri gerekir.
- A/B doğrulaması: İçerik değişikliğinden önce ve sonra alınan anlık görüntüler, nedensel ilişki kurmanın temelidir.
SQLite Şeması
import sqlite3
from datetime import datetime, timezone
DB_PATH = "rank_tracker.db"
def init_db(path: str = DB_PATH) -> None:
conn = sqlite3.connect(path)
conn.execute("""
CREATE TABLE IF NOT EXISTS rankings (
id INTEGER PRIMARY KEY AUTOINCREMENT,
keyword TEXT NOT NULL,
target_domain TEXT NOT NULL,
country TEXT NOT NULL,
device TEXT NOT NULL,
position INTEGER,
captured_at TEXT NOT NULL,
raw_html TEXT,
UNIQUE(keyword, target_domain, country, device, captured_at)
)
""")
conn.execute("""
CREATE INDEX IF NOT EXISTS idx_rank_lookup
ON rankings(keyword, target_domain, country, device, captured_at DESC)
""")
conn.commit()
conn.close()
init_db()
Bu şema, aynı anahtar kelime + alan + ülke + cihaz kombinasyonu için günlük tek satır sağlar; UNIQUE kısıtı tekrarlanan çekimleri önler ve captured_at DESC indeksi en son kaydı hızlıca bulmanızı sağlar.
SERP Sayfalama: num=100 Kaldırıldıktan Sonra start Parametresi
Google, Eylül 2025'ten itibaren num=100 parametresini yok sayıyor. İlk 100 organik sonucu almak için start=0,10,20,30,40,50,60,70,80,90 değerleriyle 10 sayfalık istek göndermeniz gerekir. Her sayfa yaklaşık 10 organik sonuç döndürür; ancak SERP özellikleri ( bilgi grafikleri, "İnsanlar da soruyor" kutuları, alışveriş reklamları) sayfa başına organik sonuç sayısını düşürebilir.
Sayfalama Mantığı
from urllib.parse import urlencode, urlparse, parse_qs
def build_google_url(keyword: str, country: str, device: str, start: int = 0) -> str:
base = "https://www.google.com/search"
params = {
"q": keyword,
"gl": country, # coğrafi konum
"hl": "en", # arayüz dili
"num": "10", # Google artık >10'u yok sayıyor
"start": str(start), # 0, 10, 20, 30...
"nfpr": "1", # yazım düzeltmelerini devre dışı bırak
}
if device == "mobile":
params["udm"] = "1" # mobil görünüm
return f"{base}?{urlencode(params)}"
# İlk 100 için sayfa başlangıç değerleri
PAGE_STARTS = list(range(0, 100, 10)) # [0, 10, 20, ..., 90]
nfpr=1 parametresi, Google'ın otomatik yazım düzeltmelerini engeller; böylece "sıralama takibi" yerine "sıralama takip" aratırsanız bile orijinal sorgu korunur. udm=1 ise mobil cihaz formatını zorunlu kılar.
Neden Residential Proxy ve Şehir Düzeyinde Coğrafi Hedefleme Zorunlu?
Google'ın anti-bot katmanı iki temel sinyale bakar:
- TLS parmak izi (JA3/JA4): Python'ın standart HTTP kütüphaneleri, Chrome veya Firefox'tan farklı bir ClientHello paketi gönderir. Bu fark, JA3 hash'inde görünür ve Google bunu bir bot sinyali olarak kullanır.
- IP itibar skoru: Veri merkezi IP blokları (ASN bazında), yüksek istek hacmi nedeniyle düşük itibar skoruna sahiptir. Residential IP'ler ise gerçek ISP atamaları olduğundan çok daha yüksek geçiş oranları sunar.
Bu nedenle, üretim düzeyinde bir sıralama takipçisi için residential proxy ve tercihen şehir düzeyinde coğrafi hedefleme kullanmak pratik bir zorunluluktur. ProxyHat, gate.proxyhat.com:8080 HTTP ağ geçidinde ve gate.proxyhat.com:1080 SOCKS5 ağ geçidinde çalışır; kullanıcı adında bayraklar ile ülke, şehir ve oturum belirleyebilirsiniz.
| Proxy Türü | IP Kaynağı | Geçiş Oranı (tahmini) | Coğrafi Hedefleme | Uygun Kullanım |
|---|---|---|---|---|
| Datacenter | ASN blokları | %40–60 | Ülke | Düşük riskli hedefler |
| Residential | Gerçek ISP atamaları | %90+ | Ülke + Şehir | SERP scraping |
| Mobile | 4G/5G operatör IP'leri | %95+ | Ülke | Mobil SERP |
Şehir düzeyinde hedefleme, yerel arama sonuçlarını doğru şekilde yakalamak için kritiktir. "pizza" sorgusu Chicago'da New York'tan farklı sonuçlar döndürür. ProxyHat kullanıcı adında -country-US-city-chicago bayrağı ile bu hedeflemeyi yapabilirsiniz. Her anahtar kelime için -session-{keyword_id} ile yapışkan oturum kullanmak, aynı IP'den art arda sayfalama yapmanızı sağlar — bu, Google'ın "aynı kullanıcı sayfalar arasında geziniyor" modeline uyması için önemlidir.
curl_cffi ile Chrome Parmak İzi Taklidi ve ProxyHat Entegrasyonu
curl_cffi, libcurl'ün TLS parmak izi taklit yeteneğini Python'a taşır. impersonate='chrome' parametresi, ClientHello paketini gerçek Chrome tarayıcısıyla birebir aynı şekilde gönderir. Bu, JA3/JA4 kontrolünü aşmanın en pratik yoludur.
Ham Proxy Kullanımı
from curl_cffi import requests as cffi_requests
from urllib.parse import quote
import logging
logger = logging.getLogger("rank_tracker")
def fetch_serp_raw(
keyword: str,
country: str = "US",
device: str = "desktop",
start: int = 0,
proxy_username: str = "user-country-US-city-chicago-session-abc123",
proxy_password: str = "pass",
) -> str:
"""Tek bir SERP sayfasını ham HTML olarak döndürür."""
url = build_google_url(keyword, country, device, start)
proxy_url = f"http://{proxy_username}:{proxy_password}@gate.proxyhat.com:8080"
try:
resp = cffi_requests.get(
url,
impersonate="chrome",
proxies={"http": proxy_url, "https": proxy_url},
timeout=20,
allow_redirects=True,
)
resp.raise_for_status()
return resp.text
except cffi_requests.errors.RequestsError as e:
logger.error("SERP çekme hatası start=%d: %s", start, e)
raise
ProxyHat SDK Tarafı (Sarmalayıcı)
from dataclasses import dataclass
from curl_cffi import requests as cffi_requests
import hashlib
import time
@dataclass
class ProxyHatConfig:
username: str = "user"
password: str = "pass"
host: str = "gate.proxyhat.com"
http_port: int = 8080
socks5_port: int = 1080
class ProxyHatClient:
def __init__(self, config: ProxyHatConfig):
self.cfg = config
def build_proxy_url(self, country: str, city: str | None, session_id: str) -> str:
flags = [f"country-{country}"]
if city:
flags.append(f"city-{city}")
flags.append(f"session-{session_id}")
uname = f"{self.cfg.username}-" + "-".join(flags)
return f"http://{uname}:{self.cfg.password}@{self.cfg.host}:{self.cfg.http_port}"
def fetch(self, url: str, country: str, city: str | None, session_id: str) -> str:
proxy_url = self.build_proxy_url(country, city, session_id)
resp = cffi_requests.get(
url,
impersonate="chrome",
proxies={"http": proxy_url, "https": proxy_url},
timeout=20,
)
resp.raise_for_status()
return resp.text
# Kullanım
cfg = ProxyHatConfig(username="user", password="pass")
client = ProxyHatClient(cfg)
session_id = hashlib.md5(b"pizza-chicago").hexdigest()[:12]
html = client.fetch(
build_google_url("pizza", "US", "desktop", 0),
country="US",
city="chicago",
session_id=session_id,
)
Organik Pozisyonları Ayrıştırma
Google'ın HTML yapısı sık değişir; bu nedenle CSS seçiciler ve regex kombinasyonu kullanmak dayanıklılığı artırır. Organik sonuçlar genellikle div.g veya div[data-sokoban-container] içinde bulunur ve her birinin başlık etiketi h3 içerir.
from bs4 import BeautifulSoup
import re
from urllib.parse import urlparse
def parse_organic_results(html: str, target_domain: str) -> int | None:
"""
Hedef alan adının ilk organik pozisyonunu döndürür.
Bulunamazsa None döner.
"""
soup = BeautifulSoup(html, "html.parser")
position = 0
# div.g seçicisi — klasik organik sonuç konteyneri
for div in soup.select("div.g"):
link = div.find("a", href=True)
if not link:
continue
href = link["href"]
# Reklam bağlantılarını atla
if "/aclk?" in href or "googleads" in href:
continue
parsed = urlparse(href)
host = parsed.netloc.lower()
if host.startswith("www."):
host = host[4:]
position += 1
if target_domain.lower() in host:
return position
# Yedek: regex ile cite etiketi
for cite in soup.find_all("cite"):
text = cite.get_text().strip()
if target_domain.lower() in text.lower():
# Pozisyonu yaklaşık olarak bul
return position or None
return None
İlk 100 Sonucu Çekme ve Saklama
import csv
from datetime import datetime, timezone
PAGE_STARTS = list(range(0, 100, 10))
def track_keyword(
keyword: str,
target_domain: str,
country: str = "US",
city: str | None = "chicago",
device: str = "desktop",
client: ProxyHatClient | None = None,
) -> dict:
if client is None:
client = ProxyHatClient(ProxyHatConfig())
session_id = hashlib.md5(f"{keyword}-{country}-{city}".encode()).hexdigest()[:12]
found_position = None
all_html = []
for start in PAGE_STARTS:
url = build_google_url(keyword, country, device, start)
html = client.fetch(url, country=country, city=city, session_id=session_id)
all_html.append(html)
pos = parse_organic_results(html, target_domain)
if pos is not None:
# Sayfa içi pozisyonu global pozisyona çevir
found_position = start + pos
break
# Sayfada hiç organik sonuç yoksa dur
if not BeautifulSoup(html, "html.parser").select("div.g"):
break
record = {
"keyword": keyword,
"target_domain": target_domain,
"country": country,
"device": device,
"position": found_position,
"captured_at": datetime.now(timezone.utc).isoformat(),
"raw_html": "\n".join(all_html)[:5000], # depolama için kısalt
}
save_to_sqlite(record)
append_to_csv(record, "rankings.csv")
return record
def save_to_sqlite(record: dict) -> None:
conn = sqlite3.connect(DB_PATH)
conn.execute("""
INSERT OR REPLACE INTO rankings
(keyword, target_domain, country, device, position, captured_at, raw_html)
VALUES (?, ?, ?, ?, ?, ?, ?)
""", (
record["keyword"],
record["target_domain"],
record["country"],
record["device"],
record["position"],
record["captured_at"],
record["raw_html"],
))
conn.commit()
conn.close()
def append_to_csv(record: dict, path: str) -> None:
with open(path, "a", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=list(record.keys()))
if f.tell() == 0:
writer.writeheader()
writer.writerow(record)
Üretim Dayanıklılığı: Tekrar Deneme, CAPTCHA Tespiti, Eşzamanlılık
Tek bir anahtar kelime için 10 sayfa çekmek, 10 HTTP isteği demektir. 100 anahtar kelime takip ediyorsanız, günde 1000 istek yaparsınız. Bu hacimde hatalar kaçınılmazdır; bu nedenle üretim kodu şu mekanizmaları içermelidir:
Üstel Geri Çekilme ile Tekrar Deneme
import time
import random
from curl_cffi import requests as cffi_requests
class RetryableFetcher:
def __init__(self, max_retries: int = 3, base_delay: float = 2.0):
self.max_retries = max_retries
self.base_delay = base_delay
def fetch_with_retry(self, url: str, proxy_url: str) -> str:
last_exc = None
for attempt in range(self.max_retries):
try:
resp = cffi_requests.get(
url,
impersonate="chrome",
proxies={"http": proxy_url, "https": proxy_url},
timeout=20,
)
if resp.status_code == 429:
raise RuntimeError("Rate limited (429)")
if self._is_captcha(resp.text):
raise RuntimeError("CAPTCHA detected")
resp.raise_for_status()
return resp.text
except Exception as e:
last_exc = e
delay = self.base_delay * (2 ** attempt) + random.uniform(0, 1)
logger.warning("Deneme %d başarısız: %s — %.1fs bekleniyor", attempt + 1, e, delay)
time.sleep(delay)
raise last_exc
@staticmethod
def _is_captcha(html: str) -> bool:
markers = [
"recaptcha",
"g-recaptcha",
"unusual traffic",
"our systems have detected",
"sorry/image",
]
html_lower = html.lower()
return any(m in html_lower for m in markers)
Eşzamanlılık Sınırları
100 anahtar kelimeyi ardışık çekmek saatler sürebilir. Ancak çok yüksek eşzamanlılık, proxy sağlayıcının hız limitlerini aşar ve geçiş oranını düşürür. Pratik bir denge, ülke başına 5–10 eşzamanlı oturumdur.
import asyncio
from asyncio import Semaphore
async def track_keyword_async(
keyword: str,
target_domain: str,
country: str,
semaphore: Semaphore,
client: ProxyHatClient,
) -> dict:
async with semaphore:
# curl_cffi'nin async API'si
loop = asyncio.get_event_loop()
return await loop.run_in_executor(
None,
lambda: track_keyword(keyword, target_domain, country, client=client),
)
async def track_batch(keywords: list[str], target_domain: str, country: str, max_concurrent: int = 5):
semaphore = Semaphore(max_concurrent)
client = ProxyHatClient(ProxyHatConfig())
tasks = [
track_keyword_async(kw, target_domain, country, semaphore, client)
for kw in keywords
]
results = await asyncio.gather(*tasks, return_exceptions=True)
return results
# Çalıştırma
# asyncio.run(track_batch(["pizza", "burger", "sushi"], "example.com", "US"))
Sıralama Oynaklığı Yumuşatma
Günlük tek ölçümler gürültülüdür. 7 günlük hareketli ortalama, gerçek trendleri ortaya çıkarır:
def moving_average(positions: list[int | None], window: int = 7) -> list[float | None]:
"""None değerleri atlayarak hareketli ortalama hesaplar."""
result = []
for i in range(len(positions)):
start = max(0, i - window + 1)
chunk = [p for p in positions[start:i + 1] if p is not None]
result.append(sum(chunk) / len(chunk) if chunk else None)
return result
Etik ve Yasal Sınırlar
SERP scraping, Google'ın Hizmet Şartları'yla çelişebilir. Kendi alan adınızın sıralamasını izlemek düşük riskli olsa da, yüksek hacimli otomatik scraping yasal risk taşır. Şu ilkeleri gözetin:
- Düşük hacim tercih edin: Günde 100 anahtar kelime, çoğu kullanım için yeterlidir. Daha fazlası için resmi bir SERP API'si (ör. Google Custom Search API veya ticari SERP API sağlayıcıları) kullanın.
- Hız sınırlarına saygı gösterin: Saniyede 1–2 istek, proxy sağlayıcının ve hedefin limitlerini aşmamak için güvenli bir bölgedir.
- robots.txt kontrolü: Google'ın robots.txt'si /search yolunu izliyor; bu, scraping'in teknik olarak kısıtlanmış olduğunu gösterir. Bu gerçeği bilerek hareket edin.
- GDPR/CCPA: SERP'ler kişisel veri içermez; ancak IP rotasyonu yaparken kullanıcı gizliliğini gözetin. Wikipedia robots.txt makalesi, standartın nasıl çalıştığını açıklar.
ProxyHat, web scraping kullanım senaryosunda ve SERP takibi sayfasında bu konuları detaylıca ele alır. Fiyatlandırma için ProxyHat fiyatlandırma sayfasını, mevcut konumlar için lokasyon listesini inceleyebilirsiniz. Teknik dokümantasyon için ProxyHat dokümantasyonuna başvurun.
Karşılaştırma: SERP API vs. Kendi Takipçiniz
| Özellik | Ticari SERP API | Kendi Takipçiniz (ProxyHat) |
|---|---|---|
| Maliyet (10k istek/ay) | $50–150 | $20–40 (proxy maliyeti) |
| Esneklik | Sınırlı (önceden tanımlı alanlar) | Tam (özel sorgular, özel ayrıştırma) |
| Gecikme | 200–800ms (API ek yükü) | 100–400ms (doğrudan) |
| Veri saklama | Sağlayıcıya bağlı | Tam kontrol (SQLite, CSV, S3) |
| Şehir düzeyi coğrafi hedef | Genelde sınırlı | Evet (ProxyHat -city bayrağı) |
Önemli Çıkarımlar
Günlük SERP anlık görüntüleri, tek seferlik kontrollere göre üç nedenden üstündür: oynaklık tespiti, korelasyon analizi ve A/B doğrulaması. Tutarlı zaman damgalı veri, SEO kararlarının temelidir.
num=100artık çalışmıyor;start=0,10,20...ile sayfalama yapın ve her sayfada organik sonuç sayısını kontrol edin.- TLS parmak izi (JA3/JA4) ve IP itibar skoru, datacenter proxy'leri SERP scraping için pratik olmaktan çıkarır; residential proxy + şehir hedefleme kullanın.
curl_cffiileimpersonate='chrome', TLS kontrolünü aşmanın en güvenilir yoludur.- ProxyHat
-session-{id}bayrağı ile her anahtar kelime için yapışkan oturum kullanın; bu, sayfalar arası tutarlılık sağlar. - Üstel geri çekilme, CAPTCHA tespiti ve eşzamanlılık sınırları, üretimde %90+ başarı oranının anahtarıdır.
- Etik sınırlara uyun: düşük hacim, hız kontrolü ve gerektiğinde resmi SERP API'si tercih edin.
SSS
Python ile residential proxy kullanarak Google sıralama takipçisi oluşturmak nedir?
Python ile residential proxy kullanarak Google sıralama takipçisi oluşturmak, Google arama sonuç sayfalarını (SERP) otomatik olarak çekip hedef alan adınızın organik pozisyonunu kaydeden bir sistem kurmaktır. Bu sistem, anahtar kelime, ülke, cihaz ve zaman damgası ile pozisyon verisini SQLite veya CSV'de saklar. Residential proxy, Google'ın TLS parmak izi ve IP itibar kontrollerini aşmak için gerçek ISP IP'leri kullanır.
Bu neden proxy kullanıcıları için önemlidir?
Google, Eylül 2025'ten itibaren num=100 parametresini kaldırdı ve TLS/JA3-JA4 parmak izi doğrulamasını sıkılaştırdı. Bu, standart Python HTTP kütüphaneleri ve datacenter proxy'leri ile SERP çekmeyi neredeyse imkansız kıldı. Residential proxy'ler, gerçek ISP atamaları olduğu için %90+ geçiş oranı sunar ve şehir düzeyinde coğrafi hedefleme ile yerel arama sonuçlarını doğru şekilde yakalamanızı sağlar.
Hangi proxy türü bu iş için en iyisidir?
SERP scraping için residential proxy en iyi seçimdir. Datacenter proxy'ler düşük IP itibarı nedeniyle %40–60 geçiş oranıyla sınırlıyken, residential proxy'ler %90+ geçiş oranı sunar. Mobil proxy'ler (%95+) daha yüksek geçiş oranı sağlasa da şehir düzeyinde coğrafi hedefleme sunmaz; bu da yerel arama sonuçlarını takip etmek için residential'ı daha uygun kılar.
Blokları nasıl önlersiniz?
Blokları önlemek için dört strateji uygulayın: (1) curl_cffi ile impersonate='chrome' kullanarak TLS parmak izini gerçek tarayıcıyla eşleştirin; (2) her anahtar kelime için yapışkan oturum (-session-{id}) kullanın, böylece sayfalar arası aynı IP'den istek yapın; (3) saniyede 1–2 istekle hız sınırlaması uygulayın; (4) üstel geri çekilme ile tekrar deneyin ve CAPTCHA tespiti yapın. Ayrıca ülke başına 5–10 eşzamanlı oturum sınırı, proxy sağlayıcının limitlerini aşmamak için önemlidir.






