Facility: 051938

MKRM Storage

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
051938
Name
MKRM Storage
URL
https://www.mkrmstorage.com/3856-coffeen-ave-sheridan-wy-82801
Address
N/A
Platform
custom_facility_051938
Parser File
src/parsers/custom/facility_051938_parser.py
Last Scraped
2026-03-23 03:18:26.058495
Created
2026-03-06 23:45:35.865957
Updated
2026-03-23 03:18:26.070987
Parser & Healing Diagnosis working
Parser Status
✓ Working
Status Reason
N/A
Last Healing Attempt
Not attempted
Parser Source (src/parsers/custom/facility_051938_parser.py)
"""Parser for Make Room Storage (mkrmstorage.com) — storedge platform.

The page embeds unit data in a JSON-LD <script type="application/ld+json"> block
as a @graph of Product items. Each item's description field encodes dimensions,
price, and an internal facility ID in the format:

    "{width}x{depth}x{height} - ${price} - {facility_id}"
    or
    "{width}x{depth} - ${price} - {facility_id}"

Example: "10x20x10 - $90.00 - 64696"
"""

from __future__ import annotations

import json
import re

from bs4 import BeautifulSoup

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

# Matches: "{width}x{depth}[x{height}] - ${price} - {rest}"
_DESC_RE = re.compile(
    r"^(\d+(?:\.\d+)?)x(\d+(?:\.\d+)?)(?:x(\d+(?:\.\d+)?))?",
    re.IGNORECASE,
)


class Facility051938Parser(BaseParser):
    """Extract storage units from Make Room Storage (storedge JSON-LD data)."""

    platform = "custom_facility_051938"

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

        # Find all JSON-LD scripts and look for the Product @graph
        for script in soup.find_all("script", type="application/ld+json"):
            text = script.string
            if not text:
                continue
            try:
                data = json.loads(text)
            except (json.JSONDecodeError, ValueError):
                continue

            graph = data.get("@graph")
            if not isinstance(graph, list):
                continue

            # Check that this graph contains Product items
            products = [item for item in graph if item.get("@type") == "Product"]
            if not products:
                continue

            for item in products:
                description = item.get("description", "")
                offers = item.get("offers", {})
                raw_price = offers.get("price")

                # Parse dimensions from description string
                match = _DESC_RE.match(description)
                if not match:
                    continue

                width = float(match.group(1))
                depth = float(match.group(2))
                height_str = match.group(3)
                height = float(height_str) if height_str else None

                size = f"{int(width)}x{int(depth)}"

                metadata: dict = {
                    "width": width,
                    "length": depth,
                    "sqft": width * depth,
                }
                if height is not None:
                    metadata["height"] = height

                price: float | None = None
                if raw_price is not None:
                    try:
                        price = float(raw_price)
                    except (TypeError, ValueError):
                        price = self.normalize_price(str(raw_price))

                unit = UnitResult(
                    size=size,
                    price=price,
                    description=description,
                    metadata=metadata,
                )
                result.units.append(unit)

            # Stop after the first matching graph block
            if result.units:
                break

        if not result.units:
            result.warnings.append("No Product units found in JSON-LD graph")

        return result

Scrape Runs (5)

Run #529 Details

Status
exported
Parser Used
Facility051938Parser
Platform Detected
storageunitsoftware
Units Found
19
Stage Reached
exported
Timestamp
2026-03-14 16:53:52.902130
Timing
Stage Duration
Fetch2716ms
Detect22ms
Parse11ms
Export15ms

Snapshot: 051938_20260314T165355Z.html · Show Snapshot · Open in New Tab

Parsed Units (19)

5x10

$45.00/mo

12x40

$150.00/mo

10x20

$90.00/mo

12x20

$100.00/mo

10x25

$120.00/mo

10x25

$140.00/mo

10x25

$150.00/mo

10x15

$85.00/mo

10x20

$105.00/mo

10x10

$60.00/mo

10x20

$85.00/mo

10x10

$70.00/mo

10x15

$80.00/mo

10x10

$65.00/mo

10x20

$100.00/mo

8x25

$25.00/mo

10x20

$95.00/mo

12x20

$10.00/mo

10x25

$160.00/mo

← Back to dashboard