Facility: 090744
5150 Storage
- Facility ID
- 090744
- Name
- 5150 Storage
- URL
- https://www.5150storage.com/2650-e-yellowstone-hwy-casper-wy-82609
- Address
- N/A
- Platform
- custom_facility_090744
- Parser File
- src/parsers/custom/facility_090744_parser.py
- Last Scraped
- 2026-03-27 14:04:28.323297
- Created
- 2026-03-06 23:45:35.865957
- Updated
- 2026-03-27 14:04:28.352147
- Parser Status
- ✓ Working
- Status Reason
- N/A
- Last Healing Attempt
- Not attempted
Parser Source (src/parsers/custom/facility_090744_parser.py)
"""Parser for 5150 Storage - Casper, WY facility.
This is a React/Material-UI site that renders storage unit cards.
Each unit is a MuiCard-root div containing an h5 title with dimensions
and an h6.MuiTypography-subtitle1 with the monthly price.
The card content is duplicated three times for responsive breakpoints;
we take only the first h5/h6 in each card to deduplicate.
"""
from __future__ import annotations
import re
from bs4 import BeautifulSoup
from src.parsers.base import BaseParser, ParseResult, UnitResult
class Facility090744Parser(BaseParser):
"""Extract storage units from 5150 Storage (MUI card layout)."""
platform = "custom_facility_090744"
# Matches "5' x 10' Unit", "10' x 10' Unit", etc.
_SIZE_RE = re.compile(
r"(\d+(?:\.\d+)?)['\u2019\u2032]?\s*[xX\u00d7]\s*(\d+(?:\.\d+)?)['\u2019\u2032]?",
re.IGNORECASE,
)
def parse(self, html: str, url: str = "") -> ParseResult:
soup = BeautifulSoup(html, "lxml")
result = ParseResult(platform=self.platform, parser_name=self.__class__.__name__)
# Each unit is wrapped in a MuiCard-root div
cards = soup.select(".MuiCard-root")
for card in cards:
unit = UnitResult()
# Size is in the first h5 element (e.g. "5' x 10' Unit")
h5 = card.find("h5")
if h5:
size_text = h5.get_text(strip=True)
unit.size = size_text
m = self._SIZE_RE.search(size_text)
if m:
width = float(m.group(1))
length = float(m.group(2))
unit.metadata = {"width": width, "length": length, "sqft": width * length}
# Price is in the first h6.MuiTypography-subtitle1 (e.g. "$48/mo")
# Strip the "/mo" suffix before normalizing.
price_el = card.find(
"h6",
class_=lambda c: c and "MuiTypography-subtitle1" in c,
)
if price_el:
price_text = re.sub(r"\s*/\s*mo\b.*", "", price_el.get_text(strip=True), flags=re.IGNORECASE)
unit.price = self.normalize_price(price_text)
# Description from first p element
p_el = card.find("p")
if p_el:
unit.description = p_el.get_text(strip=True)
if unit.size or unit.price:
result.units.append(unit)
if not result.units:
result.warnings.append("No unit cards found on page")
return result
Scrape Runs (7)
-
exported Run #21332026-03-27 14:04:22.021024 | 4 units | Facility090744Parser | View Data →
-
exported Run #21322026-03-27 14:04:22.001834 | 4 units | Facility090744Parser | View Data →
-
exported Run #13322026-03-23 03:06:40.771560 | 4 units | Facility090744Parser | View Data →
-
exported Run #8392026-03-21 18:58:41.059667 | 4 units | Facility090744Parser | View Data →
-
exported Run #3882026-03-14 16:38:20.028762 | 4 units | Facility090744Parser | View Data →
-
failed Run #152026-03-07 01:42:15.879165 | 1 failure(s)
-
started Run #62026-03-07 01:05:27.484062
Run #15 Details
- Status
- failed
- Parser Used
- N/A
- Platform Detected
- N/A
- Units Found
- 0
- Stage Reached
- fetch
- Timestamp
- 2026-03-07 01:42:15.879165
Failures (1)
fetch
DatatypeMismatch
unknown
unknown
permanent
column "success" is of type boolean but expression is of type integer LINE 3: ... VALUES ('090744', 15, '090744_20260307T014220Z.html', 0) ^ HINT: You will need to rewrite or cast the expression.
Stack trace
Traceback (most recent call last):
File "/app/src/pipeline.py", line 329, in _process_facility
manifest_id = storage.insert_snapshot_manifest(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/app/src/db/pg_backend.py", line 615, in insert_snapshot_manifest
row = self._execute_returning(
^^^^^^^^^^^^^^^^^^^^^^^^
File "/app/src/db/pg_backend.py", line 54, in _execute_returning
cur.execute(sql, params)
File "/app/.venv/lib/python3.11/site-packages/psycopg2/extras.py", line 236, in execute
return super().execute(query, vars)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
psycopg2.errors.DatatypeMismatch: column "success" is of type boolean but expression is of type integer
LINE 3: ... VALUES ('090744', 15, '090744_20260307T014220Z.html', 0)
^
HINT: You will need to rewrite or cast the expression.
All Failures for this Facility (1)
fetch
DatatypeMismatch
unknown
unknown
permanent
Run #15 | 2026-03-07 01:42:20.864580
column "success" is of type boolean but expression is of type integer LINE 3: ... VALUES ('090744', 15, '090744_20260307T014220Z.html', 0) ^ HINT: You will need to rewrite or cast the expression.
Stack trace
Traceback (most recent call last):
File "/app/src/pipeline.py", line 329, in _process_facility
manifest_id = storage.insert_snapshot_manifest(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/app/src/db/pg_backend.py", line 615, in insert_snapshot_manifest
row = self._execute_returning(
^^^^^^^^^^^^^^^^^^^^^^^^
File "/app/src/db/pg_backend.py", line 54, in _execute_returning
cur.execute(sql, params)
File "/app/.venv/lib/python3.11/site-packages/psycopg2/extras.py", line 236, in execute
return super().execute(query, vars)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
psycopg2.errors.DatatypeMismatch: column "success" is of type boolean but expression is of type integer
LINE 3: ... VALUES ('090744', 15, '090744_20260307T014220Z.html', 0)
^
HINT: You will need to rewrite or cast the expression.