Facility: 000323

Secure It Self Storage in Cheney

Stale Data Warning: This facility has not been successfully scraped in 26 days (threshold: 3 days). Data may be outdated.
Facility Information active
Facility ID
000323
Name
Secure It Self Storage in Cheney
URL
http://www.secure-itstorage.com/
Address
100 W Betz Rd, Cheney, WA 99004, USA, Cheney, Washington 99004
Platform
custom_facility_000323
Parser File
src/parsers/custom/facility_000323_parser.py
Last Scraped
2026-03-27 13:51:06.807575
Created
2026-03-14 16:21:53.706708
Updated
2026-03-27 13:51:06.836650
Parser & Healing Diagnosis working
Parser Status
✓ Working
Status Reason
N/A
Last Healing Attempt
Not attempted
Parser Source (src/parsers/custom/facility_000323_parser.py)
"""Parser for Secure It Self Storage in Cheney."""

from __future__ import annotations

import re

from bs4 import BeautifulSoup

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


class Facility000323Parser(BaseParser):
    """Extract storage units from Secure It Self Storage in Cheney."""

    platform = "custom_facility_000323"

    _UNIT_RE = re.compile(
        r"(\d+\s*[\'\'\u2032]?\s*[xX\u00d7]\s*\d+\s*[\'\'\u2032]?)"
        r"[^\$]{0,120}"
        r"\$(\d[\d,.]*)",
        re.DOTALL,
    )

    _PRICE_SIZE_RE = re.compile(
        r"\$(\d[\d,.]*)"
        r".{0,120}"
        r"(\d+\s*[\'\'\u2032]?\s*[xX\u00d7]\s*\d+\s*[\'\'\u2032]?)",
        re.DOTALL,
    )

    _SIZE_ONLY_RE = re.compile(
        r"(\d+\s*[\'\'\u2032]?\s*[xX\u00d7]\s*\d+\s*[\'\'\u2032]?)"
    )

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

        for tag in soup.find_all(["script", "style"]):
            tag.decompose()

        body_text = soup.get_text(separator="\n")

        seen: set[tuple[str, str]] = set()

        # Try size-then-price pattern
        for m in self._UNIT_RE.finditer(body_text):
            size_text = m.group(1).strip()
            price_text = m.group(2).strip()
            key = (size_text, price_text)
            if key in seen:
                continue
            seen.add(key)

            unit = UnitResult()
            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.price = self.normalize_price(price_text)
            unit.description = m.group(0).strip()[:200]
            if unit.size or unit.price:
                result.units.append(unit)

        # Try price-then-size pattern if no results
        if not result.units:
            for m in self._PRICE_SIZE_RE.finditer(body_text):
                price_text = m.group(1).strip()
                size_text = m.group(2).strip()
                key = (size_text, price_text)
                if key in seen:
                    continue
                seen.add(key)

                unit = UnitResult()
                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.price = self.normalize_price(price_text)
                unit.description = m.group(0).strip()[:200]
                if unit.size or unit.price:
                    result.units.append(unit)

        # Fallback: extract sizes without prices
        if not result.units:
            seen_sizes: set[str] = set()
            for m in self._SIZE_ONLY_RE.finditer(body_text):
                size_text = m.group(1).strip()
                if size_text in seen_sizes:
                    continue
                w, ln, sq = self.normalize_size(size_text)
                if w is None or w < 3 or ln < 3:
                    continue
                seen_sizes.add(size_text)
                unit = UnitResult()
                unit.size = size_text
                unit.metadata = {"width": w, "length": ln, "sqft": sq}
                result.units.append(unit)

        if not result.units:
            result.warnings.append("No units found via regex")

        return result

Scrape Runs (5)

Run #218 Details

Status
exported
Parser Used
Facility000323Parser
Platform Detected
table_layout
Units Found
13
Stage Reached
exported
Timestamp
2026-03-14 16:26:07.380231
Timing
Stage Duration
Fetch2037ms
Detect12ms
Parse4ms
Export16ms

Snapshot: 000323_20260314T162609Z.html · Show Snapshot · Open in New Tab

Parsed Units (13)

5x5

No price

5x10

No price

10x10

No price

10x15

No price

10x20

No price

10x25

No price

10x30

No price

12x40

No price

12x50

No price

14x50

No price

20x40

No price

34x60

No price

93x60

No price

← Back to dashboard