ארכיטקטורת היעד: למה requests פשוט לא יספיק

בואו נשים את זה על השולחן: אם התוכנית שלכם היא להשתמש בספריית requests או ב-HTTP client פשוט אחר, אתם בדרך לכאב ראש. אתר מינהל הרכש הממשלתי, כמו רוב הפלטפורמות המודרניות, לא מגיש HTML סטטי טהור. חלקים ניכרים מהתוכן, במיוחד רשימות המכרזים הדינמיות, טבלאות הנתונים והפגינציה, נטענים באמצעות JavaScript בצד הלקוח. בקשת HTTP פשוטה תחזיר לכם מעטפת HTML ריקה או חלקית, בלי הנתונים שאתם באמת צריכים.

פה נכנס לתמונה headless browser. ואני אגיד את זה חד וחלק: תפסיקו עם Selenium לפרויקטים חדשים. Playwright מנצח אותו ב-2025 בכל מטריקה רלוונטית – מהירות, יציבות, וה-API שלו פשוט נקי יותר. כשמתמודדים עם משימה כמו איסוף קטלוג מלא של כל המכרזים הפתוחים, שכולל אלפי עמודים, היכולת לחכות לאלמנטים ספציפיים, לבצע אינטראקציות מורכבות וליירט בקשות רשת היא קריטית. נסו לעשות את זה עם requests ו-BeautifulSoup ותמצאו את עצמכם מריצים reverse engineering על קריאות API פנימיות, תהליך שביר שכל שינוי קטן באתר היעד ישבור לכם את הקוד. עם Playwright, אנחנו עובדים ברמת ה-DOM המרונדר, מה שמקנה לנו חסינות גבוהה יותר לשינויים קוסמטיים ב-backend. המטרה היא לאסוף שמות מוצרים/מודעות ומפרטים בצורה עקבית, וגישה מבוססת דפדפן היא הדרך היחידה להבטיח את זה בסקייל.

ניהול סשנים ו-State: המוקש האמיתי במכרזים

זה התרחיש שראיתי קורס שוב ושוב: ה-scraper עובד נהדר על 100 העמודים הראשונים, ואז מתחיל להחזיר דפים ריקים או שגיאות. הבעיה היא כמעט תמיד ניהול state. אתרי ממשלה, ומינהל הרכש הממשלתי אינו שונה, משתמשים במנגנוני סשן כדי לעקוב אחר המשתמש. זה יכול להיות דרך cookies, CSRF tokens בטפסים, או פרמטרים ב-URL. כשאתה מבצע פגינציה על פני 5,000 עמודים של מכרזים, אתה חייב לשמור על הסשן הזה חי ותקין.

הנה failure scenario קלאסי: ה-scraper טוען את עמוד 1, אוסף את הנתונים, מוצא את הקישור ל'עמוד הבא' ועוקב אחריו. הוא עושה את זה 50 פעם. בעמוד ה-51, הסשן פג תוקף בצד השרת. הבקשה הבאה נשלחת ללא סשן תקף, והשרת מחזיר אותך לדף הבית או לדף תוצאות ריק. ה-scraper שלך, שלא מצפה לזה, לא מוצא את אלמנט הנתונים הבא, קורס, או גרוע מזה – ממשיך לרוץ ומדווח על 0 תוצאות חדשות. הפתרון הוא לא רק לשמר cookies. צריך לבנות לוגיקה שמזהה מתי סשן נשבר (למשל, בדיקה של אלמנט ספציפי שחייב להיות קיים בדף תוצאות תקין) ויודעת איך לרענן אותו – לחזור לדף החיפוש הראשי, לבצע חיפוש מחדש ולהמשיך מהמקום שבו הפסיקה. זה מוסיף מורכבות, אבל זה ההבדל בין פרויקט חד-פעמי שביר לבין צינור נתונים אמין עבור מעקב מלאי/זמינות של מכרזים.

סקייל, פרוקסיז וטביעות אצבע: איך לא להיחסם ב-10,000 בקשות

ברגע שהלוגיקה הבסיסית עובדת, האתגר הבא הוא מהירות והימנעות מחסימה. איסוף של 20,000 מכרזים, כשכל אחד דורש 2-3 בקשות (רשימה + דף פרטים), מביא אותנו ל-40,000-60,000 בקשות. להריץ את זה מכתובת IP בודדת זה מתכון בטוח לחסימה. כאן נכנסים לתמונה proxies. אבל לא כל פרוקסי יעבוד. פרוקסיז של דאטה סנטר הם זולים אבל קלים לזיהוי. כשאנחנו מדברים על מודיעין מתחרים מול אתר ממשלתי, אנחנו רוצים להיראות כמו תנועה אנושית לגיטימית. זה אומר להשתמש ב-רשת פרוקסי residential איכותית ולבצע רוטציה חכמה.

