تضم Zillow أكثر من 160 مليون سجل عقاري في الولايات المتحدة، واستخراج هذه البيانات على نطاق واسع يُعد من أكثر المهام المطلوبة — والأكثر صداعًا — في شغل بيانات العقارات. إذا سبق لك أن حاولت استخراج بيانات Zillow وانتهى بك الأمر على صفحة CAPTCHA بدل بيانات العقار، فأنت لست وحدك.
قضيت وقتًا طويلًا في البحث وتجربة طرق مختلفة لاستخراج بيانات Zillow — سواء باستخدام Python أو عبر أدوات no-code طورناها في Thunderbit. هذا الدليل يجمع المسارين معًا. سواء كنت تريد شرحًا كاملًا باستخدام Python مع استراتيجيات لتجاوز الحظر، أو كنت تحتاج فقط إلى 200 إعلان في جدول بيانات قبل الغداء، فستجد هنا ما يفيدك. سنشرح لماذا تهم بيانات Zillow، وكيف يبدو هيكل الموقع من الداخل، ودروسًا عملية خطوة بخطوة باستخدام Python، والأسباب الدقيقة لتعطل أدوات الاستخراج، وكيفية أتمتة عمليات الاستخراج المتكررة لمتابعة الأسعار.
لماذا أصلاً نحتاج إلى استخراج بيانات Zillow؟
Zillow هو أكبر مستودع لبيانات العقارات السكنية في الولايات المتحدة. يستقبل 210 ملايين زيارة شهرية ويعرض نحو 750,000+ إعلانًا نشطًا للبيع، بالإضافة إلى 1.9 مليون إعلان إيجار. تستحوذ المنصة على أكثر من 50% من إجمالي حركة المرور الخاصة ببوابات العقارات في الولايات المتحدة — أي أكثر من ضعف أقرب منافس.

