ארכיטקטורת היעד: למה headless browser הוא חובה כאן
בואו נדבר תכלס. אתר עצמל׳ה מרנדר את דפי המוצר והקטגוריות שלו בצד הלקוח. זה אומר שנתונים חיוניים כמו מחירים, מבצעים, ואפילו שמות מוצרים נטענים דרך קריאות API א-סינכרוניות לאחר שהדף הראשוני נטען. אם תנסו לעשות GET פשוט ל-URL של מוצר, תקבלו HTML בסיסי, אבל ה-div שאמור להכיל את המחיר יהיה ריק, או שיכיל placeholder. זו הסיבה שגישה מבוססת requests נידונה לכישלון מהרגע הראשון.
כאן נכנס לתמונה headless browser כמו Playwright. הוא לא רק מוריד את ה-HTML, הוא מריץ דפדפן אמיתי (כמו Chromium) מאחורי הקלעים, מפעיל את מנוע ה-JavaScript, ממתין לקריאות ה-API שיסתיימו ומרנדר את הדף המלא. רק אז אנחנו יכולים לחלץ את המידע. העבודה עם Playwright מאפשרת לנו לאסוף את כל קטלוג עצמל׳ה, כולל כל המפרטים הטכניים והתמונות, בצורה אמינה. זה דורש יותר משאבים מ-requests, אין ספק, אבל האלטרנטיבה היא פשוט לא לקבל את הדאטה. לטובת אמינות גבוהה, אנחנו בדרך כלל משלבים אותו עם מדריך Playwright stealth כדי להיראות כמו משתמש אנושי ככל האפשר ולהימנע מחסימות אוטומטיות פשוטות. המטרה היא להגיע לאחוזי הצלחה של מעל 98% בסריקה יומית של כ-5,000 דפי מוצר.
איך לבנות תהליך איסוף קטלוג יעיל
אז החלטנו על Playwright. מה הלאה? המשימה הראשונה היא מיפוי ואיסוף קטלוג. המטרה היא לבנות תהליך אוטומטי שמגלה את כל המוצרים באתר ומוסיף אותם למסד הנתונים שלנו. התהליך בעצמל׳ה מתחיל מעמוד הבית, משם אנחנו זוחלים לכל הקטגוריות הראשיות ודפי המשנה. בכל עמוד קטגוריה, אנחנו צריכים לטפל בפאג'ינציה (pagination) – המעבר בין עמודים – כדי לאסוף את כל המוצרים.
הנקודה הקריטית כאן היא ניהול התורים (Queue). אנחנו מתחילים עם רשימת קטגוריות, וכל מוצר שאנחנו מוצאים מוסיף URL חדש לתור ה-scraping שלנו. חשוב מאוד לנהל visited set כדי לא לסרוק את אותו דף פעמיים. בקנה מידה של אלפי מוצרים, סריקה כפולה היא בזבוז משאבים וזמן. עבור פרויקט איסוף קטלוג עצמל׳ה, בנינו תהליך שמריץ 5-10 instances של Playwright במקביל. זה מאפשר לנו לסרוק את כל האתר, כולל כל עמודי המוצר, תוך פחות משעתיים. הנתונים שנאספים – שמות מוצרים, מק"טים, תיאורים וקטגוריות – מהווים את הבסיס לכל פעילות עתידית, כמו ניטור מחירים או מעקב מלאי.
תרחיש כישלון קלאסי: שינוי מבנה ה-DOM וזיהוי מחירים
הנה סיפור מהשטח. בנינו סקרייפר מושלם עבור עצמל׳ה. הוא עבד במשך חודשיים עם 99% הצלחה, סיפק עדכוני מחירים יומיים והכל היה יציב. בוקר אחד, המערכת מתחילה לזרוק שגיאות. 80% מהמוצרים חזרו ללא מחיר. מה קרה? צוות הפיתוח של עצמל׳ה שינה את ה-CSS class של האלמנט שמכיל את המחיר. ה-selector שלנו, שהיה span.final-price, הפך פתאום ל-div.product-price > span. הסקרייפר לא נשבר לגמרי – הוא עדיין טען את הדף – אבל הוא פשוט לא מצא את המידע הקריטי.
זוהי נקודת תורפה קלאסית ב-scraping. ההסתמכות על selectors ספציפיים היא שבירה. הפתרון הוא לבנות מערכת חסינה יותר. במקום לחפש רק selector אחד, אנחנו בונים רשימת selectors אפשריים לפי עדיפות. בנוסף, אנחנו מוסיפים validation לכל ריצה: אם יותר מ-10% מהמוצרים חוזרים ללא מחיר, המערכת מרימה דגל אדום ושולחת התראה מיידית. כך אנחנו לא מגלים את הבעיה אחרי שבוע של נתונים חסרים. זה שינוי קטן בקוד, אבל הוא ההבדל בין פרויקט חובבני למערכת production-grade שמספקת API / קובץ נתונים אמין ללקוחות.
מעקב מלאי וזמינות: האתגר שמעבר למחיר
ניטור מחירים בעצמל׳ה הוא רק חלק מהסיפור. עבור לקוחות בתחום המודיעין המתחרים, מידע על זמינות ומלאי הוא קריטי לא פחות. באתר עצמל׳ה, הזמינות לא תמיד מוצגת כטקסט פשוט. לפעמים זו הודעת "אזל מהמלאי", לפעמים כפתור "הוסף לסל" הופך לאפור ולא לחיץ, ולפעמים יש ציון זמינות לפי סניפים ספציפיים. לכן, אי אפשר פשוט לחפש מילת מפתח אחת.
הגישה שלנו למעקב מלאי/זמינות עצמל׳ה דורשת לוגיקה מורכבת יותר. אנחנו בודקים סט של תנאים: קיום של אלמנט מסוים, ה-CSS class של כפתור הרכישה, הטקסט שמופיע ליד המחיר, ואפילו קריאות API ברקע שבודקות זמינות מול המערכת שלהם. לדוגמה, ראינו מקרים בהם לחיצה על כפתור "בדוק זמינות בסניפים" מפעילה קריאת XHR ברקע. במקום לנסות לדמות את הלחיצה, לפעמים יעיל יותר ללמוד את מבנה קריאת ה-API הזו ולשלוח אותה ישירות כדי לקבל JSON נקי עם נתוני מלאי לפי סניף. זה דורש ניתוח עמוק יותר של תעבורת הרשת בדפדפן, אבל התוצאה היא איסוף נתונים מהיר ואמין יותר, עם פחות תלות ב-DOM המשתנה.
מתי לא להשתמש ב-Playwright (כן, יש מקרים כאלה)
אחרי כל מה שאמרתי, זה אולי יישמע מוזר, אבל Playwright הוא לא תמיד הפתרון. הוא כלי כבד. כל instance שלו צורך כמות משמעותית של זיכרון ומעבד. אם המטרה שלך היא רק לבדוק אם דף מוצר מסוים עדיין קיים (בדיקת סטטוס 200), או לחלץ מידע שנמצא ישירות ב-HTML הראשוני, שימוש ב-Playwright הוא כמו להשתמש בפטיש 5 קילו כדי לתקוע נעץ.
בפרויקטים מסוימים, גילינו שחלק מהמידע באתר עצמל׳ה כן זמין ב-API פנימי שהאתר קורא לו. למשל, רשימת הסניפים או נתונים מסוימים על קטגוריות. אם אפשר לזהות את קריאות ה-API האלה (דרך ה-Network tab בכלי המפתחים), לחקות אותן ישירות עם ספרייה כמו httpx זה הרבה יותר יעיל. זה דורש עבודת חקירה ראשונית גדולה יותר, וה-API עלול להשתנות או לדרוש headers מיוחדים, אבל התמורה היא עצומה: latency של מילי-שניות במקום שניות, וצריכת משאבים נמוכה בסדרי גודל. לכן הגישה ההיברידית היא לרוב הטובה ביותר: השתמשו ב-Playwright לחילוץ מידע מורכב מדפים דינמיים, אבל אם זיהיתם API נקי, עברו אליו. זה דורש הבנה עמוקה של איך לבחור פרוקסי residential כדי שהקריאות ייראו לגיטימיות גם ברמת הרשת.
