Facility: 041635

Valley Storage WY

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
041635
Name
Valley Storage WY
URL
https://www.valleystoragewy.net/781-us-highway-14-ranchester-wy-82839
Address
N/A
Platform
custom_facility_041635
Parser File
src/parsers/custom/facility_041635_parser.py
Last Scraped
2026-03-23 03:19:06.172635
Created
2026-03-06 23:45:35.865957
Updated
2026-03-23 03:19:06.178321
Parser & Healing Diagnosis working
Parser Status
✓ Working
Status Reason
N/A
Last Healing Attempt
Not attempted
Parser Source (src/parsers/custom/facility_041635_parser.py)
"""Parser for Valley Storage (Ranchester, WY) — facility 041635.

This site is built on StorEdge/StorageUnitSoftware. Unit data is embedded
in a JSON-LD <script type="application/ld+json"> block as a @graph of
Product entries.  The description field encodes dimensions and price in
the format:

    "10x20x8 - $95.00 - 392649 - 1"
    {width}x{depth}x{height} - ${price} - {internal_id} - {count}

The offers.category field contains the unit type (e.g. "self storage",
"parking").
"""

from __future__ import annotations

import json
import re

from bs4 import BeautifulSoup

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

# Matches "10x20x8 - $95.00 - ..." at the start of the description
_DESC_RE = re.compile(
    r"^(\d+)x(\d+)x(\d+)\s*-\s*\$([\d,]+(?:\.\d+)?)",
    re.IGNORECASE,
)


class Facility041635Parser(BaseParser):
    """Extract storage units from Valley Storage (Ranchester, WY).

    Unit data is found in an application/ld+json block structured as a
    schema.org @graph of Product nodes.  Each Product's description
    encodes dimensions and price; its offers.category encodes unit type.
    """

    platform = "custom_facility_041635"

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

        # Locate the JSON-LD block that contains a @graph list of Products
        graph_data: list[dict] = []
        for script in soup.find_all("script", type="application/ld+json"):
            try:
                data = json.loads(script.string or "")
            except (json.JSONDecodeError, TypeError):
                continue

            if isinstance(data, dict) and "@graph" in data:
                graph_data = data["@graph"]
                break

        if not graph_data:
            result.warnings.append("No JSON-LD @graph block found on page")
            return result

        for item in graph_data:
            if not isinstance(item, dict):
                continue
            if item.get("@type") != "Product":
                continue

            description = item.get("description", "")
            offers = item.get("offers", {})
            category = offers.get("category", "")

            match = _DESC_RE.match(description)
            if not match:
                continue

            width = float(match.group(1))
            depth = float(match.group(2))
            height = float(match.group(3))
            price_raw = match.group(4).replace(",", "")
            price = float(price_raw)

            # Format size as "10' x 20'"
            size_str = f"{int(width)}' x {int(depth)}'"

            unit = UnitResult(
                size=size_str,
                price=price,
                description=description,
                metadata={
                    "width": width,
                    "length": depth,
                    "height": height,
                    "sqft": width * depth,
                    "category": category,
                },
            )
            result.units.append(unit)

        if not result.units:
            result.warnings.append("JSON-LD @graph found but no Product entries matched")

        return result

Scrape Runs (5)

Run #139 Details

Status
exported
Parser Used
Facility041635Parser
Platform Detected
storageunitsoftware
Units Found
5
Stage Reached
exported
Timestamp
2026-03-14 01:06:11.148663
Timing
Stage Duration
Fetch4753ms
Detect27ms
Parse14ms
Export16ms

Snapshot: 041635_20260314T010615Z.html · Show Snapshot · Open in New Tab

Parsed Units (5)

10' x 20'

$95.00/mo

5' x 10'

$50.00/mo

10' x 10'

$65.00/mo

10' x 12'

$75.00/mo

10' x 11'

$45.00/mo

← Back to dashboard