قبل الدخول في كود Python، من المهم أن تعرف أن الاستخراج عبر Python ليس الخيار الوحيد، واختيار الطريقة غير المناسبة قد يضيع ساعات من وقتك. أدوات Python مثل httpx وBeautifulSoup تناسب من لديهم مستوى متوسط، لكنها تتطلب التعامل اليدوي مع الـ headers والـ proxies، وتعمل بسرعة متوسطة (1–3 ثوانٍ لكل صفحة)، وتحتاج صيانة متكررة، رغم أنها مجانية؛ أما Selenium أو Playwright فهما أفضل في التعامل مع الحماية ضد الروبوتات عبر عرض JavaScript، لكنهما أبطأ (5–15 ثانية لكل صفحة) وما زالا يحتاجان إلى صيانة عالية؛ وخدمات scraping API مثل ScraperAPI أو ScrapFly أسرع وتأتي بدعم مدمج لمقاومة الحظر مع جهد صيانة متوسط، لكنها تكلف 30–599 دولارًا شهريًا؛ بينما الواجهة الرسمية لـ Zillow عبر Bridge Interactive سريعة ومنخفضة الصيانة لكنها محدودة وتكلف نحو 500 دولار شهريًا؛ وأدوات no-code مثل Thunderbit سهلة للمبتدئين وسريعة ولا تحتاج صيانة بفضل التكيّف بالذكاء الاصطناعي، وغالبًا ما تقدم نموذجًا مجانيًا مع مزايا مدفوعة.
أما من ناحية توفير الوقت، فالفارق كبير جدًا. البحث اليدوي عبر أكثر من 50 رمزًا بريديًا قد يستهلك 15–20 ساعة أسبوعيًا. أما الاستخراج الآلي فينجز المهمة نفسها خلال دقائق — أي تقليل الوقت المستغرق بنسبة 99.7%.
كل طرق استخراج بيانات Zillow: Python مقابل API مقابل no-code (مقارنة)
قبل أن نبدأ بكود Python، اعلم أن "استخراج Zillow باستخدام Python" ليس الخيار الوحيد. اختيار الطريقة الخاطئة يضيّع ساعات. إليك مقارنة مباشرة لتختار الأنسب لك:
| الطريقة | مستوى المهارة | التعامل مع الحظر | السرعة | الصيانة | التكلفة |
|---|---|---|---|---|---|
| Python + httpx/BeautifulSoup | متوسط | يدوي (headers, proxies) | متوسطة (1–3 ث/صفحة) | عالية (قد تتعطل المحددات) | مجانية |
| Python + Selenium/Playwright | متوسط | أفضل (يعرض JS) | بطيئة (5–15 ث/صفحة) | عالية | مجانية |
| scraping API (ScraperAPI, ScrapFly) | متوسط | مدمج | سريعة | متوسطة | 30–599 دولار/شهر |
| Zillow Official API (Bridge Interactive) | مبتدئ–متوسط | غير منطبق | سريعة | منخفضة | حوالي 500 دولار/شهر، وصول محدود |
| أداة no-code (Thunderbit) | مبتدئ | مدمج (الذكاء الاصطناعي يتكيّف) | سريعة | معدومة (الذكاء الاصطناعي يقرأ الصفحة من جديد) | Freemium |
إذا كنت تحتاج البيانات الآن من دون كتابة أي كود، فابدأ بـ Thunderbit. أما إذا كنت تريد فهم البنية التقنية أو تحتاج تخصيصًا كاملًا، فتابع قراءة الشرح العملي باستخدام Python.
الطريقة الأسرع خلال دقيقتين: استخراج Zillow باستخدام Thunderbit (من دون كود)
قبل التعمق في Python، هذه هي الطريقة المناسبة لمن يريد بيانات Zillow بسرعة — من دون إعداد Python، ومن دون ضبط proxies، ومن دون صيانة المحددات. بنينا هذا العمل في Thunderbit خصيصًا للحصول على بيانات عقارية منظمة من دون عبء هندسي.
مستوى الصعوبة: مبتدئ
الوقت المطلوب: حوالي دقيقتين
ما تحتاجه: متصفح Chrome، وThunderbit Chrome Extension (تعمل الخطة المجانية)
الخطوة 1: ثبّت Thunderbit وافتح Zillow
ثبّت إضافة Thunderbit من متجر Chrome Web Store. ثم انتقل إلى صفحة نتائج البحث في Zillow — على سبيل المثال، ابحث عن منازل في Houston, TX.
الخطوة 2: انقر على "AI Suggest Fields"
افتح الشريط الجانبي لـ Thunderbit ثم انقر على "AI Suggest Fields". سيقرأ الذكاء الاصطناعي الصفحة ويقترح الأعمدة تلقائيًا: السعر، العنوان، عدد غرف النوم، عدد الحمامات، المساحة، Zestimate، رابط الإعلان، والمزيد. في اختباري، غالبًا ما يكتشف أكثر من 20 حقلًا من دون أي إعداد يدوي.
الخطوة 3: انقر على "Scrape"
اضغط زر Scrape. ستظهر البيانات في جدول منظم داخل الإضافة. يتعامل Thunderbit تلقائيًا مع تقسيم الصفحات في Zillow — سواء كان ذلك عبر النقر أو التمرير اللانهائي.
الخطوة 4: أغنِ البيانات عبر Scrape Subpages
هل تريد بيانات من صفحة التفاصيل مثل سجل الضرائب، تقييم المدارس، أو سجل الأسعار؟ استخدم "Scrape Subpages" لإثراء الجدول. سيتنقل Thunderbit إلى كل رابط إعلان ويستخرج الحقول الإضافية — من دون أي كود إضافي.
الخطوة 5: التصدير
صدّر البيانات إلى Google Sheets أو Excel أو Airtable أو Notion. التصدير مجاني.
لماذا يعمل Thunderbit جيدًا مع Zillow
الميزة الحقيقية هنا هي المرونة. يقرأ Thunderbit بنية الصفحة من جديد في كل مرة تقوم فيها بالاستخراج. وعندما يغيّر Zillow تصميمه — وهو أمر يحدث كثيرًا — لا تعتمد الأداة على CSS selectors هشة تحتاج إلى إصلاح. الذكاء الاصطناعي يتكيّف تلقائيًا. وهذا يحل فعلًا مشكلة "الهشاشة المتأصلة" في أدوات الاستخراج البرمجية التي تربك الكثير من المستخدمين.
ما البيانات التي يمكنك استخراجها من Zillow؟ (أكثر من 20 حقلًا)
معظم الأدلة تكتفي بالسعر والعنوان ثم تتوقف. لكن إعلانات Zillow تحتوي فعليًا على بيانات أكثر بكثير مما يتوقعه الناس — إليك جدولًا مرجعيًا:
| الحقل | مكان العثور عليه | صعوبة الاستخراج |
|---|---|---|
| سعر الإدراج | نتائج البحث + صفحة التفاصيل | سهل |
| العنوان / الرمز البريدي | نتائج البحث + صفحة التفاصيل | سهل |
| Zestimate | نتائج البحث + صفحة التفاصيل | سهل |
| سجل الأسعار (كل حدث) | صفحة التفاصيل | صعب (JSON متداخل) |
| سجل الضرائب | صفحة التفاصيل | صعب (JSON متداخل) |
| عدد غرف النوم / الحمامات / المساحة | نتائج البحث + صفحة التفاصيل | سهل |
| سنة البناء | صفحة التفاصيل | سهل |
| رسوم HOA | صفحة التفاصيل | متوسط |
| Walk Score / Transit Score | صفحة التفاصيل (iframe) | صعب (يتطلب عرض JavaScript) |
| تقييم المدارس | صفحة التفاصيل | متوسط |
| مساحة الأرض | صفحة التفاصيل | سهل |
| عدد الأيام على Zillow | نتائج البحث | سهل |
| وكيل الإعلان / شركة الوساطة | نتائج البحث + صفحة التفاصيل | متوسط |
| رقم MLS | صفحة التفاصيل | سهل |
| نوع العقار | نتائج البحث + صفحة التفاصيل | سهل |
| خط العرض / خط الطول | JSON في __NEXT_DATA__ | متوسط |
| نص الوصف | صفحة التفاصيل | سهل |
| روابط الصور | نتائج البحث + صفحة التفاصيل | متوسط |
| Rent Zestimate | صفحة التفاصيل | متوسط |
| المبيعات المماثلة القريبة | صفحة التفاصيل | صعب |
الحقول "الصعبة" — مثل سجل الأسعار، وسجل الضرائب، والمبيعات المماثلة — توجد داخل JSON متداخل في صفحات التفاصيل. يوضح القسم التالي من Python كيفية استخراجها بدقة. وإذا كنت تفضل تجنب الكود، فإن AI Suggest Fields في Thunderbit يكتشف معظم هذه الأعمدة تلقائيًا، بينما يقوم Scrape Subpages بسحب حقول صفحات التفاصيل بشكل آلي.
إعداد بيئة Python لاستخراج Zillow
مستوى الصعوبة: متوسط
الوقت المطلوب: حوالي 5 دقائق للإعداد، و30 دقيقة للشرح الكامل
ما تحتاجه: Python 3.8+، متصفح Chrome (لفحص الصفحات)، ومحرر نصوص أو IDE
ثبّت المكتبات المطلوبة:
pip install httpx beautifulsoup4 pandas lxml
وهذه وظيفة كل مكتبة:
- httpx — عميل HTTP أسرع من
requestsويدعم async - beautifulsoup4 + lxml — لتحليل HTML
- pandas — لتصدير البيانات إلى CSV/Excel
- اختياريًا: selenium أو playwright إذا كنت تحتاج إلى عرض صفحات تعتمد كثيرًا على JavaScript
فهم بنية صفحة Zillow قبل البدء بالاستخراج

