Facility: 031724
Liberty Self Storage
- Facility ID
- 031724
- Name
- Liberty Self Storage
- URL
- http://www.selfstorageliberty.com/
- Address
- 4617 Hamilton Middletown Rd, Liberty Township, OH 45011, USA, Liberty Township, Ohio 45011
- Platform
- custom_facility_031724
- Parser File
- src/parsers/custom/facility_031724_parser.py
- Last Scraped
- 2026-03-27 13:41:10.960303
- Created
- 2026-03-23 02:35:08.816820
- Updated
- 2026-03-27 13:41:10.960303
- Parser Status
- ⚠ Needs Fix
- Status Reason
- Parser returned 0 units
- Last Healing Attempt
- Not attempted
Parser Source (src/parsers/custom/facility_031724_parser.py)
"""Parser for Liberty Self Storage (Sitelink PMS SPA - units loaded dynamically)."""
from __future__ import annotations
import json
import re
from bs4 import BeautifulSoup
from src.parsers.base import BaseParser, ParseResult, UnitResult
class Facility031724Parser(BaseParser):
"""Extract storage units from Liberty Self Storage.
This site uses Sitelink PMS with client-side JS rendering. Unit data
is loaded dynamically via API calls. The static HTML snapshot only
contains a 'Loading...' placeholder. We attempt to extract any unit
data that may be embedded in script tags or JSON-LD.
"""
platform = "custom_facility_031724"
def parse(self, html: str, url: str = "") -> ParseResult:
soup = BeautifulSoup(html, "lxml")
result = ParseResult(platform=self.platform, parser_name=self.__class__.__name__)
# Try JSON-LD structured data
for script in soup.find_all("script", type="application/ld+json"):
try:
data = json.loads(script.string or "")
self._extract_from_jsonld(data, result)
except (json.JSONDecodeError, TypeError):
continue
# Try to find unit data in inline scripts
for script in soup.find_all("script"):
if script.string and "UnitGroup" in script.string:
self._extract_from_script(script.string, result)
if not result.units:
result.warnings.append(
"No units found - Sitelink PMS SPA loads units dynamically via API"
)
return result
def _extract_from_jsonld(self, data: dict | list, result: ParseResult) -> None:
"""Extract unit data from JSON-LD Product entries."""
if isinstance(data, list):
for item in data:
self._extract_from_jsonld(item, result)
return
if not isinstance(data, dict):
return
if "@graph" in data:
for item in data["@graph"]:
self._extract_from_jsonld(item, result)
return
if data.get("@type") == "Product":
desc = data.get("description", "")
# Pattern: "WxLxH - $PRICE - ID"
m = re.match(r"(\d+)x(\d+)(?:x\d+)?\s*-\s*\$(\d+\.?\d*)", desc)
if m:
w, ln, price = float(m.group(1)), float(m.group(2)), float(m.group(3))
unit = UnitResult()
unit.size = f"{int(w)}x{int(ln)}"
unit.price = price
unit.description = data.get("category", "")
unit.metadata = {"width": w, "length": ln, "sqft": w * ln}
result.units.append(unit)
def _extract_from_script(self, script_text: str, result: ParseResult) -> None:
"""Extract UnitGroup data from inline script JSON."""
pattern = re.compile(
r'"name":"([^"]+)","type":"([^"]+)","price":(\d+),'
r'"__typename":"UnitGroup","amenities":\[\],"area":(\d+)'
)
for m in pattern.finditer(script_text):
name, unit_type, price, _area = m.group(1), m.group(2), m.group(3), m.group(4)
size_match = re.match(r"(\d+)x(\d+)", name)
unit = UnitResult()
if size_match:
w, ln = float(size_match.group(1)), float(size_match.group(2))
unit.size = f"{int(w)}x{int(ln)}"
unit.metadata = {"width": w, "length": ln, "sqft": w * ln}
unit.price = float(price)
unit.description = unit_type
result.units.append(unit)
Scrape Runs (3)
Run #1044 Details
- Status
- exported
- Parser Used
- Facility031724Parser
- Platform Detected
- unknown
- Units Found
- 0
- Stage Reached
- exported
- Timestamp
- 2026-03-23 02:40:57.550328
Timing
| Stage | Duration |
|---|---|
| Fetch | 1859ms |
| Detect | 1ms |
| Parse | 1ms |
| Export | 7ms |
Snapshot: 031724_20260323T024059Z.html · Show Snapshot · Open in New Tab
No units found in this run.
All Failures for this Facility (3)
parse
_WarningAsException
scraper
no_units_extracted
warning
Run #N/A | 2026-03-27 13:41:10.942143
No units extracted for 031724
Stack trace
src.reporting.failure_reporter._WarningAsException: No units extracted for 031724
parse
_WarningAsException
scraper
no_units_extracted
warning
Run #N/A | 2026-03-27 13:41:10.636594
No units extracted for 031724
Stack trace
src.reporting.failure_reporter._WarningAsException: No units extracted for 031724
parse
_WarningAsException
scraper
no_units_extracted
warning
Run #N/A | 2026-03-23 02:40:59.427551
No units extracted for 031724
Stack trace
src.reporting.failure_reporter._WarningAsException: No units extracted for 031724