Facility: 033716
Terrace Drive Storage
- Facility ID
- 033716
- Name
- Terrace Drive Storage
- URL
- https://ecom3.quikstor.com/terrace_drive_storage/listing
- Address
- N/A
- Platform
- custom_facility_033716
- Parser File
- src/parsers/custom/facility_033716_parser.py
- Last Scraped
- 2026-03-23 03:18:45.767254
- Created
- 2026-03-06 23:45:35.865957
- Updated
- 2026-03-23 03:18:45.774547
- Parser Status
- ✓ Working
- Status Reason
- N/A
- Last Healing Attempt
- Not attempted
Parser Source (src/parsers/custom/facility_033716_parser.py)
"""Parser for Terrace Drive Storage (QuikStor ecom3 platform).
The page is served by QuikStor's ecom3 platform at:
https://ecom3.quikstor.com/terrace_drive_storage/listing
Available units are loaded dynamically via AJAX into `.products` containers,
so the snapshot may only contain wait-list units rendered server-side inside
the "Wait List Units" section. The parser handles both cases: it extracts
every `.product` div and marks wait-list entries with an appropriate scarcity
label.
"""
from __future__ import annotations
import re
from bs4 import BeautifulSoup
from src.parsers.base import BaseParser, ParseResult, UnitResult
class Facility033716Parser(BaseParser):
"""Extract storage units from Terrace Drive Storage (QuikStor ecom3)."""
platform = "custom_facility_033716"
# Matches prices like "$80/mo", "$80.00/mo", "$1,200/month"
_PRICE_RE = re.compile(r"\$[\d,]+(?:\.\d+)?(?:/mo(?:nth)?)?", re.IGNORECASE)
def parse(self, html: str, url: str = "") -> ParseResult:
soup = BeautifulSoup(html, "lxml")
result = ParseResult(platform=self.platform, parser_name=self.__class__.__name__)
# Find all section containers — each may hold a group of .product divs.
# We track whether the enclosing section is a "wait list" section so we
# can annotate scarcity accordingly.
sections = soup.select("div.b-col-default-indent")
for section in sections:
# Detect wait-list context by checking the section header text.
header = section.select_one("h3.wait-list-header")
is_wait_list = header is not None
for product in section.select("div.product"):
unit = UnitResult()
# --- Size ---
size_el = product.select_one("div.product__size")
if size_el:
# The size text node is the direct text before nested tags.
size_raw = size_el.find(string=True, recursive=False)
if size_raw:
size_text = size_raw.strip()
else:
# Fallback: use full text but strip child element text.
size_text = size_el.get_text(strip=True)
unit.size = size_text
w, ln, sq = self.normalize_size(size_text)
if w is not None:
unit.metadata = {"width": w, "length": ln, "sqft": sq}
# --- Description (overview line, e.g. "Outside, Storage") ---
desc_el = product.select_one("div.product__overview-line")
if desc_el:
unit.description = desc_el.get_text(strip=True)
# --- Price ---
# Price text is in the form "$80/mo" or "$1,200/month".
# Strip the "/mo" or "/month" suffix before normalizing.
price_el = product.select_one("div.product__price")
if price_el:
price_text = price_el.get_text(strip=True)
price_text = re.sub(r"/mo(?:nth)?$", "", price_text, flags=re.IGNORECASE).strip()
unit.price = self.normalize_price(price_text)
# --- Scarcity / availability ---
if is_wait_list:
unit.scarcity = "Wait List"
else:
avail_el = product.select_one("span.product__action-title")
if avail_el:
unit.scarcity = avail_el.get_text(strip=True)
# Only add units that have at minimum a size or price.
if unit.size or unit.price:
result.units.append(unit)
if not result.units:
result.warnings.append(
"No units found — available units may require JavaScript rendering. "
"Only wait-list or statically rendered units will appear in a snapshot."
)
return result
Scrape Runs (5)
-
exported Run #14742026-03-23 03:18:42.076684 | 1 units | Facility033716Parser | View Data →
-
exported Run #9812026-03-21 19:11:36.866313 | 1 units | Facility033716Parser | View Data →
-
exported Run #5342026-03-14 16:54:09.153449 | 1 units | Facility033716Parser | View Data →
-
exported Run #1362026-03-14 01:05:51.445465 | 1 units | Facility033716Parser | View Data →
-
exported Run #552026-03-13 21:37:31.091911 | 1 units | Facility033716Parser | View Data →
Run #1474 Details
- Status
- exported
- Parser Used
- Facility033716Parser
- Platform Detected
- table_layout
- Units Found
- 1
- Stage Reached
- exported
- Timestamp
- 2026-03-23 03:18:42.076684
Timing
| Stage | Duration |
|---|---|
| Fetch | 3642ms |
| Detect | 13ms |
| Parse | 12ms |
| Export | 5ms |
Snapshot: 033716_20260323T031845Z.html · Show Snapshot · Open in New Tab
Parsed Units (1)
10x10
$80.00/mo
Wait List