X (eski adıyla Twitter), 2023 yılında ücretsiz API erişimini neredeyse tamamen kaldırdığında, sosyal medya izleme ve sentiment analizi yapan binlerce ekip ani bir duvara çarptı. Eskiden basit bir API anahtarıyla erişilebilen veriler artık aylık yüzlerce dolarlık ücretler gerektiriyor veya hiç erişilemez durumda. Bu nedenle, birçok geliştirici kamu verisine erişmek için web kazımaya yöneliyor.
Bu rehber, Twitter residential proxy kullanarak X'ten kamu verisini nasıl güvenli ve ölçeklenebilir şekilde çekebileceğinizi anlatıyor. Yasal çerçeveyi, teknik uygulamayı ve etik sınırları detaylıca ele alacağız.
Önemli Uyarı: Bu rehber yalnızca kamuya açık verilere erişim için eğitim amaçlıdır. X'in Hizmet Koşulları (Terms of Service) web kazımayı yasaklamaktadır. Herhangi bir kazıma faaliyeti başlatmadan önce hukuki danışmanlık almanız şarttır. ABD'de CFAA (Computer Fraud and Abuse Act), AB'de GDPR ve ilgili ülkelerin yasaları geçerlidir.
X API Kısıtlamaları Sonrası Manzara
2023 öncesinde, Twitter API'si üç katmanlıydı: ücretsiz, düşük maliyetli enterprise ve premium. Geliştiriciler ücretsiz katmanla günde binlerce tweet çekebilir, arama yapabilir ve trendleri takip edebilirdi. X'in Elon Musk tarafından satın alınmasından sonra bu değişti.
Mevcut X API Katmanları
| Katman | Aylık Ücret | Tweet Çekme Limiti | Arama Erişimi |
|---|---|---|---|
| Free | $0 | Yazma only (çekme yok) | Yok |
| Basic | $100 | 10,000 tweet/ay | Sınırlı |
| Pro | $5,000 | 1M tweet/ay | Tam |
| Enterprise | Özel fiyatlandırma | Özel | Tam |
Bu fiyatlandırma, küçük startup'lar ve bağımsız geliştiriciler için API'yi erişilemez kıldı. Sonuç olarak, X/Twitter scraping faaliyetleri arttı — ancak bu da X'in anti-bot önlemlerini güçlendirmesine neden oldu.
X'in Anti-Kazıma Önlemleri
- Rate limiting: Giriş yapmamış oturumlar için dakikada ~50-100 request
- Datacenter IP engellemeleri: AWS, GCP, Azure ve bilinen VPN/Proxy sağlayıcı IP aralıkları otomatik flagleniyor
- Browser fingerprinting: Canvas, WebGL, audio context parmak izi kontrolü
- JavaScript challenge: Bot tespiti için client-side challenge'lar
- GraphQL payload encryption: İçeriğe erişim için geçerli query hash'leri gerekiyor
Kamu Web Üzerinden Erişilebilen Veriler
X'in web arayüzü, giriş yapmadan bile önemli miktarda kamu verisi sunuyor. Ancak bu veriye erişmek, API'den çok daha karmaşık.
Giriş Olmadan Erişilebilen Veriler
- Profil sayfaları: Kullanıcı adı, biyografi, takipçi/takip edilen sayıları, profil ve banner görselleri
- Kamu tweet'leri: Korunmasız hesapların tüm tweet'leri, medya, beğeni/retweet/yorum sayıları
- Tweet thread'leri: Bir tweet'in tüm yanıtları ve devam eden thread'ler
- Arama sonuçları: Hashtag ve anahtar kelime aramaları (sınırlı sayıda sonuç)
- Trending topics: Ülke/bölge bazlı trendler
- Lists: Kamu listeler ve üyeleri
Giriş Gerektiren Veriler
- Korumalı hesaplar: Tweet'ler ve profil detayları
- Gelişmiş arama: Tarih aralığı, hesap türü filtreleri
- Community notes: Bağlam notları (kısmen kamu)
- Analytics: Görüntülenme, etkileşim detayları
- DM'ler ve mention'lar: Kişisel mesajlar ve bildirimler
Kritik nokta: Giriş gerektiren verileri kazımak, hesap güvenliğinizi riske atar ve X ToS'u daha ağır ihlal eder. Bu rehber yalnızca giriş yapmadan erişilebilen kamu verisine odaklanıyor.
Neden Residential Proxy Gerekli?
Scrape Twitter with proxies araması yapan herkesin ilk karşılaştığı sorun, X'in datacenter IP'lerini agresif şekilde engellemesidir. Bir AWS veya DigitalOcean sunucusundan X'e istek atarsanız, muhtemelen 429 (Too Many Requests) veya 403 (Forbidden) alırsınız.
Datacenter vs Residential Proxy Karşılaştırması
| Özellik | Datacenter Proxy | Residential Proxy |
|---|---|---|
| IP Kaynağı | Veri merkezi blokları | Gerçek ISP aboneleri |
| X Algılama Riski | Çok yüksek | Düşük-orta |
| Hız | Çok hızlı | Orta |
| Maliyet | Düşük ($1-3/GB) | Orta-yüksek ($5-15/GB) |
| Uzun Ömürlülük | IP'ler hızlı banlanır | IP havuzu rotate edilebilir |
| Coğrafi Hedefleme | Sınırlı | Ülke/sehir seviyesi |
X'in IP Sınıflandırması
X, IP'leri üç kategoriye ayırıyor:
- Güvenilir (Residential ISP): Türk Telekom, Deutsche Telekom, Comcast gibi gerçek ISP'lerden gelen IP'ler. Normal rate limit uygulanır.
- Şüpheli (Hosting/VPN): AWS, Google Cloud, bilinen VPN sağlayıcılar. Sıkı rate limit, sık sık captcha.
- Engellenmiş (Known bot networks): Daha önce tespit edilmiş bot ağları. Anında 403.
Bu nedenle, Twitter residential proxies kullanmak, başarı oranını dramatik şekilde artırır. Her istekte farklı bir residential IP kullanarak, X'in IP bazlı throttling'ini aşabilirsiniz.
Python + Playwright ile X Kazıma Uygulaması
Şimdi, residential proxy havuzu kullanarak X'ten kamu verisi çeken çalışan bir Python örneği oluşturalım. Bu örnek Playwright kullanıyor çünkü X'in JavaScript-rendered SPA'sı için gerçek bir tarayıcı gerekiyor.
Kurulum
pip install playwright asyncio
playwright install chromium
Temel Profil Kazıma Kodu
import asyncio
from playwright.async_api import async_playwright
import json
import re
PROXY_HOST = "gate.proxyhat.com"
PROXY_PORT = 8080
PROXY_USER = "your-username-country-US"
PROXY_PASS = "your-password"
def get_proxy_url(session_id=None):
"""Her istek için farklı bir residential IP."""
user = f"{PROXY_USER}-session-{session_id}" if session_id else PROXY_USER
return f"http://{user}:{PROXY_PASS}@{PROXY_HOST}:{PROXY_PORT}"
async def scrape_profile(username: str, session_id: str = None):
"""Bir X profil sayfasını kazır."""
proxy_url = get_proxy_url(session_id)
async with async_playwright() as p:
browser = await p.chromium.launch(
proxy={"server": proxy_url},
headless=True
)
context = await browser.new_context(
user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
viewport={"width": 1920, "height": 1080}
)
page = await context.new_page()
try:
# Profil sayfasına git
await page.goto(f"https://x.com/{username}", wait_until="networkidle")
# X sayfadaki JSON veriyi <script> tag'i içinde embed eder
# Bu veriyi extract edelim
content = await page.content()
# GraphQL response'u bul
pattern = r'<script id="__NEXT_DATA__" type="application/json">([^<]+)</script>'
match = re.search(pattern, content)
if match:
data = json.loads(match.group(1))
return data
# Alternatif: API intercept
# X GraphQL endpoint'lerini dinleyebiliriz
return None
except Exception as e:
print(f"Hata: {e}")
return None
finally:
await browser.close()
# Kullanım
async def main():
result = await scrape_profile("elonmusk", session_id="abc123")
if result:
print(json.dumps(result, indent=2)[:500])
asyncio.run(main())
GraphQL Intercept ile Tweet Çekme
X'in web arayüzü GraphQL API kullanıyor. Bu istekleri intercept ederek ham veriye ulaşabiliriz:
import asyncio
from playwright.async_api import async_playwright
import json
PROXY_HOST = "gate.proxyhat.com"
PROXY_PORT = 8080
PROXY_USER = "your-username-country-US"
PROXY_PASS = "your-password"
async def scrape_tweets_with_intercept(username: str, max_tweets: int = 20):
"""GraphQL isteklerini intercept ederek tweet'leri çeker."""
proxy_url = f"http://{PROXY_USER}:{PROXY_PASS}@{PROXY_HOST}:{PROXY_PORT}"
tweets = []
async with async_playwright() as p:
browser = await p.chromium.launch(
proxy={"server": proxy_url},
headless=True
)
context = await browser.new_context()
page = await context.new_page()
# GraphQL isteklerini dinle
async def handle_response(response):
if "graphql" in response.url and "UserTweets" in response.url:
try:
data = await response.json()
if "data" in data:
entries = data.get("data", {}).get("user", {}).get("result", {}).get("timeline_v2", {}).get("timeline", {}).get("instructions", [{}])
for entry in entries:
if entry.get("type") == "TimelineAddEntries":
for item in entry.get("entries", []):
tweet_data = item.get("content", {}).get("itemContent", {}).get("tweet_results", {}).get("result", {})
if tweet_data:
tweets.append({
"id": tweet_data.get("rest_id"),
"text": tweet_data.get("legacy", {}).get("full_text"),
"created_at": tweet_data.get("legacy", {}).get("created_at"),
"likes": tweet_data.get("legacy", {}).get("favorite_count"),
"retweets": tweet_data.get("legacy", {}).get("retweet_count")
})
except:
pass
page.on("response", handle_response)
try:
await page.goto(f"https://x.com/{username}", wait_until="networkidle")
# Scroll to load more tweets
for _ in range(3):
await page.evaluate("window.scrollTo(0, document.body.scrollHeight)")
await asyncio.sleep(2)
return tweets[:max_tweets]
except Exception as e:
print(f"Hata: {e}")
return tweets
finally:
await browser.close()
# Kullanım
async def main():
tweets = await scrape_tweets_with_intercept("nasa")
print(f"Çekilen tweet sayısı: {len(tweets)}")
for tweet in tweets[:5]:
print(f"- {tweet['text'][:100]}...")
asyncio.run(main())
Node.js ile X Arama Kazıma
JavaScript/TypeScript ekosisteminde çalışanlar için Playwright ile Node.js örneği:
const { chromium } = require('playwright');
const PROXY_HOST = 'gate.proxyhat.com';
const PROXY_PORT = 8080;
const PROXY_USER = 'your-username-country-US';
const PROXY_PASS = 'your-password';
async function searchTweets(query, maxResults = 50) {
const proxyUrl = `http://${PROXY_USER}:${PROXY_PASS}@${PROXY_HOST}:${PROXY_PORT}`;
const browser = await chromium.launch({
proxy: { server: proxyUrl },
headless: true
});
const context = await browser.newContext({
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
});
const page = await context.newPage();
const tweets = [];
// GraphQL responses'ları intercept et
page.on('response', async (response) => {
if (response.url().includes('graphql') && response.url().includes('SearchTimeline')) {
try {
const data = await response.json();
// Tweet'leri extract et
const instructions = data?.data?.search_by_raw_query?.search_timeline?.timeline?.instructions || [];
for (const instruction of instructions) {
if (instruction.type === 'TimelineAddEntries') {
for (const entry of instruction.entries || []) {
const tweetResult = entry?.content?.itemContent?.tweet_results?.result;
if (tweetResult) {
tweets.push({
id: tweetResult.rest_id,
text: tweetResult.legacy?.full_text,
user: tweetResult.legacy?.user_screen_name,
created_at: tweetResult.legacy?.created_at
});
}
}
}
}
} catch (e) {}
}
});
try {
const searchUrl = `https://x.com/search?q=${encodeURIComponent(query)}&src=typed_query`;
await page.goto(searchUrl, { waitUntil: 'networkidle' });
// Daha fazla sonuç için scroll
for (let i = 0; i < 5; i++) {
await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight));
await page.waitForTimeout(2000);
}
return tweets.slice(0, maxResults);
} catch (error) {
console.error('Hata:', error);
return tweets;
} finally {
await browser.close();
}
}
// Kullanım
(async () => {
const results = await searchTweets('#AI startups');
console.log(`Bulunan tweet: ${results.length}`);
results.slice(0, 3).forEach(t => console.log(`- @${t.user}: ${t.text?.slice(0, 80)}...`));
})();
Rate Limit Yönetimi
X, giriş yapmamış kullanıcılar için agresif rate limiting uygular. Bu sınırları aşmak için akıllı stratejiler gerekiyor.
X Rate Limit Türleri
| Limit Türü | Etki | Aşma Stratejisi |
|---|---|---|
| IP-level | Dakikada ~50-100 request | Residential proxy rotation |
| Session-level | Sliding window algılama | Yeni session başlatma |
| Account-level | Hesap askıya alma | Giriş yapmadan kazıma |
| Behavioral | Bot pattern tespiti | Human-like delays |
429 Hatası Yönetimi
import asyncio
import random
from playwright.async_api import async_playwright
PROXY_HOST = "gate.proxyhat.com"
PROXY_PORT = 8080
PROXY_USER = "your-username"
PROXY_PASS = "your-password"
async def scrape_with_retry(url, max_retries=3):
"""429 hatalarını yöneten retry mantığı."""
for attempt in range(max_retries):
# Her denemede yeni session ve IP
session_id = f"retry-{attempt}-{random.randint(1000, 9999)}"
proxy_url = f"http://{PROXY_USER}-session-{session_id}:{PROXY_PASS}@{PROXY_HOST}:{PROXY_PORT}"
async with async_playwright() as p:
browser = await p.chromium.launch(
proxy={"server": proxy_url},
headless=True
)
page = await browser.new_page()
try:
response = await page.goto(url, wait_until="networkidle")
if response.status == 429:
print(f"Rate limited (attempt {attempt + 1}), yeni IP deneniyor...")
await asyncio.sleep(random.uniform(5, 15))
continue
if response.status == 200:
content = await page.content()
return {"success": True, "content": content}
print(f"HTTP {response.status}, retry...")
except Exception as e:
print(f"Hata: {e}")
finally:
await browser.close()
return {"success": False, "error": "Max retries exceeded"}
# ProxyHat ile sticky session kullanarak
# uzun ömürlü IP'ler elde edebilirsiniz
async def scrape_with_sticky_session(urls):
"""Aynı IP ile birden fazla sayfa (sticky session)."""
session_id = f"sticky-{random.randint(10000, 99999)}"
proxy_url = f"http://{PROXY_USER}-session-{session_id}:{PROXY_PASS}@{PROXY_HOST}:{PROXY_PORT}"
async with async_playwright() as p:
browser = await p.chromium.launch(
proxy={"server": proxy_url},
headless=True
)
context = await browser.new_context()
results = []
for url in urls:
page = await context.new_page()
try:
await page.goto(url)
await asyncio.sleep(random.uniform(2, 5)) # Human-like delay
content = await page.content()
results.append(content)
finally:
await page.close()
await browser.close()
return results
Sliding Window Tespiti
X, basit rate limit'in ötesinde sliding window pattern tespiti yapar. Kısa sürede çok istek atarsanız, IP'niz geçici olarak flaglenir. Bu nedenle:
- Random delays: İstekler arasında 2-10 saniye rastgele bekleme
- Burst avoidance: Ani istek patlamaları yerine sürekli, yavaş akış
- Session rotation: Her 50-100 istekte yeni session/IP
- Off-peak scraping: Yoğun olmayan saatlerde kazıma
Yasal Çerçeve ve Etik Kazıma
X/Twitter scraping konusu yasal olarak karmaşıktır. Son yıllarda görülen davalar ve mevcut durum şöyledir:
Önemli Davalar
- hiQ Labs v. LinkedIn (2022): 9. Circuit, kamuya açık verilerin kazınmasının CFAA kapsamına girmediğine hükmetti. Bu, kamu verisi kazıma lehine bir emsal.
- Meta v. Bright Data (2023): Meta, Bright Data'nın Facebook/Instagram kazımasına karşı dava açtı. Anlaşma ile sonuçlandı.
- X v. Various scrapers: X, 2023'ten beri aktif olarak kazıma yapan şirketlere DMCA ve ToS ihlali bildirimleri gönderiyor.
X Hizmet Koşulları
X'in ToS'u açıkça şunları yasaklıyor:
- "Crawling, scraping veya veri madenciliği"
- "API olmayan yöntemlerle veri toplama"
- "Otomatik sistemler kullanarak Platform'a erişim"
Bununla birlikte, ABD'de ToS ihlali tek başına cezai bir suç değil — ancak X hesabınızı yasaklayabilir, IP'nizi engelleyebilir ve sivil dava açabilir.
GDPR ve AB'de Kazıma
AB'de durum daha karmaşık:
- Kişisel veri (tweet'ler, profil bilgileri) GDPR kapsamında
- Kamu verisi olsa bile, işleme amacınız meşru olmalı
- Araştırma gazetecilik amaçlı kazıma bazı AB ülkelerinde koruma altında
Kazıma vs API: Ne Zaman Hangisi?
| Kriter | API Kullanın | Kazıma Düşünün |
|---|---|---|
| Bütçe | $5,000+/ay varsa | Bütçe kısıtlıysa |
| Veri hacmi | 1M+ tweet/ay | Düşük hacim |
| Güncellik | Gerçek zamanlı gerekli | Günlük/haftalık yeterli |
| Yasal risk | Minimal | Orta-yüksek |
| Teknik kaynak | Basit REST entegrasyonu | Playwright/maintenance gerekli |
| Veri türü | Tüm API verisi | Sadece kamu verisi |
En İyi Uygulamalar ve Öneriler
Teknik Öneriler
- Residential proxy kullanın: Datacenter IP'ler X'te anında engellenir. ProxyHat gibi kaliteli residential proxy sağlayıcıları kullanın.
- Gerçek tarayıcı kullanın: Requests/BeautifulSoup yeterli değil. Playwright veya Puppeteer şart.
- Rate limit saygılı olun: Dakikada 30-50 istekten fazlası risklidir.
- User-agent rotasyonu: Farklı tarayıcı parmak izleri kullanın.
- Hata yönetimi: 429, 403, captcha durumlarında graceful degradation.
Etik Öneriler
- robots.txt kontrolü: X robots.txt ile kazıma izni vermiyor — bunu bilerek hareket edin.
- Sadece kamu verisi: Korumalı hesapların verisine erişmeyin.
- Veri minimizasyonu: İhtiyacınız olandan fazla veri toplamayın.
- Saklama süresi: Veriyi gerekli süreden fazla tutmayın.
- Araştırma amacı: Akademik veya gazetecilik amaçlar daha iyi yasal koruma sağlayabilir.
Key Takeaways
Önemli Çıkarımlar:
- X'in ücretsiz API'si kaldırıldı; Basic katman $100/ay ile başlıyor ve çok sınırlı.
- Datacenter IP'ler X'te neredeyse her zaman engellenir — residential proxy şart.
- Playwright ile GraphQL intercept, en güvenilir kazıma yöntemi.
- 429 hataları için retry mantığı ve IP rotation mutlaka uygulayın.
- X ToS kazımayı yasaklıyor — yasal riskleri değerlendirin, gerekiyorsa API kullanın.
- GDPR kapsamında kişisel veri işleme kurallarına uyun.
Sonraki Adımlar
Eğer sosyal medya izleme dashboard'u veya sentiment analiz sistemi inşa ediyorsanız, doğru proxy altyapısı kritiktir. ProxyHat, X kazıma için optimize edilmiş residential proxy planları sunuyor. Ülke bazlı hedefleme ve sticky session desteği ile X'in anti-bot önlemlerini aşmanız kolaylaşır.
Daha fazla kaynak için:
Unutmayın: Kazıma, API erişimin olmadığı durumlarda bir çözüm olabilir, ancak her zaman yasal ve etik sınırlar içinde kalmalı.






