למה Requests פשוט לא יספיק לכם ב-Booknet
בואו נשים את זה על השולחן: אם הגישה הראשונית שלכם ל-Booknet היא requests.get(), אתם מבזבזים את הזמן שלכם. האתר, כמו רוב פלטפורמות המסחר המודרניות, לא שולח את כל התוכן ב-HTML הראשוני. מה שאתם מקבלים זה שלד של אפליקציית JavaScript, כנראה React או Vue. התוכן האמיתי – שמות מוצרים, מחירים, ובמיוחד זמינות – נטען דינמית דרך קריאות API פנימיות שהדפדפן מריץ אחרי טעינת הדף.
אפשר, תיאורטית, לנסות לעשות reverse engineering לקריאות ה-XHR האלה. פותחים את ה-DevTools, מסתכלים על ה-Network tab, ומנסים לחקות את הבקשות. עשיתי את זה עשרות פעמים בפרויקטים אחרים. הבעיה? זה שביר בצורה קיצונית. כל עדכון קטן ב-frontend של Booknet יכול לשבור לכם את ה-scraper. ה-endpoint ישתנה, פרמטר יתווסף, או שיוסיפו header חדש לאימות. התחזוקה הופכת לסיוט. עם קטלוג שמוערך בלמעלה מ-20,000 כותרים פעילים, זו לא אסטרטגיה בת-קיימא.
הפתרון היחיד שעובד באופן עקבי הוא הדמיית דפדפן אמיתי. כלים כמו Playwright או Puppeteer מריצים מנוע דפדפן אמיתי (Chromium, WebKit), מריצים את ה-JavaScript בדיוק כמו שמשתמש היה עושה, ומאפשרים לכם לגשת ל-DOM הסופי – אחרי שכל הנתונים כבר נטענו. זה אולי מרגיש כמו overhead, אבל המאמץ הראשוני הזה חוסך שבועות של תיקונים ותחזוקה בהמשך הדרך.
ארכיטקטורת ה-Scraper: Playwright, Proxies, וניהול סשנים
אז החלטנו על דפדפן. אני ממליץ על Playwright. הוא פשוט מהיר ויציב יותר מ-Selenium לפרויקטים חדשים. השלב הראשון הוא להגדיר אותו נכון. אל תריצו אותו "חשוף". השתמשו בתוספים כמו playwright-extra עם stealth plugin כדי להסוות את העובדה שזה דפדפן אוטומטי. זה מטפל בעשרות פרטים קטנים, כמו שינוי user-agent, הסתרת מאפייני navigator.webdriver, והתאמת רזולוציית המסך.
השלב הבא הוא פרוקסים. אל תחשבו אפילו להריץ scraper בקנה מידה גדול מה-IP של השרת שלכם. אתם תיחסמו תוך שעה. אתם צריכים מאגר של פרוקסים, וחשוב מכך – לוגיקת רוטציה חכמה. ל-Booknet, גיליתי ש-proxies מסוג residential עובדים הכי טוב. הם יקרים יותר מבחינת משאבים, אבל הם נראים כמו תעבורה של משתמשים אמיתיים ומורידים משמעותית את הסיכוי לחסימה. תוכלו לקרוא עוד על איך לבחור פרוקסי residential במדריך שלנו. הלוגיקה צריכה להיות פשוטה: החלף IP כל 50-100 בקשות, או מיידית אחרי קבלת שגיאת 403 או CAPTCHA. שמירת סשנים (cookies) לכל IP יכולה לעזור, אבל רק לפרקי זמן קצרים כדי לא ליצור טביעת אצבע ברורה מדי.
איך לבנות Data Pipeline לניטור מחירים ואיסוף קטלוג
אחרי שהתשתית הבסיסית עובדת, המטרה היא להפוך את ה-scraping לתהליך אמין. שני ה-use cases המרכזיים עבור Booknet הם איסוף קטלוג וניטור מחירים.
לאיסוף קטלוג מלא, התהליך מתחיל מדפי הקטגוריות. ה-scraper צריך לנווט בין כל הדפים (pagination) בכל קטגוריה ולאסוף את ה-URLs של כל ספרי המוצר. את ה-URLs האלה זורקים לתור (queue) כמו RabbitMQ או Redis. worker נפרד שולף URL מהתור, מריץ את Playwright כדי לפתוח את הדף, מחלץ את כל השדות הנדרשים (שם, מחבר, הוצאה, מחיר, מבצעים, ISBN), ושומר אותם לדאטהבייס. שימוש בתור מפריד בין גילוי הלינקים (discovery) לבין העיבוד עצמו (processing), מה שמאפשר סקיילביליות. אפשר להריץ worker אחד לגילוי ועשרות workers לעיבוד במקביל.
לניטור מחירים, התהליך דומה אבל ממוקד יותר. במקום לסרוק הכל, אתם עובדים עם רשימה קיימת של URLs. כאן המטרה היא מהירות ויעילות. אפשר להריץ את ה-scraper בתדירות גבוהה יותר, למשל כל כמה שעות, כדי לתפוס שינויי מחיר ומבצעים. אם אתם רואים שה-scraper נתקל בבעיות ביצועים או נחסם, ייתכן שאתם נתקלים ב-rate limiting. במקרה כזה, חשוב לדעת איך לטפל בזה נכון, כפי שמוסבר במדריך שלנו על טיפול בשגיאות 429.
תרחיש כשל קלאסי: זמינות מלאי מטעה
הנה תרחיש שנתקלתי בו יותר מפעם אחת עם אתרי ספרים כמו Booknet: מעקב מלאי/זמינות שמניב נתונים לא נכונים. אתם טוענים דף מוצר, רואים את התווית "זמין במלאי", חולצים אותה, וממשיכים הלאה. אבל אז, אתם מגלים שהמידע הזה לא תמיד מדויק. לפעמים, הזמינות האמיתית מתגלה רק אחרי הוספה לסל או בשלב הצ'קאאוט.
למה זה קורה? הרבה אתרים מציגים זמינות כללית בדף המוצר, אבל הבדיקה המדויקת מול מערכת המלאי (ERP) מתרחשת רק כשהמשתמש מביע כוונה לרכוש. זהו failure mode קלאסי כי ה-scraper שלכם חושב שהכל תקין (הוא מקבל סטטוס 200 ומחלץ את הטקסט), אבל הנתונים פשוט שגויים. כדי להתמודד עם זה, scraper מתקדם יותר יצטרך לדמות את תהליך הוספה לסל. זה דורש ניהול סשן (cookies) הרבה יותר הדוק, ולפעמים אפילו התמודדות עם טוקנים של CSRF. אם המטרה שלכם היא לספק API / קובץ נתונים עם מידע זמינות אמין, אי אפשר לדלג על השלב הזה. זה מעלה את המורכבות פי כמה, אבל זה ההבדל בין דאטה שמיש לדאטה מטעה.
אבל רגע, הגישה הזו לא תמיד נכונה
חשוב להיות כנים: שימוש ב-Headless browser לכל בקשה הוא לא פתרון קסם, ויש לו חסרונות. העלות העיקרית היא לא כספית, אלא במונחי משאבי חישוב וזמן. כל הרצה של Playwright צורכת משמעותית יותר CPU ו-RAM מאשר בקשת HTTP פשוטה. אם אתם צריכים לסרוק 20,000 דפים, הפער הופך להיות עצום. Latency של בקשה יכול לקפוץ מ-200ms עם requests ל-2-5 שניות עם Playwright, תלוי במורכבות הדף.
לכן, הגישה הזו לא מתאימה לכל תרחיש. אם אתם צריכים רק לבדוק אם דף קיים (בדיקת סטטוס 200), או לחלץ מידע שנמצא בתוך ה-HTML הראשוני (כמו תגיות meta), שימוש ב-Playwright הוא בזבוז. במקרים כאלה, גישה היברידית היא הטובה ביותר: השתמשו בבקשות HTTP קלות ומהירות לסינון ראשוני, ורק כשיש צורך במידע שנטען דינמית – תפעילו את התותחים הכבדים של Playwright. למשל, אפשר להשתמש ב-HTTP כדי לאסוף את כל ה-URLs מה-sitemap.xml של Booknet, ורק אז להעביר אותם ל-worker מבוסס דפדפן לחילוץ התוכן המלא. זה דורש יותר תכנון ארכיטקטוני, אבל מביא לחיסכון אדיר במשאבים וזמן ריצה.
השלב הבא: סקייל, ניטור וייצוא נתונים
בניית scraper שעובד פעם אחת זה החלק הקל. האתגר האמיתי הוא להריץ אותו בסקייל, 24/7, עם אחוזי הצלחה של מעל 95%. בשביל זה, אתם חייבים מערכת ניטור (monitoring). בנו דשבורד פשוט (עם Grafana או כלי דומה) שעוקב אחרי מדדים קריטיים: מספר הבקשות בשעה, אחוז ההצלחות (סטטוס 200), אחוז החסימות (403, CAPTCHA), וזמן הריצה הממוצע לדף. אם אחוז השגיאות קופץ מעל 5%, אתם צריכים לקבל התראה מיידית.
השלב האחרון הוא הנגשת המידע. מודיעין מתחרים לא שווה כלום אם הוא תקוע בדאטהבייס. הגדירו תהליכי ייצוא אוטומטיים. זה יכול להיות קובץ CSV יומי שנשלח במייל או נשמר ב-S3, או API פנימי שהצוותים האחרים בחברה יכולים לצרוך. פורמט הנתונים צריך להיות נקי ומוכן לשימוש. אל תזרקו JSON גולמי; נרמלו את השדות, טפלו בערכים חסרים, והבטיחו שהסכמה (schema) יציבה. אם אתם צריכים לבצע משימות מורכבות בדפדפן, כמו אינטראקציה עם אלמנטים מוסתרים, כדאי להכיר טכניקות מתקדמות יותר, כמו אלו המתוארות ב-מדריך Playwright stealth שלנו. זה מה שהופך אוסף של סקריפטים למוצר דאטה אמיתי.
