Facility: 079766
Payette County Storage
- Facility ID
- 079766
- Name
- Payette County Storage
- URL
- http://www.payettecountystorage.com/
- Address
- N/A
- Platform
- custom_facility_079766
- Parser File
- src/parsers/custom/facility_079766_parser.py
- Last Scraped
- 2026-03-23 03:16:46.937176
- Created
- 2026-03-06 23:45:35.865957
- Updated
- 2026-03-23 03:16:46.945490
- Parser Status
- ✓ Working
- Status Reason
- N/A
- Last Healing Attempt
- Not attempted
Parser Source (src/parsers/custom/facility_079766_parser.py)
"""Parser for Payette County Storage facility.
This is a WordPress/Avada (Fusion Builder) site that lists storage units
as styled content boxes. Each box contains an h2 with the size (e.g. "5x10"),
an h1 with the monthly price (e.g. "$50"), and a FontAwesome icon that
indicates whether the unit is climate-controlled (fa-umbrella with circle-yes).
"""
from __future__ import annotations
import re
from bs4 import BeautifulSoup
from src.parsers.base import BaseParser, ParseResult, UnitResult
class Facility079766Parser(BaseParser):
"""Extract storage units from Payette County Storage.
Units are rendered as Avada content boxes with:
- h2.content-box-heading: size string (e.g. "5x10")
- h1: monthly price (e.g. "$50")
- i.fontawesome-icon: optional amenity icon;
fa-umbrella with circle-yes indicates climate control
"""
platform = "custom_facility_079766"
_SIZE_RE = re.compile(r"^\d+[xX]\d+$", re.IGNORECASE)
_PRICE_RE = re.compile(r"\$\s*(\d[\d,]*(?:\.\d+)?)")
def parse(self, html: str, url: str = "") -> ParseResult:
soup = BeautifulSoup(html, "lxml")
result = ParseResult(platform=self.platform, parser_name=self.__class__.__name__)
boxes = soup.find_all("div", class_="content-box-wrapper")
if not boxes:
result.warnings.append("No content-box-wrapper divs found on page")
return result
for box in boxes:
# Extract size from h2.content-box-heading
h2 = box.find("h2", class_="content-box-heading")
if not h2:
continue
size_text = h2.get_text(strip=True)
if not self._SIZE_RE.match(size_text):
continue
width, length, sqft = self.normalize_size(size_text)
if width is None or length is None:
continue
normalized_size = f"{int(width)}x{int(length)}"
# Extract price from h1
h1 = box.find("h1")
price: float | None = None
if h1:
price_match = self._PRICE_RE.search(h1.get_text(strip=True))
if price_match:
price = self.normalize_price(price_match.group(0))
# Detect climate control from icon class
icon = box.find("i", class_="fontawesome-icon")
climate_controlled = False
if icon:
icon_classes = " ".join(icon.get("class", []))
if "fa-umbrella" in icon_classes and "circle-yes" in icon_classes:
climate_controlled = True
description_parts = [normalized_size]
if climate_controlled:
description_parts.append("Climate Controlled")
unit = UnitResult(
size=normalized_size,
price=price,
description=", ".join(description_parts),
url=url,
metadata={
"width": width,
"length": length,
"sqft": sqft,
"climate_controlled": climate_controlled,
},
)
result.units.append(unit)
if not result.units:
result.warnings.append("Content boxes found but no unit data could be extracted")
return result
Scrape Runs (4)
-
exported Run #14482026-03-23 03:16:41.056451 | 3 units | Facility079766Parser | View Data →
-
exported Run #9552026-03-21 19:09:28.940230 | 3 units | Facility079766Parser | View Data →
-
exported Run #5082026-03-14 16:52:28.755936 | 3 units | Facility079766Parser | View Data →
-
exported Run #1062026-03-14 01:03:42.181571 | 3 units | Facility079766Parser | View Data →
Run #508 Details
- Status
- exported
- Parser Used
- Facility079766Parser
- Platform Detected
- table_layout
- Units Found
- 3
- Stage Reached
- exported
- Timestamp
- 2026-03-14 16:52:28.755936
Timing
| Stage | Duration |
|---|---|
| Fetch | 4282ms |
| Detect | 23ms |
| Parse | 10ms |
| Export | 14ms |
Snapshot: 079766_20260314T165233Z.html · Show Snapshot · Open in New Tab
Parsed Units (3)
5x10
$50.00/mo
10x12
$75.00/mo
12x25
$115.00/mo