Facility: 003747
Ross Point Mini Storage
- Facility ID
- 003747
- Name
- Ross Point Mini Storage
- URL
- https://www.rosspointministorage.com/3856-e-12th-ave-post-falls-id-83854
- Address
- 3856 E 12th Ave, Post Falls, ID 83854, USA, Post Falls, Idaho 83854
- Platform
- custom_facility_003747
- Parser File
- src/parsers/custom/facility_003747_parser.py
- Last Scraped
- 2026-03-27 13:57:09.437489
- Created
- 2026-03-14 16:21:53.706708
- Updated
- 2026-03-27 13:57:09.468641
- Parser Status
- ✓ Working
- Status Reason
- N/A
- Last Healing Attempt
- Not attempted
Parser Source (src/parsers/custom/facility_003747_parser.py)
"""Parser for Ross Point Mini Storage."""
from __future__ import annotations
import re
from bs4 import BeautifulSoup
from src.parsers.base import BaseParser, ParseResult, UnitResult
class Facility003747Parser(BaseParser):
"""Extract storage units from Ross Point Mini Storage."""
platform = "custom_facility_003747"
_UNIT_RE = re.compile(
r"(\d+\s*[\'\'\u2032]?\s*[xX\u00d7]\s*\d+\s*[\'\'\u2032]?)"
r"[^\$]{0,120}"
r"\$(\d[\d,.]*)",
re.DOTALL,
)
_PRICE_SIZE_RE = re.compile(
r"\$(\d[\d,.]*)"
r".{0,120}"
r"(\d+\s*[\'\'\u2032]?\s*[xX\u00d7]\s*\d+\s*[\'\'\u2032]?)",
re.DOTALL,
)
_SIZE_ONLY_RE = re.compile(
r"(\d+\s*[\'\'\u2032]?\s*[xX\u00d7]\s*\d+\s*[\'\'\u2032]?)"
)
def parse(self, html: str, url: str = "") -> ParseResult:
soup = BeautifulSoup(html, "lxml")
result = ParseResult(platform=self.platform, parser_name=self.__class__.__name__)
for tag in soup.find_all(["script", "style"]):
tag.decompose()
body_text = soup.get_text(separator="\n")
seen: set[tuple[str, str]] = set()
# Try size-then-price pattern
for m in self._UNIT_RE.finditer(body_text):
size_text = m.group(1).strip()
price_text = m.group(2).strip()
key = (size_text, price_text)
if key in seen:
continue
seen.add(key)
unit = UnitResult()
unit.size = size_text
w, ln, sq = self.normalize_size(size_text)
if w is not None:
unit.metadata = {"width": w, "length": ln, "sqft": sq}
unit.price = self.normalize_price(price_text)
unit.description = m.group(0).strip()[:200]
if unit.size or unit.price:
result.units.append(unit)
# Try price-then-size pattern if no results
if not result.units:
for m in self._PRICE_SIZE_RE.finditer(body_text):
price_text = m.group(1).strip()
size_text = m.group(2).strip()
key = (size_text, price_text)
if key in seen:
continue
seen.add(key)
unit = UnitResult()
unit.size = size_text
w, ln, sq = self.normalize_size(size_text)
if w is not None:
unit.metadata = {"width": w, "length": ln, "sqft": sq}
unit.price = self.normalize_price(price_text)
unit.description = m.group(0).strip()[:200]
if unit.size or unit.price:
result.units.append(unit)
# Fallback: extract sizes without prices
if not result.units:
seen_sizes: set[str] = set()
for m in self._SIZE_ONLY_RE.finditer(body_text):
size_text = m.group(1).strip()
if size_text in seen_sizes:
continue
w, ln, sq = self.normalize_size(size_text)
if w is None or w < 3 or ln < 3:
continue
seen_sizes.add(size_text)
unit = UnitResult()
unit.size = size_text
unit.metadata = {"width": w, "length": ln, "sqft": sq}
result.units.append(unit)
if not result.units:
result.warnings.append("No units found via regex")
return result
Scrape Runs (5)
-
exported Run #19512026-03-27 13:57:05.925705 | 21 units | Facility003747Parser | View Data →
-
exported Run #19502026-03-27 13:57:05.922044 | 21 units | Facility003747Parser | View Data →
-
exported Run #12382026-03-23 02:58:22.029184 | 21 units | Facility003747Parser | View Data →
-
exported Run #7452026-03-21 18:50:10.133285 | 21 units | Facility003747Parser | View Data →
-
exported Run #2942026-03-14 16:31:23.173068 | 21 units | Facility003747Parser | View Data →
Run #1238 Details
- Status
- exported
- Parser Used
- Facility003747Parser
- Platform Detected
- storageunitsoftware
- Units Found
- 21
- Stage Reached
- exported
- Timestamp
- 2026-03-23 02:58:22.029184
Timing
| Stage | Duration |
|---|---|
| Fetch | 4782ms |
| Detect | 79ms |
| Parse | 152ms |
| Export | 7ms |
Snapshot: 003747_20260323T025826Z.html · Show Snapshot · Open in New Tab
Parsed Units (21)
5'x5'
No price
4'x10'
No price
4'x12'
No price
6'x8'
No price
5'x10'
No price
5'x12'
No price
6'x12'
No price
5'x15'
No price
8'x10'
No price
10'x10'
No price
10'x12'
No price
10'x15'
No price
10'x20'
No price
12'x20'
No price
10'x25'
No price
12'x25'
No price
10'x30'
No price
12'x28'
No price
12'x30'
No price
12'x35'
No price
12'x40'
No price