Facility: 013327

Sheridan Storage

Stale Data Warning: This facility has not been successfully scraped in 30 days (threshold: 3 days). Data may be outdated.
Facility Information active
Facility ID
013327
Name
Sheridan Storage
URL
https://www.sheridanstorage.us/self-storage-sheridan-wy-f2632
Address
N/A
Platform
custom_facility_013327
Parser File
src/parsers/custom/facility_013327_parser.py
Last Scraped
2026-03-23 03:19:50.295610
Created
2026-03-06 23:45:35.865957
Updated
2026-03-23 03:19:50.301922
Parser & Healing Diagnosis working
Parser Status
✓ Working
Status Reason
N/A
Last Healing Attempt
Not attempted
Parser Source (src/parsers/custom/facility_013327_parser.py)
"""Parser for Sheridan Self Storage Inc. (Sheridan, WY).

This is a StorEdge-powered site that renders unit data in a table
with obfuscated CSS class names. The table has three columns:
Size, Unit Details (description/amenities), and Price.
"""

from __future__ import annotations

from bs4 import BeautifulSoup

from src.parsers.base import BaseParser, ParseResult, UnitResult


class Facility013327Parser(BaseParser):
    """Extract storage units from Sheridan Self Storage Inc.

    Units are listed in a <table class="_2QREMkZenGaFOjxHn8CDhU table">
    with obfuscated class names. Each <tr> in <tbody> represents one unit.

    - Size: div._2MVARJZtZDaHzpMVh0UAfo (e.g. "5'x10'", "12'x25'")
    - Description: inner div inside ._1JKEBjYdsniYDwnS-HtG-K
    - Price: div._3V2N9w-4bQvYJ9lUWBSkHt (e.g. "$80")
    """

    platform = "custom_facility_013327"

    # CSS class selectors (obfuscated but stable within the snapshot)
    _TABLE_CLS = "_2QREMkZenGaFOjxHn8CDhU"
    _SIZE_CLS = "_2MVARJZtZDaHzpMVh0UAfo"
    _DETAIL_CLS = "_1JKEBjYdsniYDwnS-HtG-K"
    _PRICE_CLS = "_3V2N9w-4bQvYJ9lUWBSkHt"

    def parse(self, html: str, url: str = "") -> ParseResult:
        soup = BeautifulSoup(html, "lxml")
        result = ParseResult(platform=self.platform, parser_name=self.__class__.__name__)

        # Find the units table by its distinctive class
        unit_table = soup.find("table", class_=self._TABLE_CLS)
        if not unit_table:
            result.warnings.append("Unit table not found on page")
            return result

        tbody = unit_table.find("tbody")
        if not tbody:
            result.warnings.append("No tbody in unit table")
            return result

        rows = tbody.find_all("tr")
        for row in rows:
            unit = UnitResult()

            # Extract size
            size_el = row.find(class_=self._SIZE_CLS)
            if size_el:
                size_text = size_el.get_text(strip=True)
                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}

            # Extract description (address + amenity info)
            detail_container = row.find(class_=self._DETAIL_CLS)
            if detail_container:
                # The address/description is in the last inner div (skip the icon div)
                inner_divs = detail_container.find_all("div", recursive=False)
                if inner_divs:
                    desc_text = inner_divs[-1].get_text(strip=True)
                    if desc_text:
                        unit.description = desc_text

                        # Derive amenity flags from description text
                        desc_lower = desc_text.lower()
                        meta = unit.metadata or {}
                        if "heated" in desc_lower or "climate" in desc_lower:
                            meta["climate_control"] = True
                        if "interior" in desc_lower or "indoor" in desc_lower:
                            meta["indoor"] = True
                        if "rv" in desc_lower or "parking" in desc_lower:
                            meta["vehicle_storage"] = True
                        if meta:
                            unit.metadata = meta

            # Extract price
            price_el = row.find(class_=self._PRICE_CLS)
            if price_el:
                unit.price = self.normalize_price(price_el.get_text(strip=True))

            if unit.size or unit.price:
                result.units.append(unit)

        if not result.units:
            result.warnings.append("No units found on page")

        return result

Scrape Runs (5)

Run #993 Details

Status
exported
Parser Used
Facility013327Parser
Platform Detected
storageunitsoftware
Units Found
5
Stage Reached
exported
Timestamp
2026-03-21 19:12:55.942198
Timing
Stage Duration
Fetch3874ms
Detect78ms
Parse32ms
Export8ms

Snapshot: 013327_20260321T191259Z.html · Show Snapshot · Open in New Tab

Parsed Units (5)

5'x10'

$100.00/mo

10'x15'

$100.00/mo

12'x25'

$70.00/mo

12'x35'

$80.00/mo

12'x45'

$90.00/mo

← Back to dashboard