Introduction
Zillow is a real estate company that offers various products targeted at both home buyers and sellers.
In this article, you will read about the easiest way to scrape real estate data from Zillow with Page2API.
You will find code examples for Ruby , Python , PHP , NodeJS , cURL ,
and a No-Code solution that will import Zillow listings into Google Sheets.
You can scrape real estate data from Zillow, with such information as addresses, prices, descriptions, photos, URLs to perform:
price monitoring
trends analysis
competitor analysis
Challenges
At first look, scraping Zillow doesn't seem to be a trivial task because of the following aspects:
The content from the listing page is returned dynamically, based on scrolling events.
The names of the CSS selectors are dynamically generated and cannot be used to pick the needed content, and we will use XPath selectors instead.
For this purpose, we will use Page2API - the scraping API that overtakes the challenges mentioned above with ease.
In this article, we will learn how to:
Scrape Zillow listings
Scrape Zillow Property data
Prerequisites
To start scraping, you will need the following things:
A Page2API account
A location, in which we want to search for listed properties, let's use for example Redwood City
A property overview page from Zillow. We will pick a random property link from the page mentioned above.
How to scrape Zillow listings
First what we need is to open the 'Homes' page and type the name of the city that will show the properties we are searching for.
In our case we will open this page:
https://www.zillow.com/homes/
and search for
'Redwood City'
It will change the browser URL to something similar to:
https://www.zillow.com/homes/Redwood-City,-CA_rb/
The resulted URL is the first parameter we need to start scraping the listings page.
The listings page must look similar to the following one:
If you inspect the page HTML, you will find out that a single result is wrapped into an element that looks like the following:
The HTML for a single result element will look like this:
From the listing page, we will scrape the following attributes from each property:
Price
URL
Bedrooms
Bathrooms
Living area
Status
Address
Each property container is wrapped in an
article element with the following class:
list-card .
Now, let's define the selectors for each attribute.
/* Parent: */
article.list-card
/* Price: */
.list-card-price
/* URL: */
a
/* Bedrooms: */
ul.list-card-details li:nth-child(1 )
/* Bathrooms: */
ul.list-card-details li:nth-child(2 )
/* Living area: */
ul.list-card-details li:nth-child(3 )
/* Status: */
ul.list-card-details li:nth-child(4 )
/* Address: */
a address.list-card-addr
Next is the pagination handling.
In our case, we must click on the next page link while the link will be active:
var next = document.querySelector('.search-pagination a[rel=next]'); if(next){ next.click() }
And stop our scraping request when the next page link became disabled.
In our case, a new attribute (disabled ) is assigned to the pagination link.
The stop condition for the pagination will look like this:
var next = document.querySelector('.search-pagination a[rel=next]'); next === null || next.getAttributeNames().includes('disabled')
// returns true if there is no next page
The last thing is handling the content that loads dynamically when we scroll down.
Usually, there are 40 items on the page, but when the page loads - it has only about 8 items.
To load all items we will do the next trick:
Wait for the page to load
Scroll down 3 times slowly, (with a short delay) until we see the last item
Start scraping the page
Now let's build the request that will scrape all properties that the search page returned.
The payload for our scraping request will be:
{
"api_key": "YOUR_PAGE2API_KEY",
"url": "https://www.zillow.com/homes/Redwood-City,-CA_rb/",
"real_browser": true,
"merge_loops": true,
"premium_proxy": "de",
"scenario": [
{
"loop": [
{ "wait_for": ".search-pagination a[rel=next]" },
{ "execute_js": "var articles = document.querySelectorAll('article')"},
{ "execute_js": "articles[Math.round(articles.length/4)].scrollIntoView({behavior: 'smooth'})"},
{ "wait": 1 },
{ "execute_js": "articles[Math.round(articles.length/2)].scrollIntoView({behavior: 'smooth'})"},
{ "wait": 1 },
{ "execute_js": "articles[Math.round(articles.length/1.5)].scrollIntoView({behavior: 'smooth'})"},
{ "wait": 1 },
{ "execute": "parse"},
{ "execute_js": "var next = document.querySelector('.search-pagination a[rel=next]'); if(next){ next.click() }" }
],
"stop_condition": "var next = document.querySelector('.search-pagination a[rel=next]'); next === null || next.getAttributeNames().includes('disabled')"
}
],
"parse": {
"properties": [
{
"_parent": "article.list-card",
"price": ".list-card-price >> text",
"url": "a >> href",
"bedrooms": "ul.list-card-details li:nth-child(1) >> text",
"bathrooms": "ul.list-card-details li:nth-child(2) >> text",
"living_area": "ul.list-card-details li:nth-child(3) >> text",
"status": "ul.list-card-details li:nth-child(4) >> text",
"address": "a address.list-card-addr >> text"
}
]
}
}
Note: we have to encode our js snippets in base64 to run the request in the terminal with cURL.
Running the scraping request
Ruby
Python
PHP
NodeJS
cURL
require 'rest_client'
require 'json'
api_url = 'https://www.page2api.com/api/v1/scrape'
payload = {
api_key: 'YOUR_PAGE2API_KEY',
url: "https://www.zillow.com/homes/Redwood-City,-CA_rb/",
real_browser: true,
merge_loops: true,
premium_proxy: "de",
scenario: [
{
loop: [
{ wait_for: ".search-pagination a[rel=next]" },
{ execute_js: "var articles = document.querySelectorAll('article')"},
{ execute_js: "articles[Math.round(articles.length/4)].scrollIntoView({behavior: 'smooth'})"},
{ wait: 1 },
{ execute_js: "articles[Math.round(articles.length/2)].scrollIntoView({behavior: 'smooth'})"},
{ wait: 1 },
{ execute_js: "articles[Math.round(articles.length/1.5)].scrollIntoView({behavior: 'smooth'})"},
{ wait: 1 },
{ execute: "parse"},
{ execute_js: "var next = document.querySelector('.search-pagination a[rel=next]'); if(next){ next.click() }" }
],
stop_condition: "var next = document.querySelector('.search-pagination a[rel=next]'); next === null || next.getAttributeNames().includes('disabled')"
}
],
parse: {
properties: [
{
_parent: "article.list-card",
price: ".list-card-price >> text",
url: "a >> href",
bedrooms: "ul.list-card-details li:nth-child(1) >> text",
bathrooms: "ul.list-card-details li:nth-child(2) >> text",
living_area: "ul.list-card-details li:nth-child(3) >> text",
status: "ul.list-card-details li:nth-child(4) >> text",
address: "a address.list-card-addr >> text"
}
]
}
}
response = RestClient::Request.execute(
method: :post,
payload: payload.to_json,
url: api_url,
headers: { "Content-type" => "application/json" },
).body
result = JSON.parse(response)
puts(result)
import requests
import json
api_url = 'https://www.page2api.com/api/v1/scrape'
payload = {
"api_key": "YOUR_PAGE2API_KEY",
"url": "https://www.zillow.com/homes/Redwood-City,-CA_rb/",
"real_browser": True,
"merge_loops": True,
"premium_proxy": "de",
"scenario": [
{
"loop": [
{ "wait_for": ".search-pagination a[rel=next]" },
{ "execute_js": "var articles = document.querySelectorAll('article')"},
{ "execute_js": "articles[Math.round(articles.length/4)].scrollIntoView({behavior: 'smooth'})"},
{ "wait": 1 },
{ "execute_js": "articles[Math.round(articles.length/2)].scrollIntoView({behavior: 'smooth'})"},
{ "wait": 1 },
{ "execute_js": "articles[Math.round(articles.length/1.5)].scrollIntoView({behavior: 'smooth'})"},
{ "wait": 1 },
{ "execute": "parse"},
{ "execute_js": "var next = document.querySelector('.search-pagination a[rel=next]'); if(next){ next.click() }" }
],
"stop_condition": "var next = document.querySelector('.search-pagination a[rel=next]'); next === null || next.getAttributeNames().includes('disabled')"
}
],
"parse": {
"properties": [
{
"_parent": "article.list-card",
"price": ".list-card-price >> text",
"url": "a >> href",
"bedrooms": "ul.list-card-details li:nth-child(1) >> text",
"bathrooms": "ul.list-card-details li:nth-child(2) >> text",
"living_area": "ul.list-card-details li:nth-child(3) >> text",
"status": "ul.list-card-details li:nth-child(4) >> text",
"address": "a address.list-card-addr >> text"
}
]
}
}
headers = {'Content-type': 'application/json', 'Accept': 'text/plain'}
response = requests.post(api_url, data=json.dumps(payload), headers=headers)
result = json.loads(response.text)
print(result)
<?php
$api_url = 'https://www.page2api.com/api/v1/scrape';
$payload = [
'api_key' => 'YOUR_PAGE2API_KEY',
'url' => 'https://www.zillow.com/homes/Redwood-City,-CA_rb/',
'real_browser' => true,
'merge_loops' => true,
'premium_proxy' => 'de',
'scenario' => [
0 => [
'loop' => [
0 => [ "wait_for" => ".search-pagination a[rel=next]" ],
1 => [ "execute_js" => "var articles = document.querySelectorAll('article')" ],
2 => [ "execute_js" => "articles[Math.round(articles.length/4)].scrollIntoView({behavior: 'smooth'})" ],
3 => [ "wait" => 1 ],
4 => [ "execute_js" => "articles[Math.round(articles.length/2)].scrollIntoView({behavior: 'smooth'})" ],
5 => [ "wait" => 1 ],
6 => [ "execute_js" => "articles[Math.round(articles.length/1.5)].scrollIntoView({behavior: 'smooth'})" ],
7 => [ "wait" => 1 ],
8 => [ "execute" => "parse" ],
9 => [ "execute_js" => "var next = document.querySelector('.search-pagination a[rel=next]'); if(next){ next.click() }" ]
],
'stop_condition' => 'var next = document.querySelector(".search-pagination a[rel=next]"); next === null || next.getAttributeNames().includes("disabled")'
]
],
'parse' => [
'properties' => [
0 => [
'_parent' => 'article.list-card',
'price' => '.list-card-price >> text',
'url' => 'a >> href',
'bedrooms' => 'ul.list-card-details li:nth-child(1) >> text',
'bathrooms' => 'ul.list-card-details li:nth-child(2) >> text',
'living_area' => 'ul.list-card-details li:nth-child(3) >> text',
'status' => 'ul.list-card-details li:nth-child(4) >> text',
'address' => 'a address.list-card-addr >> text'
]
]
]
];
$postdata = json_encode($payload);
$ch = curl_init($api_url);
curl_setopt($ch,CURLOPT_POST, true);
curl_setopt($ch,CURLOPT_POSTFIELDS, $postdata);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type:application/json'));
curl_setopt($ch,CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($ch);
curl_close($ch);
echo $result;
?>
const axios = require('axios');
const api_url = 'https://www.page2api.com/api/v1/scrape';
const payload = {
api_key: 'YOUR_PAGE2API_KEY',
url: 'https://www.zillow.com/homes/Redwood-City,-CA_rb/',
real_browser: true,
merge_loops: true,
premium_proxy: 'de',
scenario: [
{
loop: [
{ wait_for: '.search-pagination a[rel=next]' },
{ execute_js: 'var articles = document.querySelectorAll("article")'},
{ execute_js: 'articles[Math.round(articles.length/4)].scrollIntoView({behavior: "smooth"})'},
{ wait: 1 },
{ execute_js: 'articles[Math.round(articles.length/2)].scrollIntoView({behavior: "smooth"})'},
{ wait: 1 },
{ execute_js: 'articles[Math.round(articles.length/1.5)].scrollIntoView({behavior: "smooth"})'},
{ wait: 1 },
{ execute: 'parse'},
{ execute_js: 'var next = document.querySelector(".search-pagination a[rel=next]"); if(next){ next.click() }' }
],
stop_condition: 'var next = document.querySelector(".search-pagination a[rel=next]"); next === null || next.getAttributeNames().includes("disabled")'
}
],
parse: {
properties: [
{
_parent: 'article.list-card',
price: '.list-card-price >> text',
url: 'a >> href',
bedrooms: 'ul.list-card-details li:nth-child(1) >> text',
bathrooms: 'ul.list-card-details li:nth-child(2) >> text',
living_area: 'ul.list-card-details li:nth-child(3) >> text',
status: 'ul.list-card-details li:nth-child(4) >> text',
address: 'a address.list-card-addr >> text'
}
]
}
};
axios.post(api_url, payload)
.then((res) => {
console.log(JSON.stringify(res.data, null, " "));
}).catch((err) => {
console.error(err);
});
curl -XPOST -H "Content-type: application/json" -d '{
"api_key": "YOUR_PAGE2API_KEY",
"url": "https://www.zillow.com/homes/Redwood-City,-CA_rb/",
"real_browser": true,
"premium_proxy": "de",
"merge_loops": true,
"scenario": [
{
"loop" : [
{ "wait_for": ".search-pagination a[rel=next]" },
{ "execute_js": "dmFyIGFydGljbGVzID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvckFsbCgnYXJ0aWNsZScp"},
{ "execute_js": "YXJ0aWNsZXNbTWF0aC5yb3VuZChhcnRpY2xlcy5sZW5ndGgvNCldLnNjcm9sbEludG9WaWV3KHtiZWhhdmlvcjogJ3Ntb290aCd9KQ=="},
{ "wait": 1 },
{ "execute_js": "YXJ0aWNsZXNbTWF0aC5yb3VuZChhcnRpY2xlcy5sZW5ndGgvMildLnNjcm9sbEludG9WaWV3KHtiZWhhdmlvcjogJ3Ntb290aCd9KQ=="},
{ "wait": 1 },
{ "execute_js": "YXJ0aWNsZXNbTWF0aC5yb3VuZChhcnRpY2xlcy5sZW5ndGgvMS41KV0uc2Nyb2xsSW50b1ZpZXcoe2JlaGF2aW9yOiAnc21vb3RoJ30p"},
{ "wait": 1 },
{ "execute": "parse"},
{ "execute_js": "dmFyIG5leHQgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKCcuc2VhcmNoLXBhZ2luYXRpb24gYVtyZWw9bmV4dF0nKTsgaWYobmV4dCl7IG5leHQuY2xpY2soKSB9" }
],
"stop_condition": "dmFyIG5leHQgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKCcuc2VhcmNoLXBhZ2luYXRpb24gYVtyZWw9bmV4dF0nKTsgbmV4dCA9PT0gbnVsbCB8fCBuZXh0LmdldEF0dHJpYnV0ZU5hbWVzKCkuaW5jbHVkZXMoJ2Rpc2FibGVkJyk="
}
],
"parse": {
"properties": [
{
"_parent": "article.list-card",
"price": ".list-card-price >> text",
"url": "a >> href",
"bedrooms": "ul.list-card-details li:nth-child(1) >> text",
"bathrooms": "ul.list-card-details li:nth-child(2) >> text",
"living_area": "ul.list-card-details li:nth-child(3) >> text",
"status": "ul.list-card-details li:nth-child(4) >> text",
"address": "a address.list-card-addr >> text"
}
]
}
}' 'https://www.page2api.com/api/v1/scrape' | python -mjson.tool
The result
{
"result": {
"properties": [
{
"price": "$600,000",
"url": "https://www.zillow.com/homedetails/464-Clinton-St-APT-211-Redwood-City-CA-94062/15638802_zpid/",
"bedrooms": "1 bd",
"bathrooms": "1 ba",
"living_area": "761 sqft",
"status": "- Condo for sale",
"address": "464 Clinton St APT 211, Redwood City, CA 94062"
},
{
"price": "$2,498,000",
"url": "https://www.zillow.com/homedetails/3618-Midfield-Way-Redwood-City-CA-94062/15571874_zpid/",
"bedrooms": "4 bds",
"bathrooms": "4 ba",
"living_area": "2,960 sqft",
"status": "- House for sale",
"address": "3618 Midfield Way, Redwood City, CA 94062"
},
...
]
}, ...
}
How to scrape Zillow property data
From the 'Homes' page, we click on any property.
This will change the browser URL to something similar to:
https://www.zillow.com/homedetails/464-Clinton-St-APT-211-Redwood-City-CA-94062/15638802_zpid/
We will see something like this when we will inspect the page source:
From this page, we will scrape the following attributes:
Price
Address
Bedrooms
Bathrooms
Living area
Status
Overview
Time on Zillow
Views
Saves
Images
Let's define the selectors for each attribute.
/* Price: */
.ds-summary-row span
/* Address */
h1
/* Bedrooms: */
.ds-bed-bath-living-area-container span span
/* Bathrooms: */
.ds-bed-bath-living-area-container button span span
/* Living area: */
.ds-bed-bath-living-area-container span:nth-child(5 ) span
/* Status: */
.ds-status-details
/* Overview: */
.ds-overview-section
/* Time on Zillow: */
//*[contains(text(),'Time on Zillow')]/../div[2]
/* Views: */
//*[contains(text(),'Time on Zillow')]/../../div[2]/div[2]
/* Saves: */
//*[contains(text(),'Time on Zillow')]/../../div[3]/div[2]
/* Images: */
.media-stream-tile picture img
The payload for our scraping request will be:
{
"api_key": "YOUR_PAGE2API_KEY",
"url": "https://www.zillow.com/homedetails/3449-Thomas-Dr-Palo-Alto-CA-94303/19498585_zpid/",
"premium_proxy": "de",
"real_browser": true,
"wait_for": ".ds-summary-row",
"parse": {
"price": ".ds-summary-row span >> text",
"address": "h1 >> text",
"bedrooms": ".ds-bed-bath-living-area-container span span >> text",
"bathrooms": ".ds-bed-bath-living-area-container button span span >> text",
"living_area": ".ds-bed-bath-living-area-container span:nth-child(5) span >> text",
"status": ".ds-status-details >> text",
"overview": ".ds-overview-section >> text",
"time_on_zillow": "//*[contains(text(),'Time on Zillow')]/../div[2] >> text",
"views": "//*[contains(text(),'Time on Zillow')]/../../div[2]/div[2] >> text",
"saves": "//*[contains(text(),'Time on Zillow')]/../../div[3]/div[2] >> text",
"images": [
".media-stream-tile picture img >> src"
]
}
}
Running the scraping request
Ruby
Python
PHP
NodeJS
cURL
require 'rest_client'
require 'json'
api_url = 'https://www.page2api.com/api/v1/scrape'
payload = {
api_key: 'YOUR_PAGE2API_KEY',
url: "https://www.zillow.com/homedetails/3449-Thomas-Dr-Palo-Alto-CA-94303/19498585_zpid/",
real_browser: true,
premium_proxy: "de",
wait_for: ".ds-summary-row",
parse: {
price: ".ds-summary-row span >> text",
address: "h1 >> text",
bedrooms: ".ds-bed-bath-living-area-container span span >> text",
bathrooms: ".ds-bed-bath-living-area-container button span span >> text",
living_area: ".ds-bed-bath-living-area-container span:nth-child(5) span >> text",
status: ".ds-status-details >> text",
overview: ".ds-overview-section >> text",
time_on_zillow: "//*[contains(text(),'Time on Zillow')]/../div[2] >> text",
views: "//*[contains(text(),'Time on Zillow')]/../../div[2]/div[2] >> text",
saves: "//*[contains(text(),'Time on Zillow')]/../../div[3]/div[2] >> text",
images: [
".media-stream-tile picture img >> src"
]
}
}
response = RestClient::Request.execute(
method: :post,
payload: payload.to_json,
url: api_url,
headers: { "Content-type" => "application/json" },
).body
result = JSON.parse(response)
puts(result)
import requests
import json
api_url = 'https://www.page2api.com/api/v1/scrape'
payload = {
"api_key": "YOUR_PAGE2API_KEY",
"url": "https://www.zillow.com/homedetails/3449-Thomas-Dr-Palo-Alto-CA-94303/19498585_zpid/",
"real_browser": True,
"premium_proxy": "de",
"wait_for": ".ds-summary-row",
"parse": {
"price": ".ds-summary-row span >> text",
"address": "h1 >> text",
"bedrooms": ".ds-bed-bath-living-area-container span span >> text",
"bathrooms": ".ds-bed-bath-living-area-container button span span >> text",
"living_area": ".ds-bed-bath-living-area-container span:nth-child(5) span >> text",
"status": ".ds-status-details >> text",
"overview": ".ds-overview-section >> text",
"time_on_zillow": "//*[contains(text(),'Time on Zillow')]/../div[2] >> text",
"views": "//*[contains(text(),'Time on Zillow')]/../../div[2]/div[2] >> text",
"saves": "//*[contains(text(),'Time on Zillow')]/../../div[3]/div[2] >> text",
"images": [
".media-stream-tile picture img >> src"
]
}
}
headers = {'Content-type': 'application/json', 'Accept': 'text/plain'}
response = requests.post(api_url, data=json.dumps(payload), headers=headers)
result = json.loads(response.text)
print(result)
<?php
$api_url = 'https://www.page2api.com/api/v1/scrape';
$payload = [
'api_key' => 'YOUR_PAGE2API_KEY',
'url' => 'https://www.zillow.com/homedetails/3449-Thomas-Dr-Palo-Alto-CA-94303/19498585_zpid/',
'real_browser' => true,
'premium_proxy' => 'de',
'wait_for' => '.ds-summary-row',
'parse' => [
'price' => '.ds-summary-row span >> text',
'address' => 'h1 >> text',
'bedrooms' => '.ds-bed-bath-living-area-container span span >> text',
'bathrooms' => '.ds-bed-bath-living-area-container button span span >> text',
'living_area' => '.ds-bed-bath-living-area-container span:nth-child(5) span >> text',
'status' => '.ds-status-details >> text',
'overview' => '.ds-overview-section >> text',
'time_on_zillow' => '//*[contains(text(),"Time on Zillow")]/../div[2] >> text',
'views' => '//*[contains(text(),"Time on Zillow")]/../../div[2]/div[2] >> text',
'saves' => '//*[contains(text(),"Time on Zillow")]/../../div[3]/div[2] >> text',
'images' => [
0 => '.media-stream-tile picture img >> src'
]
]
];
$postdata = json_encode($payload);
$ch = curl_init($api_url);
curl_setopt($ch,CURLOPT_POST, true);
curl_setopt($ch,CURLOPT_POSTFIELDS, $postdata);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type:application/json'));
curl_setopt($ch,CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($ch);
curl_close($ch);
echo $result;
?>
const axios = require('axios');
const api_url = 'https://www.page2api.com/api/v1/scrape';
const payload = {
api_key: 'YOUR_PAGE2API_KEY',
url: 'https://www.zillow.com/homedetails/3449-Thomas-Dr-Palo-Alto-CA-94303/19498585_zpid/',
real_browser: true,
premium_proxy: 'de',
wait_for: '.ds-summary-row',
parse: {
price: '.ds-summary-row span >> text',
address: 'h1 >> text',
bedrooms: '.ds-bed-bath-living-area-container span span >> text',
bathrooms: '.ds-bed-bath-living-area-container button span span >> text',
living_area: '.ds-bed-bath-living-area-container span:nth-child(5) span >> text',
status: '.ds-status-details >> text',
overview: '.ds-overview-section >> text',
time_on_zillow: '//*[contains(text(),"Time on Zillow")]/../div[2] >> text',
views: '//*[contains(text(),"Time on Zillow")]/../../div[2]/div[2] >> text',
saves: '//*[contains(text(),"Time on Zillow")]/../../div[3]/div[2] >> text',
images: [
'.media-stream-tile picture img >> src'
]
}
};
axios.post(api_url, payload)
.then((res) => {
console.log(JSON.stringify(res.data, null, " "));
}).catch((err) => {
console.error(err);
});
curl -XPOST -H "Content-type: application/json" -d '{
"api_key": "YOUR_PAGE2API_KEY",
"url": "https://www.zillow.com/homedetails/3449-Thomas-Dr-Palo-Alto-CA-94303/19498585_zpid/",
"real_browser": true,
"premium_proxy": "de",
"wait_for": ".ds-summary-row",
"parse": {
"price": ".ds-summary-row span >> text",
"address": "h1 >> text",
"bedrooms": ".ds-bed-bath-living-area-container span span >> text",
"bathrooms": ".ds-bed-bath-living-area-container button span span >> text",
"living_area": ".ds-bed-bath-living-area-container span:nth-child(5) span >> text",
"status": ".ds-status-details >> text",
"overview": ".ds-overview-section >> text",
"time_on_zillow": "//*[contains(text(),\"Time on Zillow\")]/../div[2] >> text",
"views": "//*[contains(text(),\"Time on Zillow\")]/../../div[2]/div[2] >> text",
"saves": "//*[contains(text(),\"Time on Zillow\")]/../../div[3]/div[2] >> text",
"images": [
".media-stream-tile picture img >> src"
]
}
}' 'https://www.page2api.com/api/v1/scrape' | python -mjson.tool
The result
{
"result": {
"price": "$5,198,888",
"address": "1280 Lincoln Ave, Palo Alto, CA 94301",
"bedrooms": "4",
"bathrooms": "4",
"living_area": "3,073",
"status": "For sale",
"overview": "Welcome home to your Mediterranean estate- the perfect medley of modern elegance and classical style...",
"time_on_zillow": "2 days",
"views": "1,059",
"saves": "24",
"images": [
"https://photos.zillowstatic.com/fp/cbb350b4eb7d45741a02589f25824d66-cc_ft_960.jpg",
"https://photos.zillowstatic.com/fp/76a2bb926537616441a0bbfe0c1caa3e-cc_ft_576.jpg",
"https://photos.zillowstatic.com/fp/e8c2f85a80250f0beb8a2cc9fffda509-cc_ft_576.jpg",
"https://photos.zillowstatic.com/fp/93d1a07ae2eda5565e07871566bc3e6c-cc_ft_576.jpg",
"https://photos.zillowstatic.com/fp/667b4b6b21ac287c609f9e2b26763e95-cc_ft_576.jpg",
"https://photos.zillowstatic.com/fp/36fdfb7ee4fbb8aa2d00256d6ccdcc45-cc_ft_576.jpg",
"https://photos.zillowstatic.com/fp/07c526d7ad8aa0c8d363360909330555-cc_ft_576.jpg"
]
}
}
How to export Zillow listings to Google Sheets
In order to be able to export our Zillow listings to a Google Spreadsheet we will need to slightly modify our request to receive the data in
CSV format instead of
JSON .
According to the
documentation , we need to add the following parameters to our payload:
"raw": {
"key": "properties", "format": "csv"
}
Now our payload will look like:
{
"api_key": "YOUR_PAGE2API_KEY",
"url": "https://www.zillow.com/homes/Redwood-City,-CA_rb/",
"real_browser": true,
"merge_loops": true,
"premium_proxy": "de",
"raw": {
"key": "properties", "format": "csv"
},
"scenario": [
{
"loop": [
{ "wait_for": ".search-pagination a[rel=next]" },
{ "execute_js": "var articles = document.querySelectorAll('article')"},
{ "execute_js": "articles[Math.round(articles.length/4)].scrollIntoView({behavior: 'smooth'})"},
{ "wait": 1 },
{ "execute_js": "articles[Math.round(articles.length/2)].scrollIntoView({behavior: 'smooth'})"},
{ "wait": 1 },
{ "execute_js": "articles[Math.round(articles.length/1.5)].scrollIntoView({behavior: 'smooth'})"},
{ "wait": 1 },
{ "execute": "parse"},
{ "execute_js": "var next = document.querySelector('.search-pagination a[rel=next]'); if(next){ next.click() }" }
],
"stop_condition": "var next = document.querySelector('.search-pagination a[rel=next]'); next === null || next.getAttributeNames().includes('disabled')"
}
],
"parse": {
"properties": [
{
"_parent": "article.list-card",
"price": ".list-card-price >> text",
"url": "a >> href",
"bedrooms": "ul.list-card-details li:nth-child(1) >> text",
"bathrooms": "ul.list-card-details li:nth-child(2) >> text",
"living_area": "ul.list-card-details li:nth-child(3) >> text",
"status": "ul.list-card-details li:nth-child(4) >> text",
"address": "a address.list-card-addr >> text"
}
]
}
}
Now, edit the payload above if needed, and press Encode →
The URL with encoded payload will be:
Press 'Encode'
Note: If you are reading this article being logged in - you can copy the link above since it will already have your api_key in the encoded payload.
The final part is adding the
IMPORTDATA function, and we are ready to import our Zillow listings into a Google Spreadsheet.
Press 'Encode'
The result must look like the following one:
Conclusion
That's it!
In this article, you've discovered the easiest way to scrape a real estate website, such as Zillow , with Page2API - a Web Scraping API that handles any challenges for you.