Facility: 011634
ACMS Storage
- Facility ID
- 011634
- Name
- ACMS Storage
- URL
- http://www.acmsstorage.com/
- Address
- N/A
- Platform
- custom_facility_011634
- Parser File
- src/parsers/custom/facility_011634_parser.py
- Last Scraped
- 2026-03-23 03:18:18.309576
- Created
- 2026-03-06 23:45:35.865957
- Updated
- 2026-03-23 03:18:18.309576
- Parser Status
- ⚠ Needs Fix
- Status Reason
- Parser returned 0 units
- Last Healing Attempt
- Not attempted
Parser Source (src/parsers/custom/facility_011634_parser.py)
"""Parser for ACMS Storage (Sheridan, WY) — Storable/Storedge platform.
The site is a React SPA (Storable Voyager) that embeds unit data in a
``window.__APOLLO_STATE__`` JSON blob. Unit groups are keyed as
``UnitGroup:<uuid>`` with dimensions encoded in the ``name`` field as
``WxLx0 - $streetPrice - facilityCode - [unitIds]`` and the current
listed price in the ``price`` field.
"""
from __future__ import annotations
import json
import re
from bs4 import BeautifulSoup
from src.parsers.base import BaseParser, ParseResult, UnitResult
class Facility011634Parser(BaseParser):
"""Extract storage units from ACMS Storage via Apollo state JSON.
The page embeds a ``window.__APOLLO_STATE__`` JSON object that contains
``UnitGroup`` entries. Each entry has:
- ``name``: ``"WxLx0 - $<street_price> - <code> - [<ids>]"``
- ``price``: current listed price (float)
- ``type``: unit type string, e.g. ``"Self Storage"``, ``"Lot Parking"``
"""
platform = "custom_facility_011634"
# Parses "10x15x0 - $95.00 - ..." → width=10, length=15, street_price=95.00
_NAME_RE = re.compile(
r"^(?P<width>\d+)x(?P<length>\d+)x\d+\s*-\s*\$(?P<street_price>[\d.]+)"
)
def parse(self, html: str, url: str = "") -> ParseResult:
soup = BeautifulSoup(html, "lxml")
result = ParseResult(platform=self.platform, parser_name=self.__class__.__name__)
# Locate the Apollo state script
apollo_data = self._extract_apollo_state(soup)
if apollo_data is None:
result.warnings.append("window.__APOLLO_STATE__ not found in page")
return result
unit_groups = [
v for k, v in apollo_data.items()
if k.startswith("UnitGroup:") and isinstance(v, dict)
]
if not unit_groups:
result.warnings.append("No UnitGroup entries found in Apollo state")
return result
for group in unit_groups:
unit = self._parse_unit_group(group)
if unit is not None:
result.units.append(unit)
if not result.units:
result.warnings.append("UnitGroup entries found but none could be parsed")
return result
# ------------------------------------------------------------------
# Helpers
# ------------------------------------------------------------------
def _extract_apollo_state(self, soup: BeautifulSoup) -> dict | None:
"""Return the parsed ``__APOLLO_STATE__`` dict or ``None``."""
for script in soup.find_all("script"):
text = script.string or ""
if "__APOLLO_STATE__" not in text:
continue
try:
idx = text.index("{")
decoder = json.JSONDecoder()
data, _ = decoder.raw_decode(text, idx)
return data
except (ValueError, json.JSONDecodeError):
continue
return None
def _parse_unit_group(self, group: dict) -> UnitResult | None:
"""Convert a single UnitGroup dict into a ``UnitResult``."""
name = group.get("name", "")
listed_price = group.get("price")
unit_type = group.get("type", "")
m = self._NAME_RE.match(name)
if not m:
return None
width = float(m.group("width"))
length = float(m.group("length"))
street_price = float(m.group("street_price"))
size = f"{int(width)}' x {int(length)}'"
unit = UnitResult(
size=size,
description=f"{size} {unit_type}".strip(),
price=float(listed_price) if listed_price is not None else None,
sale_price=street_price if street_price != float(listed_price or 0) else None,
metadata={
"width": width,
"length": length,
"sqft": width * length,
"unit_type": unit_type,
},
)
return unit
Scrape Runs (5)
-
exported Run #14672026-03-23 03:18:14.505718 | Facility011634Parser
-
exported Run #9742026-03-21 19:11:06.880343 | Facility011634Parser
-
exported Run #5272026-03-14 16:53:46.151960 | Facility011634Parser
-
exported Run #1272026-03-14 01:05:21.550165 | Facility011634Parser
-
exported Run #462026-03-13 21:12:34.137441 | Facility011634Parser
Run #127 Details
- Status
- exported
- Parser Used
- Facility011634Parser
- Platform Detected
- storageunitsoftware
- Units Found
- 0
- Stage Reached
- exported
- Timestamp
- 2026-03-14 01:05:21.550165
Timing
| Stage | Duration |
|---|---|
| Fetch | 4277ms |
| Detect | 21ms |
| Parse | 9ms |
| Export | 10ms |
Snapshot: 011634_20260314T010525Z.html · Show Snapshot · Open in New Tab
No units found in this run.
All Failures for this Facility (5)
parse
_WarningAsException
scraper
no_units_extracted
warning
Run #N/A | 2026-03-23 03:18:18.305867
No units extracted for 011634
Stack trace
src.reporting.failure_reporter._WarningAsException: No units extracted for 011634
parse
_WarningAsException
scraper
no_units_extracted
warning
Run #N/A | 2026-03-21 19:11:11.221323
No units extracted for 011634
Stack trace
src.reporting.failure_reporter._WarningAsException: No units extracted for 011634
parse
_WarningAsException
scraper
no_units_extracted
warning
Run #N/A | 2026-03-14 16:53:49.084786
No units extracted for 011634
Stack trace
src.reporting.failure_reporter._WarningAsException: No units extracted for 011634
parse
_WarningAsException
scraper
no_units_extracted
warning
Run #N/A | 2026-03-14 01:05:25.905634
No units extracted for 011634
Stack trace
src.reporting.failure_reporter._WarningAsException: No units extracted for 011634
parse
_WarningAsException
scraper
no_units_extracted
warning
Run #N/A | 2026-03-13 21:12:39.695954
No units extracted for 011634
Stack trace
src.reporting.failure_reporter._WarningAsException: No units extracted for 011634