Skip to content

Instantly share code, notes, and snippets.

@adnjoo
Last active August 18, 2025 20:09
Show Gist options
  • Select an option

  • Save adnjoo/0ab41be4dfec4f14bb7ee74ebce458f4 to your computer and use it in GitHub Desktop.

Select an option

Save adnjoo/0ab41be4dfec4f14bb7ee74ebce458f4 to your computer and use it in GitHub Desktop.
Zillow Scrape Images
/**
* Zillow Image Scraper with ZIP Download
*
* - Collects all high-res JPG images from Zillow listing photo gallery
* - Dedupes by photo ID (keeps largest resolution)
* - Names ZIP file after the property address
* - Uses JSZip + FileSaver loaded dynamically
*
* Usage:
* 1. Open any Zillow listing in your browser
* 2. Open DevTools Console
* 3. Paste this script and hit Enter
* 4. Wait a few seconds — a ZIP named <address>.zip will download
*/
(async function () {
// Helper: dynamically load a script into <head>
function loadScript(src) {
return new Promise((resolve, reject) => {
const script = document.createElement("script");
script.src = src;
script.onload = resolve;
script.onerror = reject;
document.head.appendChild(script);
});
}
// Load JSZip and FileSaver safely
if (typeof JSZip === "undefined") {
await loadScript("https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js");
}
if (typeof saveAs === "undefined") {
await loadScript("https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js");
}
// Try to get address for ZIP filename
let address = "zillow-images";
const addressEl = document.querySelector("h1");
if (addressEl && addressEl.innerText.trim()) {
address = addressEl.innerText.trim()
.replace(/[^\w\d]+/g, "_") // replace spaces/punctuation with underscores
.replace(/_+/g, "_") // collapse multiple underscores
.replace(/^_|_$/g, ""); // trim underscores
}
// Locate the Zillow media wall
const mediaWall = document.querySelector('ul[aria-label="media wall images"]');
if (!mediaWall) {
console.log("❌ Could not find Zillow media wall on this page.");
return;
}
// Collect all JPG URLs
const allUrls = new Set();
mediaWall.querySelectorAll("img").forEach(img => {
if (img.src.includes("zillowstatic.com") && img.src.endsWith(".jpg")) {
allUrls.add(img.src);
}
});
mediaWall.querySelectorAll("source").forEach(source => {
const srcset = source.getAttribute("srcset");
if (srcset) {
srcset.split(",").forEach(item => {
const cleanUrl = item.trim().split(" ")[0];
if (cleanUrl.includes("zillowstatic.com") && cleanUrl.endsWith(".jpg")) {
allUrls.add(cleanUrl);
}
});
}
});
// Deduplicate: keep only highest resolution per photo
const map = {};
[...allUrls].forEach(url => {
const match = url.match(/(.*-cc_ft_)(\d+)\.jpg$/);
if (match) {
const base = match[1];
const size = parseInt(match[2], 10);
const key = base + "jpg";
if (!map[key] || size > map[key].size) {
map[key] = { url, size };
}
}
});
const deduped = Object.values(map).map(v => v.url);
console.log("✅ Found", deduped.length, "unique JPG images");
// Build ZIP
const zip = new JSZip();
const folder = zip.folder(address);
for (let i = 0; i < deduped.length; i++) {
const url = deduped[i];
console.log("⬇️ Fetching", url);
const resp = await fetch(url);
const blob = await resp.blob();
folder.file(`image_${i + 1}.jpg`, blob);
}
// Generate ZIP and trigger download
const content = await zip.generateAsync({ type: "blob" });
saveAs(content, address + ".zip");
console.log(`📦 Download complete → ${address}.zip`);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment