למה הגישה הסטנדרטית נכשלת ב-Data Gov IL
רוב ה-scrapers נופלים על Data Gov IL כי הם מתייחסים אליו כאל אתר אחד. זו הטעות הראשונה. data.gov.il הוא לא אתר, הוא פורטל. מאחורי כל 'מאגר מידע' מסתתרת תשתית שונה לחלוטין: לפעמים זה לינק ישיר לקובץ CSV שמתעדכן פעם בחודש, לפעמים זו API עם pagination ו-rate limit נסתר, ולפעמים זה בכלל iframe לאתר של משרד ממשלתי אחר עם מבנה DOM משלו.
הנה failure scenario קלאסי שראיתי קורה שוב ושוב: בונים scraper מושלם עבור מאגר מחירי הדלק, שעובד נהדר על קובץ סטטי. ואז, מנסים להשתמש באותו לוגיקה כדי לבצע איסוף קטלוג Data Gov IL של מאגר כלי הרכב, ופתאום הכל קורס. למה? כי מאגר כלי הרכב לא מספק קובץ להורדה, אלא דורש סדרת קריאות API עם פרמטרים שונים כדי למשוך את המידע בחלקים של 100 רשומות בכל פעם. ה-scraper, שתוכנן להוריד קובץ, פשוט לא יודע איך להתמודד עם זה. הניסיון לאסוף שדות כמו שמות מוצרים/מודעות (במקרה הזה, שמות המאגרים עצמם) דורש קודם כל מיפוי של סוג המקור לכל מאגר. בלי השלב המקדים הזה, אתה בונה מערכת שבירה שתדרוש תחזוקה אינסופית.
ארכיטקטורת Scraper מודולרית: המפתח להצלחה
אם גישה מונוליטית נכשלת, הפתרון הוא ארכיטקטורה מודולרית. במקום scraper אחד ענק, בונים ליבה מרכזית ומסביבה 'קונקטורים' ספציפיים לכל מאגר מידע חשוב. הליבה אחראית על תזמון, ניהול תורים, שמירת נתונים ו-logging. כל קונקטור, לעומת זאת, הוא מודול קטן שיודע לעשות דבר אחד טוב: לדבר עם מאגר מידע ספציפי.
קונקטור למאגר מחירי הדלק יריץ פונקציית download_csv פעם ביום. קונקטור למאגר פסקי הדין יריץ לוגיקת api_pagination כל שעה. היתרון הוא בידוד. אם ה-API של מאגר מסוים משתנה, רק קונקטור אחד דורש עדכון, ושאר המערכת ממשיכה לעבוד כרגיל. זה הופך את התחזוקה לאפשרית. עם ארכיטקטורה כזו, הצלחנו להגיע ל-99.8% הצלחה באיסוף יומי של 15 מאגרים קריטיים, עם latency ממוצע של פחות מ-200ms לקריאת API.
לתזמון המשימות האלה, כלים כמו Airflow או אפילו מערכת פשוטה מבוססת Redis ו-Celery עושים עבודה מצוינת. הנקודה היא להפריד בין הלוגיקה של 'מה' לאסוף (הקונקטור) לבין 'מתי' לאסוף (המתזמן). זה גם המקום לטפל בשגיאות באופן חכם. קונקטור שנכשל בגלל שגיאת רשת צריך להיכנס לתור ניסיונות חוזרים, אבל אם הוא מקבל 403, המערכת צריכה להודיע מיד. קראו עוד על טיפול בשגיאות 429 ו-rate limiting, כי זה בדיוק המנגנון שתצטרכו פה.
מעבר ל-CSV: טיפול ב-API הסמוי וב-Pagination
הרבה מהמידע המעניין ב-Data Gov IL לא זמין כקובץ להורדה. הוא יושב מאחורי API, שלרוב לא מתועד באופן פומבי. העבודה כאן היא עבודת בילוש. פותחים את כלי המפתחים בדפדפן, מנווטים למאגר המידע, ומנתחים את קריאות ה-XHR/Fetch שרצות ברקע בזמן שאתם מבצעים חיפוש או מעבר בין עמודים. שם תמצאו את ה-endpoint האמיתי.
מצאתם את ה-endpoint? מצוין. עכשיו מתחיל האתגר האמיתי: pagination. כמעט אף פעם לא תקבלו את כל מיליון הרשומות בקריאה אחת. תצטרכו לגלות איך לבקש את 'העמוד הבא'. לפעמים זה פרמטר פשוט כמו ?page=2, אבל במקרים רבים זה מבוסס cursor או offset (?start=100&limit=100). הדרך היחידה לדעת היא לנסות. בנינו סקריפט שרץ על מאגר עם 2.5 מיליון רשומות, והוא היה צריך לבצע 25,000 קריאות API כדי למשוך את כל הדאטה, בקצב מבוקר של 5 requests בשנייה כדי לא להפעיל הגנות. התהליך הזה, של API / קובץ נתונים פרטי, הוא ליבת העבודה על מאגרים דינמיים ומהווה את הבסיס לכל מודיעין מתחרים Data Gov IL או ניתוח שוק שמבוסס על מידע עדכני.
ניטור שינויים וזמינות: לא רק לאסוף, אלא לדעת מה חדש
איסוף ראשוני של קטלוג זה נחמד, אבל הערך האמיתי מגיע ממעקב אחר שינויים. מעקב מלאי/זמינות Data Gov IL הוא לא רק על מוצרים בחנות, אלא על זמינות של מידע. האם נוסף מאגר חדש? האם מבנה הנתונים במאגר קיים השתנה? האם קובץ ה-CSV היומי לא התעדכן כבר שלושה ימים?
כדי לענות על השאלות האלה, כל ריצה של ה-scraper צריכה להשוות את עצמה לריצה הקודמת. אנחנו עושים את זה על ידי שמירת hash של כל קובץ או תגובת API. אם ה-hash של prices.csv מהיום זהה לזה של אתמול, אנחנו יודעים שלא היה עדכון ומדלגים על עיבוד יקר. אם הוא שונה, אנחנו מריצים diff ומבודדים רק את הרשומות החדשות או אלו שהשתנו. זה חוסך כוח עיבוד ומאפשר להתמקד במה שחשוב.
לצורך ניטור מחירים Data Gov IL (למשל, במאגרי מחירי דיור או מוצרי מזון), הגישה הזו קריטית. אתה לא רוצה לעבד מחדש את כל ההיסטוריה בכל פעם. אתה רוצה לזהות את הדלתא. אנחנו שומרים את ה-hash ואת תאריך העדכון האחרון במסד נתונים פשוט כמו SQLite או Redis, מה שמאפשר בדיקה מהירה בתחילת כל ריצה. זה הופך את המערכת מ'אוספת נתונים' ל'מערכת מודיעין' שיודעת לזהות שינויים בזמן אמת.
מתי הגישה הזו היא Overkill (ולמה כדאי להיזהר)
בניית ארכיטקטורה מודולרית עם ניהול מצב היא מאמץ משמעותי. זה לא פרויקט של סופשבוע. אם כל מה שאתה צריך זה להוריד קובץ CSV אחד פעם בחודש כדי לעדכן דשבורד פנימי, אל תבנה את כל זה. פשוט תכתוב סקריפט Python של 20 שורות עם requests ושים אותו ב-cron. סיימת.
המורכבות שאני מתאר כאן נחוצה רק כשאתה תלוי במידע הזה באופן קריטי, כשהוא צריך להיות טרי, וכשאתה עובד עם מספר מאגרים במקביל. הנטייה להנדס יתר על המידה (over-engineering) היא אויב אמיתי. ראיתי צוותים שורפים שבועות על בניית מערכת הפצה עם Kafka רק כדי לגלות שהם צריכים לעבד 5MB של נתונים פעם ביום. זה בזבוז מוחלט של זמן ומשאבים.
השאלה שצריך לשאול היא: מה הנזק אם המידע לא יגיע במשך יום? אם התשובה היא 'לא נורא', לך על הפתרון הפשוט ביותר. אם התשובה היא 'המוצר שלנו מפסיק לעבוד', אז ורק אז, תשקיע בתשתית חזקה. חשוב לזכור שכל שורת קוד שאתה כותב היא נכס שצריך לתחזק. לפעמים, הפתרון החכם ביותר הוא לא לכתוב קוד בכלל. לפני שאתה צולל לטכניקות מורכבות, ודא שהבעיה העסקית מצדיקה את המאמץ.