هذه أهم نقطة يجب فهمها قبل كتابة أي كود. Zillow هو تطبيق Next.js — وهذا مؤكد من خلال منشورات هندسية لموظفين في Zillow. هذا يعني أن معظم البيانات التي تريدها ليست داخل عناصر HTML الظاهرة، بل مضمنة داخل كتلة JSON في <script id="__NEXT_DATA__">.
افتح أي صفحة عقار في Zillow، ثم اضغط F12، واذهب إلى Elements، وابحث عن __NEXT_DATA__. ستجد كائن JSON ضخمًا يحتوي على كل بيانات الإعلان — الأسعار، الإحداثيات، تفاصيل العقار، سجل الأسعار، السجلات الضريبية، تقييم المدارس، والمزيد.
لماذا هذا مهم؟ لأن أسماء CSS classes في Zillow مُولّدة بشكل تجزئي (hashed) عبر styled-components وتتغير مع كل عملية نشر. فمثلًا class مثل StyledPropertyCardHomeDetailsList-c11n-8-109-3__sc-1j0som5-0 قد يحمل hash مختلفًا تمامًا الأسبوع القادم. أي scraper يعتمد على CSS selectors سيتعطل باستمرار.
أما أسلوب استخراج JSON من __NEXT_DATA__ فهو أكثر ثباتًا لأنه لا يعتمد أصلًا على بنية HTML.
مسارات JSON الأساسية لنتائج البحث:
| المسار | المحتوى |
|---|---|
props.pageProps.searchPageState.cat1.searchResults.listResults | مصفوفة نتائج البحث |
props.pageProps.searchPageState.cat1.searchResults.mapResults | نتائج عرض الخريطة |
props.pageProps.searchPageState.cat1.searchList.totalPages | إجمالي الصفحات المتاحة |
أما في صفحات التفاصيل، فبعضها يستخدم __NEXT_DATA__ وبعضها يعتمد على وسم script بديل اسمه hdpApolloPreloadedData. الكود التالي يتعامل مع الحالتين.
خطوة بخطوة: كيفية استخراج Zillow باستخدام Python
الخطوة 1: إعداد HTTP Headers لتجنب الحظر الفوري
إرسال httpx.get() عادي إلى Zillow سيعيد صفحة CAPTCHA بدل بيانات الإعلانات. تستخدم Zillow نظام PerimeterX (HUMAN Security) إلى جانب Cloudflare — وكلاهما مُصنَّف 8/10 من حيث الصعوبة في اختبارات scraping. يفحص النظام بصمة TLS، والـ headers، وسمعة الـ IP.
إليك الحد الأدنى من الـ headers التي تعمل حتى عام 2025:
import httpx
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
"(KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,"
"image/avif,image/webp,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.9",
"Accept-Encoding": "gzip, deflate, br",
"Sec-Ch-Ua": '"Chromium";v="124", "Google Chrome";v="124", "Not-A.Brand";v="99"',
"Sec-Ch-Ua-Platform": '"Windows"',
"Sec-Fetch-Dest": "document",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Site": "none",
"Sec-Fetch-User": "?1",
"Upgrade-Insecure-Requests": "1",
}
تُعد ترويسات Sec-Ch-Ua بالغة الأهمية. كثير من الشروحات تتجاهلها — وهذا بالضبط سبب فشل الأكواد في مواجهة PerimeterX.
الخطوة 2: استخراج نتائج البحث من Zillow
روابط بحث Zillow تتبع نمطًا متوقعًا. بالنسبة إلى Houston, TX:
- الصفحة 1:
https://www.zillow.com/houston-tx/ - الصفحة 2:
https://www.zillow.com/houston-tx/2_p/ - الصفحة 3:
https://www.zillow.com/houston-tx/3_p/
كل صفحة تحتوي تقريبًا على 41 إعلانًا. Zillow يحدد النتائج بحد أقصى 20 صفحة (~820 إعلانًا). وللحصول على مجموعات بيانات أكبر، ستحتاج إلى تقسيم البحث جغرافيًا (سنشرح ذلك لاحقًا).
إليك الكود لاستخراج نتائج البحث عبر سحب البيانات من JSON الخاص بـ __NEXT_DATA__:
from bs4 import BeautifulSoup
import json
import time
import random
def scrape_zillow_search(url):
"""استخراج بيانات الإعلانات من صفحة نتائج بحث Zillow."""
response = httpx.get(url, headers=headers, timeout=15)
if response.status_code != 200:
print(f"Got status {response.status_code} for {url}")
return []
soup = BeautifulSoup(response.text, "lxml")
script_tag = soup.find("script", {"id": "__NEXT_DATA__"})
if not script_tag:
print("No __NEXT_DATA__ found — likely blocked by CAPTCHA")
return []
next_data = json.loads(script_tag.string)
try:
results = (
next_data["props"]["pageProps"]["searchPageState"]
["cat1"]["searchResults"]["listResults"]
)
except KeyError:
print("Unexpected JSON structure — Zillow may have changed its format")
return []
listings = []
for item in results:
listing = {
"zpid": item.get("zpid"),
"address": item.get("addressStreet"),
"city": item.get("addressCity"),
"state": item.get("addressState"),
"zipcode": item.get("addressZipcode"),
"price": item.get("unformattedPrice") or item.get("price"),
"beds": item.get("beds"),
"baths": item.get("baths"),
"sqft": item.get("area"),
"zestimate": item.get("zestimate"),
"days_on_zillow": item.get("daysOnZillow"),
"listing_url": item.get("detailUrl"),
"img_src": item.get("imgSrc"),
"property_type": item.get("hdpData", {}).get("homeInfo", {}).get("homeType"),
"latitude": item.get("latLong", {}).get("latitude"),
"longitude": item.get("latLong", {}).get("longitude"),
}
listings.append(listing)
return listings
للاستخراج من عدة صفحات، استخدم حلقة مع فواصل زمنية:
all_listings = []
base_url = "https://www.zillow.com/houston-tx/"
for page in range(1, 6): # أول 5 صفحات
url = base_url if page == 1 else f"{base_url}{page}_p/"
print(f"Scraping page {page}...")
page_listings = scrape_zillow_search(url)
all_listings.extend(page_listings)
# تأخير عشوائي بين 3 و7 ثوانٍ
delay = random.uniform(3, 7)
time.sleep(delay)
print(f"Total listings scraped: {len(all_listings)}")
يُفترض أن ترى بيانات منظّمة تتراكم داخل all_listings. إذا حصلت على نتائج فارغة، فراجع قسم "لماذا تتعطل أدوات الاستخراج" أدناه.
الخطوة 3: استخراج صفحات تفاصيل عقارات Zillow
نتائج البحث تعطيك الأساسيات. أما صفحات التفاصيل فتحتوي على البيانات الأعمق: سجل الأسعار، سجل الضرائب، تقييم المدارس، معلومات الوكيل، ووصف العقار. كل رابط إعلان من الخطوة 2 يشير إلى صفحة تفاصيل.
تستخدم صفحات التفاصيل في Zillow تنسيقين محتملين للبيانات. إليك كودًا يتعامل مع الاثنين:
def scrape_zillow_detail(url):
"""استخراج بيانات تفصيلية لعقار من صفحة Zillow."""
response = httpx.get(url, headers=headers, timeout=15)
if response.status_code != 200:
return None
soup = BeautifulSoup(response.text, "lxml")
# جرّب __NEXT_DATA__ أولًا (الأكثر شيوعًا)
script_tag = soup.find("script", {"id": "__NEXT_DATA__"})
if script_tag:
next_data = json.loads(script_tag.string)
try:
cache_str = next_data["props"]["pageProps"]["componentProps"]["gdpClientCache"]
cache = json.loads(cache_str)
first_key = next(iter(cache))
prop = cache[first_key]["property"]
return extract_property_fields(prop)
except (KeyError, StopIteration):
pass
# بديل: hdpApolloPreloadedData
apollo_tag = soup.find("script", {"id": "hdpApolloPreloadedData"})
if apollo_tag:
raw = json.loads(apollo_tag.string)
api_cache = json.loads(raw["apiCache"])
for key, value in api_cache.items():
if "ForSale" in key or "property" in str(value)[:100]:
prop = value.get("property", value)
return extract_property_fields(prop)
return None
def extract_property_fields(prop):
"""استخراج الحقول المنظمة من كائن JSON خاص بعقار Zillow."""
return {
"zpid": prop.get("zpid"),
"zestimate": prop.get("zestimate"),
"rent_zestimate": prop.get("rentZestimate"),
"description": prop.get("description"),
"year_built": prop.get("yearBuilt"),
"lot_size": prop.get("lotSize"),
"hoa_fee": prop.get("monthlyHoaFee"),
"mls_id": prop.get("mlsid"),
"broker_name": prop.get("brokerName") or prop.get("attributionInfo", {}).get("brokerName"),
"price_history": [
{
"date": event.get("date"),
"event": event.get("event"),
"price": event.get("price"),
}
for event in prop.get("priceHistory", [])
],
"tax_history": [
{
"year": record.get("time"),
"tax_paid": record.get("taxPaid"),
"value": record.get("value"),
}
for record in prop.get("taxHistory", [])
],
"schools": [
{
"name": school.get("name"),
"rating": school.get("rating"),
"distance": school.get("distance"),
}
for school in prop.get("schools", [])
],
}
مرّر على روابط الإعلانات مع فواصل زمنية:
detail_data = []
for listing in all_listings[:10]: # ابدأ بـ 10 للاختبار
detail_url = listing.get("listing_url")
if not detail_url:
continue
if not detail_url.startswith("http"):
detail_url = f"https://www.zillow.com{detail_url}"
print(f"Scraping detail: {detail_url}")
detail = scrape_zillow_detail(detail_url)
if detail:
detail_data.append({**listing, **detail})
time.sleep(random.uniform(3, 8))
بعد هذه الخطوة، يجب أن تحصل على قائمة من القواميس تحتوي على بيانات مستوى البحث وبيانات مستوى التفاصيل لكل عقار.
الخطوة 4: التعامل مع تقسيم الصفحات لاستخراج صفحات متعددة
في المناطق التي تحتوي على أكثر من 820 إعلانًا (حد الـ 20 صفحة)، ستحتاج إلى تقسيم المنطقة جغرافيًا. تقبل واجهة Zillow الداخلية المعامل mapBounds. الاستراتيجية هنا: قسم الخريطة إلى أرباع واستخراج كل جزء بشكل منفصل.
def split_bounds(bounds):
"""تقسيم حدود الخريطة إلى 4 أرباع."""
mid_lat = (bounds["north"] + bounds["south"]) / 2
mid_lng = (bounds["east"] + bounds["west"]) / 2
return [
{"north": bounds["north"], "south": mid_lat, "east": bounds["east"], "west": mid_lng},
{"north": bounds["north"], "south": mid_lat, "east": mid_lng, "west": bounds["west"]},
{"north": mid_lat, "south": bounds["south"], "east": bounds["east"], "west": mid_lng},
{"north": mid_lat, "south": bounds["south"], "east": mid_lng, "west": bounds["west"]},
]
في معظم الاستخدامات — مثل متابعة 50–200 إعلان في منطقة محددة — يكون ترقيم الصفحات القياسي في الرابط كافيًا. أما أسلوب تقسيم الأرباع فهو مناسب للاستخراج على مستوى المدينة أو الولاية.
الخطوة 5: تصدير بيانات Zillow التي استخرجتها
احفظ كل شيء إلى CSV باستخدام pandas:
import pandas as pd
df = pd.DataFrame(detail_data)
df.to_csv("zillow_houston_listings.csv", index=False)
print(f"Exported {len(df)} listings to zillow_houston_listings.csv")
وللتصدير إلى JSON:
with open("zillow_houston_listings.json", "w") as f:
json.dump(detail_data, f, indent=2)
إذا أردت تجاوز خطوة التصدير بالكامل، فإن Thunderbit يصدّر إلى Google Sheets وAirtable وNotion مجانًا — وهذا مفيد إذا كنت تريد البيانات بصيغة تعاونية فورًا.
لماذا تتعطل أدوات استخراج Zillow (وكيف تبني أدوات أكثر صمودًا)
هذا هو دليل النجاة.
من واقع تجربتي، تتعطل أدوات الاستخراج على Zillow لثلاثة أسباب محددة — ولكل منها حل واضح.
PerimeterX وCAPTCHA: لماذا تعود طلباتك ببيانات فارغة
يفحص تكامل PerimeterX في Zillow عدة إشارات في الوقت نفسه: بصمة TLS، وHTTP headers، وسمعة IP، وأنماط الطلبات. وعندما يكتشف الأتمتة، يعرض صفحة CAPTCHA من نوع "Press & Hold" بدلًا من بيانات العقار.
سيناريو الفشل بالضبط: ترسل طلبًا باستخدام headers الافتراضية في Python. يحتوي HTML الناتج على سكربتات تحدي PerimeterX بدلًا من بيانات العقار — وعندها لا يجد BeautifulSoup أي وسم __NEXT_DATA__.
الحل: استخدم الـ headers الكاملة المشابهة للمتصفح من الخطوة 1. وإذا كنت ترسل أكثر من بضع عشرات من الطلبات، فستحتاج أيضًا إلى تدوير proxies (سنغطي ذلك لاحقًا). وللاستخراج الثقيل، فكّر في مكتبة مثل curl_cffi مع impersonate="chrome" — فهي عميل HTTP الوحيد في Python تقريبًا القادر على محاكاة بصمة TLS حقيقية شبيهة بـ Chrome.
محددات CSS الديناميكية: لماذا تعيد BeautifulSoup قيمة None
إذا كنت تستخدم CSS selectors مثل .list-card-price أو أسماء classes تحتوي على hashes، فسيتعطل scraper كلما نشرت Zillow تحديثًا جديدًا.
تستخدم Zillow styled-components، التي تولّد أسماء class مثل StyledPropertyCardHomeDetailsList-c11n-8-109-3__sc-1j0som5-0. والجزء الخاص بالـ hash يتغير مع كل build.
الحل: لا تستخدم CSS selectors أصلًا. استخرج البيانات من كتلة JSON الخاصة بـ __NEXT_DATA__ كما في الكود أعلاه. هذا الأسلوب مستقر منذ سنوات لأنه لا يعتمد على HTML markup، الذي يتغير بسرعة أكبر بكثير.
إذا اضطررت إلى تحليل HTML، فابحث عن سمات data-test مثل data-test="property-card" أو استخدم مطابقة جزئية لأسماء الكلاسات مثل [class*="PropertyCard"]. لكن استخراج JSON يظل الطريق الأكثر موثوقية.
تدوير proxies وExponential Backoff: كود يصمد أمام حظر الـ IP
يتم إدراج عناوين IP الخاصة بمراكز البيانات في قائمة الحظر فورًا من قبل Zillow. لذلك تحتاج إلى residential proxies للوصول الموثوق. المعدل الآمن: طلب واحد كل 3–8 ثوانٍ لكل IP، مع البقاء تحت حوالي 500 طلب في الساعة.
إليك دالة إعادة المحاولة مع exponential backoff وjitter:
import random
import time
def backoff_with_jitter(attempt, base_delay=2, max_delay=60):
"""Exponential backoff بأسلوب AWS مع jitter كامل."""
delay = min(max_delay, base_delay * (2 ** attempt))
return random.uniform(0, delay)
def fetch_with_retry(url, max_retries=5):
for attempt in range(max_retries):
try:
response = httpx.get(url, headers=headers, timeout=15)
if response.status_code == 200:
return response
if response.status_code in (403, 429):
delay = backoff_with_jitter(attempt, base_delay=5)
print(f"Blocked ({response.status_code}). Retrying in {delay:.1f}s...")
time.sleep(delay)
continue
except Exception as e:
if attempt == max_retries - 1:
raise
time.sleep(backoff_with_jitter(attempt))
return None
وهنا pool بسيط لتدوير proxies:
class ProxyPool:
def __init__(self, proxies):
self.proxies = proxies
self.index = 0
self.failures = {}
def get_next(self):
proxy = self.proxies[self.index % len(self.proxies)]
self.index += 1
return {"http://": proxy, "https://": proxy}
def report_failure(self, proxy):
self.failures[proxy] = self.failures.get(proxy, 0) + 1
if self.failures[proxy] > 3:
self.proxies.remove(proxy)
# الاستخدام:
pool = ProxyPool(proxies=[
"http://user:pass@residential1.example.com:8080",
"http://user:pass@residential2.example.com:8080",
])
بالنسبة إلى مزوّدي الـ proxy، فإن DataImpulse يقدم residential proxies بسعر يقارب 1 دولار/GB (وهو الخيار الأرخص)، بينما IPRoyal وSmartproxy يُعدّان خيارين جيدين في الفئة المتوسطة بسعر 4–7 دولارات/GB.
البديل منخفض الصيانة تمامًا
إذا كنت تستخراج بيانات Zillow بشكل متكرر وتعبت من إصلاح المحددات المعطلة أو إدارة مجموعات الـ proxies، فإن Thunderbit يقرأ بنية الصفحة من جديد في كل عملية استخراج. لا توجد selectors تحتاج إلى صيانة، ولا إعدادات proxy. إنه يحل فعلًا مشكلة الهشاشة التي تجعل الأدوات البرمجية مصدر صداع متكرر.
أتمتة استخراج Zillow: الجدولة ومتابعة تغيّر الأسعار
كل مستثمر عقاري تحدثت معه يريد هذا، ولا يغطيه أي دليل آخر عن استخراج Zillow: عمليات استخراج متكررة وآلية لمتابعة الأسعار.
لمستخدمي Python: مهام Cron واكتشاف تغيّر الأسعار
أعدّ مهمة cron تشغّل scraper أسبوعيًا وتعلّم تغيّرات الأسعار:
import pandas as pd
from datetime import datetime
def detect_price_changes(new_data, historical_file, threshold=0.05):
"""مقارنة الاستخراج الجديد بالبيانات التاريخية، ووضع علامة على التغييرات الأكبر من threshold."""
try:
old = pd.read_csv(historical_file)
except FileNotFoundError:
new_data.to_csv(historical_file, index=False)
print("First run — saved baseline data.")
return pd.DataFrame()
merged = new_data.merge(old, on="zpid", suffixes=("_new", "_old"))
merged["price_change_pct"] = (
(merged["price_new"] - merged["price_old"]) / merged["price_old"]
)
alerts = merged[merged["price_change_pct"].abs() > threshold]
# إلحاق البيانات الجديدة مع طابع زمني
new_data["scraped_at"] = datetime.now().isoformat()
new_data.to_csv(historical_file, mode="a", header=False, index=False)
return alerts
أضف هذا إلى crontab ليعمل أسبوعيًا صباح الاثنين الساعة 6:00:
0 6 * * 1 cd /path/to/scraper && python zillow_monitor.py
مثال عملي: راقب 50 إعلانًا في Austin, TX أسبوعيًا. كل يوم اثنين، يقوم السكربت باستخراج الأسعار الحالية، ومقارنتها بالأسبوع السابق، ثم إخراج ملف CSV يوضح أي انخفاض في السعر يتجاوز 5%.
لغير المبرمجين: Scheduled Scraper من Thunderbit
تتيح لك ميزة Scheduled Scraper في Thunderbit وصف الفاصل الزمني بلغة بسيطة ("كل يوم اثنين الساعة 9 صباحًا")، ثم إدخال روابط بحث Zillow، والنقر على Schedule. يتم تصدير البيانات تلقائيًا إلى Google Sheets في كل تشغيل. لا Python، ولا cron، ولا خادم تحتاج إلى إدارته. هذه ميزة مفيدة جدًا لوكلاء العقارات أو فرق العمليات التي تحتاج متابعة منتظمة للأسعار من دون دعم هندسي.
نصائح لاستخراج بيانات Zillow بشكل مسؤول
بعض الملاحظات المهمة للبقاء ضمن الحدود الصحيحة:
- استخرج البيانات المتاحة للعامة فقط. لا تحاول الوصول إلى صفحات خلف تسجيل الدخول أو التحقق.
- استخدم معدلات طلب معقولة. اترك 3–8 ثوانٍ بين الطلبات. لا تُرهق الخادم.
- لا تستخرج بيانات شخصية أو خاصة بالمستخدمين. أسماء الوكلاء ومعلومات شركات الوساطة في الإعلانات عامة؛ أما بيانات الحسابات الشخصية فليست كذلك.
- خزّن البيانات واستخدمها بطريقة أخلاقية. أبحاث السوق، وتحليل الاستثمار، وتوليد العملاء المحتملين كلها استخدامات مشروعة. أما الرسائل المزعجة فليست كذلك.
- السياق القانوني: حكم hiQ v. LinkedIn رسّخ أن استخراج البيانات المتاحة للعامة لا يخالف CFAA. كما أكّد حكم Meta v. Bright Data (2024) مبادئ مشابهة. ومع ذلك، فإن شروط استخدام Zillow تقيد الوصول الآلي، وتُطبَّق عبر حظر الـ IP وصفحات CAPTCHA بدلًا من الإجراءات القانونية. تحقّق دائمًا من الإرشادات الحالية واحترم robots.txt.
اختر النهج المناسب لاستخراج Zillow باستخدام Python
أفضل مسار لك يعتمد على حالتك:
تحتاج البيانات بسرعة ومن دون كود؟ Thunderbit ينقلك من صفحة بحث Zillow إلى جدول بيانات منظم في حوالي دقيقتين. يتكيّف الذكاء الاصطناعي مع تغييرات التصميم، ويتعامل مع تقسيم الصفحات، ويتيح التصدير مجانًا. ثبّت Chrome Extension وجربه على صفحة بحث في Zillow.
تريد تحكمًا كاملًا؟ استخدم كود Python الموجود في هذا الدليل. استخرج من JSON في __NEXT_DATA__ وليس من CSS selectors لضمان الثبات. أضف headers تشبه المتصفح الحقيقي. ودوّر residential proxies واستخدم exponential backoff لرفع الاعتمادية.
توسّع في الحجم؟ خدمات scraping API مثل ScrapFly (نسبة نجاح 99% على Zillow) أو ScraperAPI تتولى لك بنية الـ proxy والحماية من CAPTCHA، مقابل 30–599 دولارًا شهريًا بحسب الحجم.
تتابع الأسعار بمرور الوقت؟ أنشئ cron job باستخدام سكربت اكتشاف تغيّر الأسعار، أو استخدم Scheduled Scraper من Thunderbit كخيار بلا صيانة.
البيانات موجودة. السؤال الوحيد هو: كم من وقت الهندسة تريد أن تنفقه لاستخراجها؟ ولمزيد من المعلومات حول نقل بيانات الويب إلى جداول البيانات، اطلع على دليلنا حول استخراج البيانات من المواقع إلى Excel أو على ملخص إحصاءات Zillow للحصول على أحدث بيانات المنصة. كما يمكنك مشاهدة الدروس على قناة Thunderbit على YouTube.
جرّب Thunderbit لاستخراج بيانات Zillow Get Started Free
الأسئلة الشائعة
هل يمكن استخراج Zillow باستخدام Python مجانًا؟
نعم — httpx وBeautifulSoup وpandas كلها مجانية ومفتوحة المصدر. المقابل هو الوقت: ستحتاج إلى إدارة الـ headers، وتدوير الـ proxies، وصيانة المحددات بنفسك. توقّع أن تستثمر 4–8 ساعات في الإعداد الأولي و4–10 ساعات شهريًا في الصيانة عندما يغيّر Zillow موقعه. كما يوفّر Thunderbit أيضًا خطة مجانية إذا أردت تجاوز عبء البرمجة بالكامل.
هل لدى Zillow واجهة API رسمية؟
أوقفت Zillow واجهتها العامة المجانية في سبتمبر 2021. أصبح الوصول الآن عبر Bridge Interactive، ويتطلب موافقة، ويكلف تقريبًا 500 دولار شهريًا، وهو موجّه لمتخصصي العقارات المرخّصين. بالنسبة لمعظم المستخدمين — المستثمرين، والباحثين، والوكلاء الذين يجرون تحليلًا للسوق — يكون الاستخراج هو البديل العملي. كما تنشر Zillow أيضًا بيانات بحثية مجانية بصيغة CSV قابلة للتنزيل على zillow.com/research/data/، بما في ذلك Zillow Home Value Index وZillow Observed Rent Index.
كيف أتجنب الحظر أثناء استخراج Zillow؟
ثلاثة أمور: (1) استخدم headers واقعية تشبه المتصفح، بما في ذلك Sec-Ch-Ua — فهذا هو الرأس الذي تغفله معظم الشروحات، وهو أول ما يفحصه PerimeterX؛ (2) دوّر residential proxies — لأن عناوين IP الخاصة بمراكز البيانات تُدرج في قائمة الحظر فورًا؛ (3) استخرج البيانات من JSON الخاص بـ __NEXT_DATA__ بدلًا من HTML selectors لتجنب التعطل بسبب تغيّر التصميم. اجعل المعدل 1 طلب كل 3–8 ثوانٍ لكل IP. أو استخدم أداة مثل Thunderbit التي تتولى الحماية من الروبوتات تلقائيًا.
ما أفضل طريقة لاستخراج Zillow من دون كتابة كود؟
AI Web Scraper من Thunderbit هو أسرع طريق. ثبّت Chrome Extension، ثم انتقل إلى صفحة بحث Zillow، واضغط "AI Suggest Fields" لاكتشاف الأعمدة تلقائيًا، ثم اضغط "Scrape." صدّر النتائج إلى Google Sheets أو Excel أو Airtable أو Notion من دون أي كود. يقرأ الذكاء الاصطناعي الصفحة من جديد في كل مرة، لذلك لا يتعطل عندما يحدّث Zillow تصميمه.
كم مرة يغيّر Zillow بنية موقعه، وكيف يؤثر ذلك على أدوات الاستخراج؟
ينشر Zillow تحديثات بشكل متكرر — أحيانًا أسبوعيًا. وبما أنهم يستخدمون styled-components، تتغير أسماء CSS class مع كل تحديث، وبالتالي تتعطل الأدوات المبنية على CSS selectors بشكل متكرر. أكثر نهج صامد في Python هو استخراج البيانات من JSON داخل __NEXT_DATA__، لأنه يتغير بوتيرة أقل بكثير. أما النهج الخالي من الصيانة، فإن Thunderbit يقرأ بنية الصفحة من جديد في كل عملية استخراج ويتكيف تلقائيًا مع تغييرات التصميم.
تعرف أكثر


