DEV Community

Zenkdom
Zenkdom

Posted on Edited on

Scraping Real Estate Data: How to Bypass Cloudflare Turnstile on Zillow in Python

Real estate intelligence relies on real-time property listings, price drops, and historical sales data. However, portals like Zillow, Redfin, and Realtor.com deploy strict Cloudflare Turnstile and PerimeterX defenses.

In this tutorial, you'll learn how to scrape listing prices, addresses, and property specs without triggering 403 blocks or endless verification loops.


1. Why Zillow Blocks Your Scraper

When you fetch a Zillow listing with Python, three things happen simultaneously:

  1. TCP/TLS Handshake Inspection (JA4): Cloudflare examines the order of cipher suites and TLS extensions in the Client Hello packet.
  2. HTTP/2 Fingerprinting: Cloudflare checks the SETTINGS frame values, initial window sizes, and header capitalization order.
  3. Turnstile JavaScript Challenge: If any anomaly is detected, an interactive or invisible Turnstile challenge executes, demanding clearance tokens (cf_clearance).

Standard Python libraries fail on step 1 before the challenge script even runs.


2. Fast Solution: Parse Everything API

Instead of configuring residential proxy rotations and headless browsers, you can extract Zillow listing data directly through Parse Everything.

Features Used:

  • Autonomous Cloudflare Bypass: Passes Turnstile with zero manual human interaction.
  • CSS Selectors Mode: Extracts only the prices, beds/baths, and square footage directly into JSON.
  • Sub-Second Execution: No heavy browser launch needed when TLS spoofing succeeds.

3. Python Extraction Script

import requests
import json

API_KEY = "pe_live_YOUR_API_KEY"  # Get 1,000 free credits at parse-everything.com
ZILLOW_URL = "https://www.zillow.com/homes/for_sale/Austin-TX/"

headers = {
    "Authorization": f"Bearer {API_KEY}",
    "Content-Type": "application/json"
}

payload = {
    "target_url": ZILLOW_URL,
    "bypass_cloudflare": True,
    "country_code": "US",
    "selectors": {
        "prices": "[data-test='property-card-price']",
        "addresses": "[data-test='property-card-addr']",
        "links": "a.property-card-link"
    }
}

print(f"[*] Querying Zillow via Parse Everything API...")
response = requests.post(
    "https://parse-everything.com/api/v1/extract",
    headers=headers,
    json=payload,
    timeout=45
)

result = response.json()

if response.status_code == 200:
    data = result["data"]
    print(f"[+] HTTP Status: {data['http_status']}")
    print(f"[+] Bypassed: {data['protection_bypassed']} in {data['execution_time_seconds']}s")

    selectors = data.get("selectors_data", {})
    prices = selectors.get("prices", [])
    addresses = selectors.get("addresses", [])

    print(f"\n[+] Found {len(prices)} property listings:")
    for price, addr in zip(prices[:5], addresses[:5]):
        print(f"  • {price} — {addr}")
else:
    print(f"[-] Error: {result}")
Enter fullscreen mode Exit fullscreen mode

4. Key Takeaways

  • Bypass Cloudflare at the TLS layer to reduce latency from 10 seconds to 300 milliseconds.
  • Always use US residential proxies when querying US-specific real estate portals.
  • Filter data using server-side CSS selectors to keep payloads lightweight.

Top comments (0)