מבנה האתר ותכנון ה-Crawl הראשוני

לפני שכותבים שורת קוד אחת, צריך למפות את השטח. האתר של אייבורי, כמו רוב אתרי הקמעונאות המודרניים, הוא היברידי. דפי הקטגוריה הראשיים כנראה מרונדרים בשרת (SSR) בשביל SEO, מה שהופך את גילוי המוצרים הראשוני לקל יחסית. אבל השטן נמצא בפרטים. כשנכנסים לדף מוצר, הרבה מהמידע הקריטי – כמו זמינות בסניפים ספציפיים או מבצעים מבוססי זמן – נטען דינמית באמצעות קריאות API אסינכרוניות. סקריפט פשוט מבוסס requests ו-BeautifulSoup יפספס את המידע הזה ב-90% מהמקרים.

השלב הראשון הוא crawl רוחבי לאיסוף כל כתובות ה-URL של המוצרים. אפשר להתחיל ממפת האתר (sitemap.xml) אם היא קיימת ועדכנית, או פשוט לעבור רקורסיבית על כל דפי הקטגוריות. עם קטלוג שמוערך ב-25,000-40,000 מוצרים, גם הפעולה הזו לבדה דורשת תכנון. צריך לנהל תור של כתובות, לעקוב אחר מה שכבר נסרק, ולרוץ בקצב שלא יעורר חשד. קצב של 30-40 בקשות לדקה עם IP בודד זה הגבול העליון לפני שמתחילים לראות שגיאות 429. המטרה כאן היא לבנות את בסיס הנתונים הראשוני של המוצרים, מה שמוביל אותנו ישירות למשימת איסוף קטלוג אייבורי המלא, כולל מזהי מוצר (SKUs), שמות ומפרטים בסיסיים. רק אחרי שיש לנו את המפה המלאה, אפשר להתחיל לחשוב על איסוף הנתונים העמוקים יותר.

למה Playwright הוא הבחירה הנכונה כאן (ולא requests)

אני אגיד את זה ישירות: אם אתם מתחילים פרויקט scraping לאייבורי ב-2025 עם ספריית HTTP פשוטה, אתם בונים לעצמכם כאב ראש עתידי. בגלל אותן קריאות API בצד הלקוח שדיברנו עליהן, אנחנו חייבים כלי שיכול לרנדר JavaScript. הבחירה הברורה היא Headless Browser. ותפסיקו עם Selenium לפרויקטים חדשים. Playwright מנצח אותו בכל מטריקה רלוונטית – מהירות, יציבות, ו-API נקי יותר.

השימוש ב-Playwright מאפשר לנו לחכות לרכיבים ספציפיים שנטענים דינמית. במקום לנחש sleep(5), אנחנו יכולים להורות לסקריפט לחכות עד שה-div שמכיל את מלאי הסניפים יופיע ב-DOM. זה הבדל בין scraper שביר שנכשל כל פעם שהרשת קצת איטית, לבין מערכת רובסטית. יתרון נוסף הוא היכולת ליירט בקשות רשת. אפשר להאזין לקריאות ה-API הפנימיות שהדפדפן מבצע כדי לקבל מידע על מחירים או מלאי, ולפעמים אפילו לגלות endpoints לא מתועדים שאפשר לפנות אליהם ישירות. זה יכול לחסוך משאבים אדירים כי לא צריך לרנדר דף שלם רק כדי לקבל פיסת JSON קטנה. אבל כדי שזה יעבוד, צריך להשקיע בטביעת אצבע אמינה. כאן נכנסים פתרונות כמו המדריך ל-Playwright stealth שעוזרים להסוות את העובדה שהדפדפן נשלט על ידי אוטומציה.

תרחיש כישלון נפוץ: מעקב מלאי וזמינות

בואו נדבר על תרחיש שראיתי קורס שוב ושוב. בניתם scraper שעובד נהדר על 10 מוצרים שבדקתם ידנית. הוא מביא מחיר, שם, הכל פיקס. אתם מריצים אותו על כל הקטלוג, ואחרי כמה שעות מגלים ש-30% מהמוצרים מופיעים כ"לא זמינים במלאי", למרות שאתם רואים אותם באתר. מה קרה?