אבל IP הוא רק חלק מהסיפור. טביעת האצבע של הדפדפן (browser fingerprint) חשובה לא פחות. פרטים כמו user-agent, רזולוציית מסך, פונטים מותקנים ופרמטרים של WebGL יכולים לשמש לזיהוי ה-scraper שלכם. שימוש ב-Playwright עם תוספים כמו ה-stealth plugin עוזר להסוות את העובדה שמדובר בדפדפן אוטומטי. קצב הבקשות הוא גורם נוסף. אל תנסו להריץ 50 instances במקביל. התחילו עם 3-5 עובדים (workers), עם השהיות אקראיות של 1-3 שניות בין בקשות, ונטרו את אחוזי ההצלחה. המטרה היא לא 100% מהירות, אלא 99.9% הצלחה בריצה של 8 שעות. זה דורש סבלנות וכיול, אבל זה מה שמבדיל בין חובבן למקצוען.

מתי ה-Scraping הזה הוא Overkill (ומה האלטרנטיבה)

אחרי כל מה שאמרתי, חשוב להיות כנים. לא כל פרויקט דורש בניית scraper מורכב מאפס. אם כל מה שאתם צריכים זה עדכון שבועי של 100 מכרזים מובילים בתחום ספציפי, הקמת תשתית עם Playwright, פרוקסיז, וניהול סשנים היא כנראה מוגזמת. המורכבות הזו מצדיקה את עצמה רק כשאתם צריכים נתונים מקיפים, בזמן אמת, ובסקייל גבוה. למשל, אם אתם בונים API / קובץ נתונים פנימי שמאנדקס את כל היסטוריית המכרזים לצורכי ניתוח, או מבצעים ניטור מחירים (או הצעות, במקרה הזה) על פני קטגוריות שלמות. במקרים אלה, ההשקעה הראשונית בתשתית חזקה מחזירה את עצמה במהירות ביציבות ובאמינות הנתונים.

אז מתי לא? אם הצורך הוא חד-פעמי או מצומצם מאוד, עדיף לפעמים לבצע את האיסוף ידנית או עם סקריפט פשוט יותר. לפעמים, אתרים ממשלתיים מציעים יצוא נתונים בסיסי או גישה דרך ערוצים רשמיים. שווה לבדוק את האפשרויות האלה לפני שצוללים לבניית scraper. חשוב להבין את ה-trade-off: בניית scraper מותאם אישית נותנת גמישות ושליטה מלאה, אבל דורשת תחזוקה. אם ה-scraper שלכם יפסיק לעבוד בגלל שינוי קטן באתר, אתם צריכים להיות מוכנים להקדיש זמן לתקן אותו. אם אין לכם את המשאבים לתחזוקה שוטפת, גישה מורכבת מדי עלולה להפוך לנטל. זה שיקול הנדסי, לא רק טכני.

בניית צינור נתונים: מ-HTML גולמי ל-CSV שמיש

איסוף ה-HTML הוא רק חצי מהעבודה. הנתונים הגולמיים כמעט תמיד מבולגנים, לא עקביים ומלאים ברעש. השלב הבא, והקריטי לא פחות, הוא שלב ה-Parsing והניקוי. כאן אנחנו הופכים את מרק התגים למבנה נתונים נקי ושימושי, כמו JSON או שורות ב-database. השתמשו בספריות כמו BeautifulSoup או lxml על ה-HTML ש-Playwright מחזיר לכם כדי לחלץ את המידע הספציפי. חשוב לבנות את הלוגיקה בצורה חסינה. אל תניחו שאלמנט תמיד יהיה קיים. עטפו כל חילוץ שדה בבדיקות שגיאה (try-except) והגדירו ערכי ברירת מחדל (null או N/A). זה ימנע מה-scraper כולו לקרוס בגלל מכרז אחד עם מבנה שונה.

בפרויקט שבו עבדתי על אתר דומה, מצאנו ש-5% מהמכרזים השתמשו ב-layout HTML שונה במקצת. בלי error handling ראוי, היינו מאבדים את כל ה-batch. לאחר החילוץ, בצעו נורמליזציה. הפכו תאריכים לפורמט ISO, נקו רווחים מיותרים משמות, והמירו מספרים לטיפוסים מספריים. התוצר הסופי צריך להיות קובץ CSV נקי או טבלה ב-PostgreSQL, מוכנים לניתוח או להזנה למערכות אחרות. אם אתם מתמודדים עם שגיאות רשת תכופות, כמו שגיאות 429 ו-rate limiting, חשוב שתהיה לכם אסטרטגיית retry עם exponential backoff. זה מבטיח שהסקריפט לא יוותר בקלות, אבל גם לא יפציץ את השרת. התהליך הזה הופך אוסף של דפי אינטרנט לנכס נתונים אמיתי.