דלג לתוכן הראשי
scraping.
חזרה לכל המאמרים

הלוגיקה מאחורי rotation חכם של פרוקסים

8 במאי 20267 דק׳ קריאה
תרשים זרימה מופשט המראה החלטות לוגיות ברשת של צמתים המייצגים פרוקסים ושרתים

איפה רוב ה-scrapers נופלים: Rotation עיוור

בוא נתחיל מהסוף. רוב אסטרטגיות ה-rotation שראיתי בשטח פשוט לא עובדות. הן מבוססות על הרעיון הכי פשטני שיש: "אחרי כל 10 בקשות, תחליף IP". או "לכל thread, תן IP אחר". קוראים לזה rotation מבוסס ספירה, או round-robin.

זה נשמע הגיוני על הנייר. זה קל למימוש. וזה בדיוק המקום שבו 90% מהפרויקטים מתחילים לייצר שגיאות בקצב מסחרר.

למה? כי הגישה הזאת עיוורת לחלוטין למה שהשרת בצד השני אומר לך. אם פרוקסי מסוים (נניח 1.2.3.4) נחסם על ידי האתר בבקשה השנייה, המערכת העיוורת שלך תמשיך לשלוח איתו עוד 8 בקשות כושלות. היא תדווח על 80% כישלון עבור ה-IP הזה, תבזבז זמן ורוחב פס, ובסוף אולי גם תגרום לחסימה של כל ה-subnet.

הגישה הזו מתעלמת מהסיגנל הכי חשוב שיש לך: ה-HTTP status code של התגובה. זו לא אסטרטגיה, זו תפילה.

מה כן עובד: Rotation מבוסס תגובה (Event-Driven)

במקום לספור בקשות, בוא נקשיב לתגובות. לוגיקת rotation חכמה היא event-driven. כלומר, ההחלטה אם להחליף פרוקסי או להמשיך איתו מתקבלת *אחרי* קבלת התגובה מהשרת, לא לפני שליחת הבקשה.

החוקים פשוטים ואינטואיטיביים:

  • קיבלת 200 OK? מעולה. הפרוקסי הזה עובד מצוין מול היעד הזה. אין שום סיבה להחליף אותו. למעשה, כדאי להצמיד אותו לסשן הזה (sticky session) כדי לשמור על עקביות.
  • קיבלת 403 Forbidden או 429 Too Many Requests? זה סיגנל ברור. ה-IP הזה שרוף, לפחות זמנית. צריך להחליף אותו *מיד* ולהוציא אותו מהמאגר הפעיל עבור היעד הספציפי הזה.
  • קיבלת 503 Service Unavailable? זה מקרה אפור. זה יכול להיות שהשרת של היעד נפל, אבל סביר יותר שהפרוקסי עצמו לא יציב. הצעד הנכון הוא לנסות שוב עם פרוקסי אחר, אבל לא בהכרח לפסול את הפרוקסי הראשון לצמיתות. אולי הוא יתאושש.

המעבר מחשיבה של "כמה בקשות שלחתי" לחשיבה של "מה השרת ענה לי" הוא השינוי המנטלי הכי חשוב שצריך לעשות. זה ההבדל בין מערכת שמנחשת למערכת שמגיבה.

Circuit Breaker: איך לא לשרוף פרוקסים טובים

אוקיי, אז הבנו שצריך להוציא פרוקסי כושל מהמאגר. אבל לכמה זמן? אם נוציא אותו לצמיתות, מהר מאוד נישאר בלי פרוקסים. אם נחזיר אותו מיד, הוא ייכשל שוב.

כאן נכנס לתמונה דפוס תכנון שנקרא Circuit Breaker. הרעיון הוא לתת ל-IP "להתקרר". אם פרוקסי IP נכשל מול יעד ספציפי (למשל, מקבל 403), אנחנו לא זורקים אותו לפח. אנחנו מעבירים אותו למצב "פתוח" (tripped) *רק עבור אותו יעד* למשך זמן קבוע, נניח 10 דקות.

בזמן הזה, הלוגיקה שלנו אפילו לא תנסה להשתמש בו עבור היעד הבעייתי, אבל הוא עדיין זמין לשימוש מול אתרים אחרים. אחרי 10 דקות, המעגל "נסגר" למצב "חצי פתוח", ואנחנו מרשים לבקשה אחת לעבור דרכו. אם היא מצליחה, הוא חוזר למאגר הפעיל. אם היא נכשלת, הוא חוזר לתקופת צינון נוספת, אולי ארוכה יותר.

הנה דוגמה קונספטואלית בפייתון:


from collections import defaultdict
import time

# מילונים שישמרו את מצב הפרוקסים לכל יעד
proxy_failures = defaultdict(lambda: defaultdict(int))
proxy_cooldown_until = defaultdict(lambda: defaultdict(float))

MAX_FAILURES = 3
COOLDOWN_SECONDS = 600 # 10 minutes

def should_use_proxy(proxy_ip, target_host):
    """בודק אם פרוקסי מסוים נמצא בתקופת צינון עבור יעד ספציפי"""
    if time.time() < proxy_cooldown_until[proxy_ip][target_host]:
        return False # The circuit is open
    return True

