Facility: 14
A Safe Self Storage
- Facility ID
- 14
- Name
- A Safe Self Storage
- URL
- https://www.asafeselfstorage.com/sizes.html
- Address
- N/A
- Platform
- custom_facility_14
- Parser File
- src/parsers/custom/facility_14_parser.py
- Last Scraped
- 2026-03-23 03:14:04.068220
- Created
- 2026-03-14 16:21:53.706708
- Updated
- 2026-03-23 03:14:04.076797
- Parser Status
- ✓ Working
- Status Reason
- N/A
- Last Healing Attempt
- Not attempted
Parser Source (src/parsers/custom/facility_14_parser.py)
"""Parser for A Safe Self Storage."""
from __future__ import annotations
import re
from bs4 import BeautifulSoup
from src.parsers.base import BaseParser, ParseResult, UnitResult
class Facility14Parser(BaseParser):
"""Extract storage units from A Safe Self Storage.
Uses an AccordionWidget where each panel tab has size text
(e.g. "5 x 5 . 25 sq ft") and the panel content has
"Rent = $55.00" / "Monthly Payment = $58.30".
"""
platform = "custom_facility_14"
_RENT_RE = re.compile(r"Rent\s*=\s*\$([\d,]+(?:\.\d+)?)", re.IGNORECASE)
def parse(self, html: str, url: str = "") -> ParseResult:
soup = BeautifulSoup(html, "lxml")
result = ParseResult(platform=self.platform, parser_name=self.__class__.__name__)
panels = soup.select(".AccordionPanel")
if not panels:
result.warnings.append("No AccordionPanel elements found")
return result
for panel in panels:
tab = panel.select_one(".AccordionPanelTab")
content = panel.select_one(".AccordionPanelContent")
if not tab or not content:
continue
size_text = tab.get_text(strip=True)
content_text = content.get_text(separator=" ", strip=True)
unit = UnitResult()
unit.size = size_text
unit.description = f"{size_text} | {content_text}"
w, ln, sq = self.normalize_size(size_text)
if w is not None:
unit.metadata = {"width": w, "length": ln, "sqft": sq}
rent_match = self._RENT_RE.search(content_text)
if rent_match:
unit.price = self.normalize_price(rent_match.group(1))
if (unit.metadata and "width" in unit.metadata) or unit.price:
result.units.append(unit)
if not result.units:
result.warnings.append("Accordion panels found but no units extracted")
return result
Scrape Runs (3)
Run #1402 Details
- Status
- exported
- Parser Used
- Facility14Parser
- Platform Detected
- unknown
- Units Found
- 8
- Stage Reached
- exported
- Timestamp
- 2026-03-23 03:14:01.727536
Timing
| Stage | Duration |
|---|---|
| Fetch | 2199ms |
| Detect | 83ms |
| Parse | 33ms |
| Export | 7ms |
Snapshot: 14_20260323T031403Z.html · Show Snapshot · Open in New Tab
Parsed Units (8)
5 x 5 • 25 sq ft
$55.00/mo
5 x 10 • 50 sq ft
$75.00/mo
5 x 15 • 75 sq ft
$85.00/mo
10 x 10 • 100 sq ft
$110.00/mo
10 x 15 • 150 sq ft
$130.00/mo
10 x 20 • 200 sq ft
$150.00/mo
10 x 25 • 250 sq ft
$180.00/mo
10 x 30 • 300 sq ft
$200.00/mo