Facility: 31

Storage Vail

Stale Data Warning: This facility has not been successfully scraped in 30 days (threshold: 3 days). Data may be outdated.
Facility Information active
Facility ID
31
Name
Storage Vail
URL
https://www.storagevail.com/reservations/
Address
N/A
Platform
custom_facility_31
Parser File
src/parsers/custom/facility_31_parser.py
Last Scraped
2026-03-23 03:15:06.057976
Created
2026-03-14 16:21:53.706708
Updated
2026-03-23 03:15:06.065959
Parser & Healing Diagnosis working
Parser Status
✓ Working
Status Reason
N/A
Last Healing Attempt
Not attempted
Parser Source (src/parsers/custom/facility_31_parser.py)
"""Parser for AAA Self Storage / Storage Vail."""
from __future__ import annotations

from bs4 import BeautifulSoup

from src.parsers.base import BaseParser, ParseResult, UnitResult


class Facility31Parser(BaseParser):
    """Extract storage units from storagevail.com.

    Units are displayed as list items inside a ``rate_bottom`` container,
    each with ``rate_left`` (size), ``rate_text`` (description), and
    ``rate_right`` (price) div elements.
    """

    platform = "custom_facility_31"

    def parse(self, html: str, url: str = "") -> ParseResult:
        soup = BeautifulSoup(html, "lxml")
        result = ParseResult(platform=self.platform, parser_name=self.__class__.__name__)

        rate_section = soup.select_one(".rate_outer .rate_bottom ul")
        if not rate_section:
            result.warnings.append("No rate section found")
            return result

        items = rate_section.find_all("li")
        for item in items:
            # Skip hidden items
            style = item.get("style", "")
            if "display" in style and "none" in style:
                continue

            size_el = item.select_one(".rate_left")
            price_el = item.select_one(".rate_right")

            if not size_el or not price_el:
                continue

            size_text = size_el.get_text(strip=True)
            price_text = price_el.get_text(strip=True)

            unit = UnitResult()
            unit.description = item.get_text(separator=" ", 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}
            unit.sale_price = self.normalize_price(price_text)

            result.units.append(unit)

        if not result.units:
            result.warnings.append("Rate section found but no units extracted")

        return result

Scrape Runs (3)

Run #476 Details

Status
exported
Parser Used
Facility31Parser
Platform Detected
unknown
Units Found
9
Stage Reached
exported
Timestamp
2026-03-14 16:46:36.713834
Timing
Stage Duration
Fetch3766ms
Detect11ms
Parse6ms
Export15ms

Snapshot: 31_20260314T164640Z.html · Show Snapshot · Open in New Tab

Parsed Units (9)

2x2

$56.00/mo

3x3

$69.00/mo

4x4

$98.00/mo

5x5

$113.00/mo

5x10

$157.00/mo

5x15

$205.00/mo

10x10

$241.00/mo

10x15

$298.00/mo

10x20

$374.00/mo

← Back to dashboard