למה Requests ו-BeautifulSoup לא יספיקו לכם כאן
בואו נשים את זה על השולחן: ניסיון להריץ requests.get() על עמוד של שמורת טבע ספציפית יחזיר לכם מעטפת HTML ריקה מתוכן משמעותי. כל המידע שאתם מחפשים, במיוחד עבור מעקב מלאי/זמינות רשות הטבע והגנים, נטען אסינכרונית אחרי שהדף הראשוני עולה. המידע על כמות הכרטיסים הפנויים לתאריך מסוים, או האם האתר פתוח במזג אוויר מסוים, מגיע דרך קריאות API פנימיות (XHR/Fetch) שהדפדפן מריץ ברקע.
הגישה הזו הופכת את ה-scraper התמים שמבוסס על HTTP בלבד לחסר תועלת. הוא פשוט לא רואה את הדאטה. הפתרון היחיד שעובד באופן עקבי הוא שימוש ב-headless browser. תשכחו מ-Selenium; ב-2025, Playwright הוא הכלי הנכון לעבודה. הוא מהיר יותר, ה-API שלו נקי יותר, והיכולת שלו ליירט ולנתח תעבורת רשת מובנית וחזקה. במקום לנתח HTML, אנחנו יכולים להורות לדפדפן לבצע את האינטראקציות, להמתין לקריאת ה-API הרלוונטית, ולחלץ את ה-JSON הנקי ישירות מהתשובה. זה לא רק אמין יותר, זה גם חוסך לנו את הכאב ראש של התמודדות עם שינויים ב-CSS selectors, שהם הרבה יותר תנודתיים ממבנה ה-API. כשמדובר באיסוף קטלוג מלא, כולל כל המסלולים והאתרים, זו הדרך היחידה להבטיח שלא פספסתם מידע שנטען מאוחר.
ארכיטקטורת ה-Scraper הנכונה ל-parks.org.il
אז איך בונים את זה נכון? התהליך מתחיל בהבנה שאנחנו לא סתם 'גורדים' דפים, אלא מדמים התנהגות משתמש. הפלואו הטיפוסי נראה כך:
- אתחול: מרימים instance של Playwright, רצוי עם פרופיל דפדפן נקי לכל ריצה כדי למנוע זליגת state בין סשנים.
- ניווט ראשי: מתחילים מעמוד 'כל האתרים' או קטגוריה אזורית. מכאן, אנחנו אוספים את רשימת ה-URLs של כל האתרים והשמורות. זה השלב הראשוני של איסוף קטלוג רשות הטבע והגנים. שלב זה קריטי כדי להבטיח כיסוי מלא של כל הנכסים הדיגיטליים.
- עיבוד דף יחיד: לכל URL, פותחים עמוד חדש. כאן אנחנו לא ממהרים לגרד. אנחנו צריכים להגדיר
waitחכם. במקום להמתין ל-loadאוdomcontentloaded, אנחנו ממתינים לאירוע ספציפי: הופעה של אלמנט מסוים (כמו לוח השנה לבחירת תאריך) או, טוב יותר, סיום של קריאת רשת ספציפית שמכילה את נתוני הזמינות. - חילוץ נתונים: לאחר שהדף טעון במלואו, כולל המידע הדינמי, אנחנו מחלצים את השדות הרלוונטיים:
שמות מוצרים/מודעות(במקרה הזה, שמות האתרים), שעות פתיחה, הנחיות מיוחדות, וכמובן, את מצב הזמינות ההתחלתי. אם אתם חדשים בתחום, כדאי לקרוא את המדריך שלנו ל-Playwright stealth כדי להתחיל נכון ולהימנע מזיהוי מהיר.
איפה הכל נופל: אינטראקציה עם לוח השנה הדינמי
זה ה-failure mode הקלאסי באתרים כאלה. מהנדסים מצליחים לטעון את הדף, אבל נכשלים באינטראקציה עם רכיבים מורכבים כמו לוח שנה לבחירת תאריך. הבעיה היא שהרכיב הזה מנהל state פנימי. קליק על 'החודש הבא' לא סתם משנה את ה-HTML; הוא מפעיל קריאת API ברקע כדי להביא את הזמינות לחודש החדש, ואז מעדכן את ה-DOM.
רוב הניסיונות נכשלים כי הם לא ממתינים לסיום התהליך הזה. ה-scraper מבצע קליק, ומייד מנסה לקרוא את הנתונים, אבל הוא קורא את המצב הישן, לפני שהתשובה מהשרת חזרה ועיבדה את העדכון. ראיתי את זה קורה עשרות פעמים. ה-scraper מדווח על 0% הצלחה באיסוף זמינות עתידית, למרות שהקוד נראה 'תקין'. הפתרון הוא סנכרון מלא. אחרי כל אינטראקציה שמשנה state (כמו קליק על תאריך או חץ), חובה להגדיר page.waitForResponse() שמחכה לתשובת ה-API הספציפית שמעדכנת את לוח השנה. רק אחרי שהתשובה הזו מתקבלת, אפשר בבטחה לחלץ את הנתונים החדשים. כל גישה אחרת היא הימור שיוביל ל-race conditions ולדאטה לא אמין.
קנה מידה, פרוקסיז, וקצב בקשות
כשיש לנו scraper שעובד לדף בודד, האתגר הבא הוא קנה מידה. לאתר רשות הטבע והגנים יש מאות אתרים ומסלולים. אם נרצה לבדוק זמינות ל-90 יום קדימה עבור כל אחד מהם, אנחנו מדברים על עשרות אלפי 'דפים' וירטואליים. הרצה סדרתית של תהליך כזה תיקח שעות. הפתרון הוא הקבלה (parallelization), אבל כאן צריך להיזהר.
הפעלת 50 instances של Chrome במקביל מאותה כתובת IP היא דרך בטוחה להיחסם. האתר יזהה את הפעילות החריגה ויציג CAPTCHA או יחסום את ה-IP לחלוטין. הדרך הנכונה היא להשתמש ב-proxy rotation. לכל worker או קבוצת workers, הקצו IP שונה. זה מדמה תנועה ממספר משתמשים שונים. ניהול נכון של פרוקסי הוא קריטי, ובמיוחד הבחירה בין סוגים שונים. תוכלו לקרוא עוד על איך לבחור פרוקסי residential כדי להבין את היתרונות והחסרונות. מבחינת קצב, אני ממליץ להתחיל לאט, עם עיכוב של 2-3 שניות בין בקשות מאותו IP, ולעלות בהדרגה תוך ניטור אחוזי ההצלחה. אם אחוזי השגיאה (כמו 429 או 403) עולים מעל 2-3%, זה סימן להאט. ניהול נכון של קצב יאפשר לכם להגיע לאחוזי הצלחה של מעל 98% לאורך זמן.
מתי Scraping הוא לא הפתרון הנכון (כן, יש מקרים כאלה)
חשוב להיות כנים. למרות שאנחנו יכולים טכנית לגרד כמעט כל דבר, זה לא תמיד הפתרון היעיל ביותר. אם כל מה שאתם צריכים זה רשימה סטטית של כל שמורות הטבע והגנים הלאומיים, בניית scraper מורכב מבוסס Playwright היא overkill. סביר להניח שניתן למצוא את המידע הזה במקורות אחרים, אולי אפילו בקובץ שמישהו כבר הכין. המורכבות של תחזוקת scraper שצריך להתמודד עם שינויי frontend היא לא אפסית.
המקום שבו scraping מצטיין הוא באיסוף דאטה דינמי ומשתנה בתדירות גבוהה. למשל, אם אתם בונים שירות שמתריע על כרטיסים שמתפנים לאתר פופולרי, או מבצעים ניטור מחירים רשות הטבע והגנים על פעילויות מיוחדות (כמו סיורי לילה) — כאן אין תחליף ל-scraper. זהו Use Case קלאסי של API / קובץ נתונים רשות הטבע והגנים שאתם יוצרים בעצמכם כי אין אחד רשמי. גם עבור מודיעין מתחרים רשות הטבע והגנים (למשל, אם אתם סוכנות תיירות שרוצה להבין את היצע הפעילויות), היכולת לקבל תמונה עדכנית היא קריטית. אבל אם הדרישה היא חד-פעמית והמידע סטטי יחסית, כדאי לשקול אם המאמץ ההנדסי מצדיק את התוצאה. לפעמים, עבודה ידנית של שעה חוסכת שבוע של פיתוח ותחזוקה.
