yt bot protection
This commit is contained in:
@@ -5,35 +5,126 @@ from youtube_transcript_api import YouTubeTranscriptApi
|
||||
from typing import List, Dict, Optional
|
||||
import time
|
||||
import logging
|
||||
import random
|
||||
import os
|
||||
|
||||
# Logger oluştur
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Gerçek tarayıcı User-Agent'ları (rotasyon için)
|
||||
USER_AGENTS = [
|
||||
# Chrome (Windows)
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
||||
# Chrome (macOS)
|
||||
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
||||
# Chrome (Linux)
|
||||
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
||||
# Firefox (Windows)
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0',
|
||||
# Firefox (macOS)
|
||||
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:121.0) Gecko/20100101 Firefox/121.0',
|
||||
# Safari (macOS)
|
||||
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Safari/605.1.15',
|
||||
# Edge (Windows)
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0',
|
||||
]
|
||||
|
||||
|
||||
class TranscriptExtractor:
|
||||
"""YouTube transcript çıkarıcı sınıfı"""
|
||||
|
||||
def __init__(self, rate_limit: int = 2, time_window: int = 30):
|
||||
def __init__(self, rate_limit: int = 1, time_window: int = 60):
|
||||
"""
|
||||
Args:
|
||||
rate_limit: Zaman penceresi başına maksimum istek sayısı (YouTube IP blocking'i önlemek için düşük)
|
||||
time_window: Zaman penceresi (saniye)
|
||||
rate_limit: Zaman penceresi başına maksimum istek sayısı (YouTube IP blocking'i önlemek için çok düşük)
|
||||
time_window: Zaman penceresi (saniye) - daha uzun süre
|
||||
"""
|
||||
self.rate_limit = rate_limit
|
||||
self.rate_limit = rate_limit # 1 istek/60 saniye (çok konservatif)
|
||||
self.time_window = time_window
|
||||
self.request_times = []
|
||||
self.last_blocked_time = 0
|
||||
self.block_count = 0 # Toplam blocking sayısı
|
||||
|
||||
# Gerçek tarayıcı header'larını ayarla
|
||||
self._setup_browser_headers()
|
||||
|
||||
def _setup_browser_headers(self):
|
||||
"""Gerçek tarayıcı gibi HTTP header'larını ayarla"""
|
||||
try:
|
||||
import requests
|
||||
|
||||
# requests.Session'ın get/post metodlarını patch et
|
||||
# Bu, youtube-transcript-api'nin yaptığı tüm isteklere header ekler
|
||||
original_get = requests.Session.get
|
||||
original_post = requests.Session.post
|
||||
|
||||
def patched_get(self, url, **kwargs):
|
||||
"""requests.Session.get'i patch et - header'ları ekle"""
|
||||
headers = kwargs.get('headers', {})
|
||||
browser_headers = TranscriptExtractor._get_browser_headers()
|
||||
# Mevcut header'ları koru, browser header'larını ekle
|
||||
merged_headers = {**browser_headers, **headers}
|
||||
kwargs['headers'] = merged_headers
|
||||
return original_get(self, url, **kwargs)
|
||||
|
||||
def patched_post(self, url, **kwargs):
|
||||
"""requests.Session.post'i patch et - header'ları ekle"""
|
||||
headers = kwargs.get('headers', {})
|
||||
browser_headers = TranscriptExtractor._get_browser_headers()
|
||||
# Mevcut header'ları koru, browser header'larını ekle
|
||||
merged_headers = {**browser_headers, **headers}
|
||||
kwargs['headers'] = merged_headers
|
||||
return original_post(self, url, **kwargs)
|
||||
|
||||
# Patch'i uygula (sadece bir kez)
|
||||
if not hasattr(requests.Session, '_browser_headers_patched'):
|
||||
requests.Session.get = patched_get
|
||||
requests.Session.post = patched_post
|
||||
requests.Session._browser_headers_patched = True
|
||||
|
||||
logger.info("[HEADERS] ✅ requests.Session patch edildi - Gerçek tarayıcı header'ları eklendi")
|
||||
except Exception as e:
|
||||
logger.warning(f"[HEADERS] ⚠️ Header patch edilemedi: {e}")
|
||||
|
||||
@staticmethod
|
||||
def _get_browser_headers() -> Dict[str, str]:
|
||||
"""Gerçek tarayıcı header'larını döndür"""
|
||||
user_agent = random.choice(USER_AGENTS)
|
||||
|
||||
headers = {
|
||||
'User-Agent': user_agent,
|
||||
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8',
|
||||
'Accept-Language': 'en-US,en;q=0.9,tr;q=0.8',
|
||||
'Accept-Encoding': 'gzip, deflate, br',
|
||||
'DNT': '1',
|
||||
'Connection': 'keep-alive',
|
||||
'Upgrade-Insecure-Requests': '1',
|
||||
'Sec-Fetch-Dest': 'document',
|
||||
'Sec-Fetch-Mode': 'navigate',
|
||||
'Sec-Fetch-Site': 'none',
|
||||
'Sec-Fetch-User': '?1',
|
||||
'Cache-Control': 'max-age=0',
|
||||
'Referer': 'https://www.youtube.com/',
|
||||
}
|
||||
|
||||
logger.debug(f"[HEADERS] Header'lar oluşturuldu - User-Agent: {user_agent[:50]}...")
|
||||
return headers
|
||||
|
||||
def _check_rate_limit(self):
|
||||
"""Rate limiting kontrolü (YouTube IP blocking'i önlemek için)"""
|
||||
now = time.time()
|
||||
|
||||
# Eğer son 5 dakikada IP blocking hatası aldıysak, daha uzun bekle
|
||||
if self.last_blocked_time > 0 and (now - self.last_blocked_time) < 300:
|
||||
wait_time = 60 # 1 dakika bekle
|
||||
logger.warning(f"[RATE_LIMIT] IP blocking sonrası bekleme: {wait_time} saniye (Son blocking: {int(now - self.last_blocked_time)} saniye önce)")
|
||||
# Eğer son 10 dakikada IP blocking hatası aldıysak, çok daha uzun bekle
|
||||
if self.last_blocked_time > 0 and (now - self.last_blocked_time) < 600:
|
||||
# Blocking sayısına göre bekleme süresini artır
|
||||
base_wait = 300 # 5 dakika base
|
||||
wait_time = base_wait + (self.block_count * 60) # Her blocking'de +1 dakika
|
||||
wait_time = min(wait_time, 1800) # Maksimum 30 dakika
|
||||
|
||||
logger.warning(f"[RATE_LIMIT] IP blocking sonrası bekleme: {wait_time} saniye ({wait_time/60:.1f} dakika) - Blocking sayısı: {self.block_count}")
|
||||
logger.warning(f"[RATE_LIMIT] Son blocking: {int(now - self.last_blocked_time)} saniye önce")
|
||||
time.sleep(wait_time)
|
||||
self.last_blocked_time = 0 # Reset
|
||||
# Reset etme, 10 dakika boyunca hatırla
|
||||
|
||||
# Son time_window saniyesindeki istekleri filtrele
|
||||
self.request_times = [t for t in self.request_times if now - t < self.time_window]
|
||||
@@ -49,12 +140,18 @@ class TranscriptExtractor:
|
||||
self.request_times = [t for t in self.request_times if now - t < self.time_window]
|
||||
|
||||
# İstekler arasında minimum bekleme (YouTube blocking'i önlemek için)
|
||||
# Human-like behavior: Random bekleme (10-20 saniye arası)
|
||||
if self.request_times:
|
||||
time_since_last = now - self.request_times[-1]
|
||||
min_interval = 3 # Minimum 3 saniye
|
||||
# Blocking varsa daha uzun bekle
|
||||
if self.block_count > 0:
|
||||
min_interval = 30 + random.uniform(0, 30) # 30-60 saniye random
|
||||
else:
|
||||
min_interval = 10 + random.uniform(0, 10) # 10-20 saniye random
|
||||
|
||||
if time_since_last < min_interval:
|
||||
sleep_time = min_interval - time_since_last
|
||||
logger.debug(f"[RATE_LIMIT] Minimum interval bekleme: {sleep_time:.2f} saniye (Son istek: {time_since_last:.2f} saniye önce)")
|
||||
logger.info(f"[RATE_LIMIT] Human-like bekleme: {sleep_time:.1f} saniye (Son istek: {time_since_last:.1f} saniye önce, Blocking sayısı: {self.block_count})")
|
||||
time.sleep(sleep_time)
|
||||
|
||||
# İstek zamanını kaydet
|
||||
@@ -101,10 +198,20 @@ class TranscriptExtractor:
|
||||
logger.error(f"[TRANSCRIPT] ❌ Video {video_id} transcript çıkarımı başarısız: {error_type} - {error_msg[:200]}")
|
||||
|
||||
# IP blocking hatası tespit edilirse işaretle
|
||||
if "blocking" in error_msg.lower() or "blocked" in error_msg.lower() or "IP" in error_msg:
|
||||
if "blocking" in error_msg.lower() or "blocked" in error_msg.lower() or "IP" in error_msg or "IpBlocked" in error_type:
|
||||
self.last_blocked_time = time.time()
|
||||
logger.warning(f"[TRANSCRIPT] 🚫 IP blocking tespit edildi! Video: {video_id}, Sonraki isteklerde 60 saniye bekleme yapılacak")
|
||||
self.block_count += 1
|
||||
wait_time = 300 + (self.block_count * 60) # 5 dakika + (blocking sayısı * 1 dakika)
|
||||
wait_time = min(wait_time, 1800) # Maksimum 30 dakika
|
||||
|
||||
logger.error(f"[TRANSCRIPT] 🚫 IP blocking tespit edildi! Video: {video_id}")
|
||||
logger.error(f"[TRANSCRIPT] Toplam blocking sayısı: {self.block_count}, Sonraki isteklerde {wait_time} saniye ({wait_time/60:.1f} dakika) bekleme yapılacak")
|
||||
logger.warning(f"[TRANSCRIPT] IP blocking detayları: {error_msg[:500]}")
|
||||
|
||||
# Çok fazla blocking varsa uyar
|
||||
if self.block_count >= 3:
|
||||
logger.error(f"[TRANSCRIPT] ⚠️ UYARI: {self.block_count} kez IP blocking alındı! YouTube IP'nizi geçici olarak engellemiş olabilir.")
|
||||
logger.error(f"[TRANSCRIPT] Öneriler: 1) Daha uzun bekleme (30+ dakika), 2) Farklı IP kullan, 3) Proxy/VPN kullan")
|
||||
|
||||
return None
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import os
|
||||
import yaml
|
||||
import time
|
||||
import logging
|
||||
import random
|
||||
from pathlib import Path
|
||||
|
||||
# Logger oluştur
|
||||
@@ -211,10 +212,10 @@ def process_channel(channel_id: str, max_items: int = 50) -> dict:
|
||||
else:
|
||||
logger.debug(f"[PROCESS] Tüm videolar zaten veritabanında")
|
||||
|
||||
# Bekleyen videoları işle (max_items kadar, 20'şer batch'ler halinde)
|
||||
# YouTube IP blocking'i önlemek için her batch'te 20 video işlenir
|
||||
# Bekleyen videoları işle (max_items kadar, küçük batch'ler halinde)
|
||||
# YouTube IP blocking'i önlemek için her batch'te sadece 5 video işlenir
|
||||
# max_items: Her istekte kaç video transcript işleneceği (maksimum 100)
|
||||
batch_size = 20 # Her batch'te işlenecek video sayısı
|
||||
batch_size = 5 # Her batch'te işlenecek video sayısı (küçük batch = daha az blocking riski)
|
||||
processed_count = 0 # İşlenen transcript sayısı
|
||||
|
||||
# Tüm bekleyen videoları al (channel_id'ye göre filtrele)
|
||||
@@ -290,10 +291,11 @@ def process_channel(channel_id: str, max_items: int = 50) -> dict:
|
||||
# Batch özeti
|
||||
logger.info(f"[BATCH] Batch {current_batch}/{total_batches} tamamlandı - İşlenen: {batch_processed}, Cache: {batch_cached}, Başarısız: {batch_failed}")
|
||||
|
||||
# Batch tamamlandı, kısa bir bekleme (rate limiting için)
|
||||
# Batch tamamlandı, uzun bekleme (YouTube IP blocking önleme için)
|
||||
if processed_count < max_items and batch_start + batch_size < len(all_pending_videos):
|
||||
wait_time = 2
|
||||
logger.debug(f"[BATCH] Batch'ler arası bekleme: {wait_time} saniye")
|
||||
# Blocking varsa daha uzun bekle
|
||||
wait_time = 60 + random.uniform(0, 30) # 60-90 saniye random (human-like)
|
||||
logger.info(f"[BATCH] Batch'ler arası bekleme: {wait_time:.1f} saniye ({wait_time/60:.1f} dakika) - YouTube IP blocking önleme")
|
||||
time.sleep(wait_time)
|
||||
|
||||
# İşlenmiş videoları getir
|
||||
|
||||
Reference in New Issue
Block a user