def handle_request_failure(proxy_ip, target_host):
    """מעדכן את מצב ה-Circuit Breaker לאחר כישלון"""
    proxy_failures[proxy_ip][target_host] += 1
    if proxy_failures[proxy_ip][target_host] >= MAX_FAILURES:
        print(f"Tripping circuit breaker for {proxy_ip} on {target_host} for {COOLDOWN_SECONDS}s")
        proxy_cooldown_until[proxy_ip][target_host] = time.time() + COOLDOWN_SECONDS
        # Reset a bit later, or when it comes back

def handle_request_success(proxy_ip, target_host):
    """מאפס את המונים לאחר הצלחה"""
    proxy_failures[proxy_ip][target_host] = 0
    proxy_cooldown_until[proxy_ip][target_host] = 0

המודל הזה מונע מאיתנו לשרוף IPs טובים בגלל בעיה זמנית, ומגדיל דרמטית את אורך החיים והיעילות של מאגר הפרוקסים שלנו.

אסטרטגיית Fallback: מה קורה כשהכל נכשל

בואו נהיה ריאליים. לפעמים, גם עם הלוגיקה הכי חכמה, סוג מסוים של פרוקסי פשוט לא יעבוד מול יעד מסוים. אתר קשוח במיוחד עלול לחסום את כל טווח ה-IPs של ספק הדאטה-סנטר שלך. מה עושים אז?

הפתרון הוא לבנות שרשרת חלופות (Fallback Chain) מדורגת. אנחנו לא מתייחסים לכל הפרוקסים כשווים. אנחנו מתחילים עם הזול והמהיר, ואם הוא נכשל, אנחנו מסלימים את הבעיה לסוג הפרוקסי הבא בתור, שהוא יקר יותר אבל עם סיכוי הצלחה גבוה יותר.

שרשרת קלאסית נראית כך:

  1. ניסיון ראשון: Datacenter Proxy. הם מהירים, זולים, וזמינים. תמיד כדאי להתחיל איתם. אם זה עובד, חסכנו בעלויות.
  2. Fallback 1 (לאחר חסימה): Residential Proxy. אם פרוקסי הדאטה-סנטר מקבל חסימה (למשל, דף CAPTCHA), המערכת צריכה לזהות את זה ולנסות את אותה בקשה שוב, הפעם דרך פרוקסי residencial. אלה IPs של משתמשים אמיתיים, והם נראים הרבה יותר לגיטימיים לאתר המטרה.
  3. Fallback 2 (חסימה מתקדמת): Mobile Proxy. יש אתרים, בעיקר רשתות חברתיות ואתרי מסחר גדולים, שיודעים לזהות גם פרוקסים residencial. במקרה כזה, החלופה הבאה היא פרוקסי סלולרי. אלה ה-IPs הכי "נקיים" שיש, כי הם משותפים לאלפי משתמשים ברשת הסלולר, וקשה מאוד לחסום אותם.
  4. Fallback 3 (המוצא האחרון): פתרון CAPTCHA. אם גם פרוקסי סלולרי נתקל ב-CAPTCHA, זה הזמן להפעיל את התותחים הכבדים: שליחת הבקשה דרך דפדפן אמיתי (כמו Playwright) שמופעל על ידי שירות פתרון CAPTCHAs אוטומטי.

זוהי ארכיטקטורת scraping בריאה. היא מאזנת בין עלות לביצועים, ומבטיחה שאנחנו משתמשים בכלי היקר רק כשאין ברירה.

תרחיש כישלון קלאסי: אתר מסחר אלקטרוני

בוא נדבר על מקרה אמיתי. עבדתי על פרויקט scraping לאתר אופנה גדול. המטרה הייתה לאסוף מחירים וזמינות מוצרים. התחלנו עם 1,000 פרוקסים של דאטה-סנטר ו-rotation מבוסס ספירה.

בשעה הראשונה, הכל עבד. קצב ההצלחה היה מעל 95%. ואז, בום. תוך 15 דקות, קצב ההצלחה צנח ל-20%. התחלנו לקבל 403 Forbidden בכל מקום. מערכת ה-WAF (Web Application Firewall) של האתר זיהתה את התבנית של הבקשות שלנו ואת טווח ה-IPs של הדאטה-סנטר ופשוט חסמה את כולם. המערכת העיוורת שלנו המשיכה לשלוח בקשות עם IPs שרופים, מה שרק החמיר את המצב.

הפתרון היה מעבר למודל היברידי: התחלנו כל בקשה עם פרוקסי דאטה-סנטר. ברגע שזיהינו תגובת חסימה, אותה בקשה ספציפית נוסתה מחדש אוטומטית עם פרוקסי residencial. בנוסף, ה-IP של הדאטה-סנטר שנכשל נכנס ל-Circuit Breaker של 20 דקות. קצב ההצלחה חזר מיידית ל-98%, והמערכת הפכה ליציבה לאורך זמן.