הכשל הקלאסי באתרי e-commerce כמו אייבורי הוא שהזמינות לא נטענת עם הדף הראשוני. היא מגיעה בגל שני של בקשות. הדף נטען, מציג placeholder או "בודק זמינות...", ורק אז קריאת JavaScript יוצאת לשרת כדי לקבל את המידע העדכני ביותר. ה-scraper שלכם, שהיה מהיר מדי, הספיק לקרוא את ה-HTML לפני שהמידע האמיתי הגיע ופשוט גרד את ה-placeholder. זו הסיבה שחייבים להשתמש ב-waitForSelector או waitForResponse של Playwright. צריך לזהות את ה-endpoint הספציפי שמחזיר את נתוני המלאי ולהמתין לתשובה ממנו לפני שממשיכים. זה אולי מוסיף 500ms ל-latency של כל בקשה, אבל זה ההבדל בין דאטה שמיש לדאטה חסר ערך. מעקב מלאי/זמינות אייבורי דורש סבלנות ברמת הבקשה הבודדת, לא רק מהירות ברמת המערכת.

ניהול Proxies, טביעות אצבע, וחסימות

אי אפשר לדבר על scraping בסדר גודל כזה בלי לדבר על איך לא להיחסם. כתובת IP בודדת שתבצע אלפי בקשות לאתר אייבורי תזוהה ותיחסם תוך דקות. הפתרון הוא proxy rotation. אבל לא כל פרוקסי נולד שווה. פרוקסים של דאטה סנטר הם זולים ומהירים, אבל קל מאוד לזהות אותם. לאתר קמעונאות גדול, הבחירה הנכונה היא כמעט תמיד רשת של residential proxies. הם איטיים יותר ויש להם אחוזי כישלון גבוהים יותר, אבל הם נראים כמו תעבורה של משתמשים אמיתיים. המטרה היא להגיע לאחוז הצלחה של מעל 98% באופן עקבי.

ניהול הרשת הזו הוא אתגר בפני עצמו. צריך מערכת שתבצע רוטציה אוטומטית של כתובות IP, תזהה פרוקסים שנחסמו ותוציא אותם מה-pool, ותטפל ב-retries בצורה חכמה. כשנתקלים בשגיאת 429 (Too Many Requests), לא מנסים שוב מיד מאותו IP. מחליפים IP ומנסים שוב אחרי השהייה אקראית. זה לא מספיק רק להחליף IP. מערכות הגנה מודרניות בונות טביעת אצבע של הדפדפן (fingerprint). אם כל הבקשות שלכם מגיעות עם אותו User-Agent, אותה רזולוציית מסך ואותם פונטים, גם אם ה-IP משתנה, קל לקשר ביניהן. לכן, חשוב לגוון גם את הפרמטרים האלה. איך לבחור פרוקסי residential זה נושא שלם בפני עצמו ששווה להעמיק בו.

מתי לא כדאי לבנות Scraper ייעודי לאייבורי

למרות כל מה שאמרתי, יש מצבים שבהם בניית מערכת scraping מורכבת היא פשוט Overkill. חשוב להיות פרגמטיים. אם כל מה שאתם צריכים זה לעקוב אחרי המחיר של שני כרטיסי מסך ולקבל התראה כשהם יורדים מתחת לרף מסוים, אתם לא צריכים ארכיטקטורת מיקרו-שירותים עם Kafka ו-Kubernetes. סקריפט פשוט שירוץ פעם ביום מהמחשב שלכם כנראה יספיק.

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

השלב הסופי: הפיכת המידע הגולמי ל-API או קובץ נתונים

איסוף הנתונים הוא רק חצי מהעבודה. דאטה גולמי שיושב במסד נתונים לא שווה כלום אם אי אפשר להשתמש בו. השלב האחרון והקריטי הוא הנגשת המידע. עבור רוב הלקוחות, זה אומר לספק API / קובץ נתונים אייבורי בצורה נוחה לצריכה. הפורמט הנפוץ ביותר הוא ייצוא יומי או שבועי של קובץ CSV או JSON ל-S3 bucket. זה פשוט, אמין, וקל לאינטגרציה עם מערכות אחרות.

לא משנה באיזה פורמט תבחרו, חשוב להשקיע בסכימת נתונים נקייה ועקבית. כל מוצר צריך מזהה ייחודי קבוע (SKU או מזהה פנימי של אייבורי). שדות כמו מחיר צריכים להיות מנורמלים (למשל, תמיד כמספר, בלי '₪'). חשוב גם לתעד שינויים לאורך זמן. במקום רק לשמור את המחיר הנוכחי, שמרו היסטוריית מחירים עם חותמת זמן. זה מאפשר ניתוחים מורכבים יותר בהמשך, כמו זיהוי מגמות או תדירות של מבצעים. בניית API פנימי שמחזיר את המידע העדכני ביותר למוצר ספציפי היא השלב הבא, ומאפשרת שימושים בזמן אמת. אבל גם כאן, חשוב לזכור את הצד השני – טיפול בשגיאות 429 הוא קריטי גם ב-API שאתם חושפים, כדי למנוע עומס על המערכת שלכם.