המטרה
הלקוח מפעיל אגרגטור מודעות אזורי ונזקק לפידים רצופים ומאוחדים של נדל"ן ורכב מצ'ילה, מקסיקו, פוארטו ריקו וספרד באתרים עם הגנות שונות.
הפתרון
פורטפוליו של 8 סקרייפרים ב-Python, אחד לכל אתר, כולם מייצרים נתונים לשתי סכמות JSON משותפות. הסקרייפרים מחולקים בין HTTP טהור (curl_cffi + httpx), Playwright לאתרי Angular, ו-session-based לאתרים עם login, עם 2Captcha היכן שנדרש ו-checkpoints לכל סקרייפר.
עיקרי הדברים
- 4 מדינות, 8 אתרים, 2 סכמות מאוחדות
- ארכיטקטורת שלוש שכבות לסקרייפינג
- ריצות resumable בכל סקרייפר דרך JSON checkpoints
- Angular pagination מטופל עם Playwright + JSON query payloads
האתגר
כל אחד מ-8 האתרים מחזיר שדות שונים (bedrooms vs habitaciones, שטח ב-m² או ft², מטבעות שונים) — האיחוד שביר.
build_record() בכל סקרייפר מנרמל לסכמה המשותפת: המרת יחידות, hid דטרמיניסטי, מיפוי for_sale/rent ל-listing_type, שמירת source_url ו-source.
ההגנות משתנות דרמטית: Idealista/Inmuebles24 עם fingerprinting, Chileautos/Kavak על Cloudflare, FindItPR Angular בלבד, Coches rate-limit.
שלוש שכבות — httpx היכן שאפשר, curl_cffi עם browser fingerprint היכן שחוסמים, Playwright + sessions + 2Captcha לאתרים עוינים. כל סקרייפר משתמש ברמת המינימום.
FindItPR בונה URL מ-JSON blob (busqueda) ומרנדר רק אחרי Angular hydration — HTTP סטטי מחזיר שלד ריק.
Playwright sync API, בניית busqueda תכנותית עם json.dumps + URL encoding, המתנה ל-cards, parsing עם BeautifulSoup, pagination עם delays — עד 600 תוצאות ב-12 למסך.
ריצות ארוכות (Inmuebles24, Idealista) לוקחות שעות — כישלון באמצע מאבד הכל.
כל סקרייפר כותב *_progress*.json אחרי כל דף (עמוד נוכחי, IDs שנאספו, URL אחרון). בהפעלה חוזרת מדלגים על מה שנעשה; יחד עם הוספה אטומית ל-output, השחזור חינמי.
מה נמסר
- 8 סקרייפרים ב-Python מאורגנים לפי מדינה
- שתי סכמות JSON מאוחדות
- קובצי config לכל אתר
- קובצי browser session שמורים
- requirements.txt עם גרסאות נעולות
תוצאות
4
מדינות
8
אתרים
Real estate + vehicles
קטגוריות
Resumable
שחזור
מה זה לימד אותי
- בחירת רמת הסקרייפינג המינימלית לכל אתר משפרת דרמטית throughput ואמינות
- נרמול סכמה פעם אחת בכל סקרייפר זול יותר מלנרמל אחר כך
- Resume מ-checkpoint צריך להיות default — כלום לא שורף יותר זמן מהרצה מלאה חוזרת