ומה לגבי Sticky Sessions?

לפעמים, המטרה היא דווקא *לא* להחליף פרוקסי. תחשבו על תהליך רכישה באתר שדורש מכם לעבור 4-5 עמודים. אם תחליפו IP באמצע התהליך, השרת כנראה יזרוק את הסשן שלכם ויחזיר אתכם לדף הבית. זה נכון גם לגבי אתרים שדורשים התחברות.

במקרים כאלה, אנחנו צריכים "סשנים דביקים" (Sticky Sessions). הלוגיקה היא כזו: כשמתחילים תהליך חדש (למשל, הוספת מוצר לעגלה), אנחנו מקצים לו פרוקסי ספציפי ממאגר הפרוקסים שלנו. כל עוד הפרוקסי הזה מחזיר תגובות 200 OK, כל הבקשות הבאות באותו תהליך ישתמשו בו. רק אם הוא נכשל, ננסה להחליף אותו בפרוקסי אחר מאותו אזור גיאוגרפי (כדי לשמור על עקביות) ולהמשיך את הסשן.

זה דורש ניהול מצב (state management) יותר מורכב, אבל זה קריטי עבור תהליכי scraping שאינם רק קריאת דפים בודדים.

שאלות נפוצות

ההבדל המרכזי הוא בטריגר שמפעיל את החלפת הפרוקסי. Rotation מבוסס ספירה (round-robin) מחליף IP אחרי מספר קבוע של בקשות, בלי קשר להצלחתן, מה שגורם לבזבוז בקשות על פרוקסים חסומים. לעומת זאת, rotation מבוסס תגובה (event-driven) מחליף פרוקסי רק כאשר מתקבלת תגובת שרת שלילית, כמו 403 או 429. גישה זו מגיבה בזמן אמת לחסימות, משפרת את אחוזי ההצלחה מ-70% לפעמים למעל 98%, ומנצלת את מאגר הפרוקסים בצורה יעילה הרבה יותר.

יישום Circuit Breaker דורש ניהול מצב עבור כל זוג של (פרוקסי, יעד). משתמשים במבנה נתונים כמו hash map (או מילון בפייתון) כדי לעקוב אחר מספר הכישלונות הרצופים. כאשר פרוקסי חוצה רף מסוים (למשל, 3 כישלונות), מוסיפים אותו למבנה נתונים נוסף עם חותמת זמן שמציינת מתי תקופת הצינון שלו מסתיימת. לפני כל בקשה, הלוגיקה בודקת אם הפרוקסי הנבחר נמצא בצינון עבור היעד. כלים כמו Redis מתאימים מאוד לניהול מצב כזה במערכות מבוזרות.

יש להשתמש ב-Sticky Sessions כאשר אתם מבצעים scraping של תהליך מרובה שלבים שדורש שמירת מצב (state). דוגמאות קלאסיות כוללות מילוי טפסים, תהליכי צ'ק-אאוט באתרי מסחר אלקטרוני, או ניווט באתר לאחר התחברות עם שם משתמש וסיסמה. החלפת IP באמצע תהליך כזה תגרום לרוב לאיבוד הסשן. במקרים אלה, מקצים פרוקסי אחד לסשן כולו ומחליפים אותו רק אם הוא נכשל, בניגוד ל-scraping של דפי מוצר בודדים שבו rotation אגרסיבי עדיף.

שרשרת fallback יעילה מתחילה מהאפשרות הזולה והמהירה ביותר ומתקדמת ליקרות והאמינות יותר רק בעת הצורך. סדר מומלץ הוא: 1. פרוקסי דאטה-סנטר (מהיר וזול). 2. אם נחסם, נסה שוב עם פרוקסי Residential (IP ביתי, אמין יותר). 3. אם גם זה נחסם, השתמש בפרוקסי Mobile (הכי אמין ויקר). 4. כמוצא אחרון מול CAPTCHA, השתמש בדפדפן אמיתי (כמו Playwright) עם שירות פתרון CAPTCHA. ארכיטקטורה זו ממזערת עלויות וממקסמת את סיכויי ההצלחה.

זיהוי אוטומטי מתבסס בעיקר על ניתוח תגובת ה-HTTP. הכלל הבסיסי הוא להתייחס לכל status code שאינו 2xx ככישלון פוטנציאלי. קודים כמו 403, 429, 401, ו-407 הם סימנים ברורים לחסימת IP. קודים ממשפחת 5xx (כמו 503) מעידים לרוב על בעיה בפרוקסי עצמו. בנוסף, חשוב לנתח גם את תוכן הדף: לפעמים מקבלים 200 OK, אבל הדף מכיל CAPTCHA. לכן, לוגיקה חזקה תבדוק גם נוכחות של מילות מפתח כמו 'captcha' או 'verify you are human' ב-HTML שהתקבל.

אהבתם את הכתבה? הצטרפו לניוזלטר ה-AI.

סיכום שבועי של כל מה שחדש ב-AI, פרומפטים מעשיים וביקורות כלים — ישר למייל שלכם.

הירשמו עכשיו

עוד לקריאה