Facility: 017878

ABC Storage LLC MT

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
017878
Name
ABC Storage LLC MT
URL
https://www.abcstoragellcmt.com/available-units
Address
N/A
Platform
custom_facility_017878
Parser File
src/parsers/custom/facility_017878_parser.py
Last Scraped
2026-03-23 03:21:36.959191
Created
2026-03-06 23:45:35.865957
Updated
2026-03-23 03:21:36.959191
Parser & Healing Diagnosis needs_fix
Parser Status
⚠ Needs Fix
Status Reason
Parser returned 0 units
Last Healing Attempt
Not attempted
Parser Source (src/parsers/custom/facility_017878_parser.py)
"""Parser for ABC Storage LLC (Ballantine, MT) — Squarespace site.

Unit data is embedded as h3 headings inside Squarespace HTML content blocks
with the format: "<WxL> Storage Unit (<sqft> sq. ft.) - $<price> / mo"
"""

from __future__ import annotations

import re

from bs4 import BeautifulSoup

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


class Facility017878Parser(BaseParser):
    """Extract storage units from ABC Storage LLC Squarespace site.

    Unit headings follow the pattern:
        7x12 Storage Unit (84 sq. ft.) - $43 / mo
    """

    platform = "custom_facility_017878"

    # Matches: "7x12 Storage Unit (84 sq. ft.) - $43 / mo"
    # Groups: (width, length, sqft, price)
    _UNIT_RE = re.compile(
        r"(\d+(?:\.\d+)?)\s*[xX\u00d7]\s*(\d+(?:\.\d+)?)"  # WxL dimensions
        r".*?"  # "Storage Unit"
        r"\((\d+(?:\.\d+)?)\s*sq\.?\s*ft\.?\)"  # (sqft sq. ft.)
        r".*?\$"  # " - $"
        r"([\d,]+(?:\.\d+)?)"  # price digits
        r"\s*/\s*mo",  # " / mo"
        re.IGNORECASE | re.DOTALL,
    )

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

        # Find all Squarespace HTML content blocks
        content_blocks = soup.find_all("div", class_="sqs-html-content")

        for block in content_blocks:
            # Look for h3 headings that contain unit listings
            for heading in block.find_all(re.compile(r"h[1-6]")):
                heading_text = heading.get_text(strip=True)
                match = self._UNIT_RE.search(heading_text)
                if not match:
                    continue

                width = float(match.group(1))
                length = float(match.group(2))
                sqft = float(match.group(3))
                price_str = match.group(4).replace(",", "")
                price = float(price_str)

                size_label = f"{int(width)}' x {int(length)}'"

                # Collect description from sibling <p> tags in the same block
                description_parts = []
                for sibling in heading.find_next_siblings(["p", "ul"]):
                    sibling_text = sibling.get_text(separator=" ", strip=True)
                    if sibling_text:
                        description_parts.append(sibling_text)
                description = " ".join(description_parts) if description_parts else None

                unit = UnitResult(
                    size=size_label,
                    price=price,
                    description=description,
                    metadata={
                        "width": width,
                        "length": length,
                        "sqft": sqft,
                    },
                )
                result.units.append(unit)

        if not result.units:
            result.warnings.append("No unit headings found matching expected pattern")

        return result

Scrape Runs (5)

Run #163 Details

Status
exported
Parser Used
Facility017878Parser
Platform Detected
table_layout
Units Found
0
Stage Reached
exported
Timestamp
2026-03-14 05:00:24.048068
Timing
Stage Duration
Fetch3890ms
Detect28ms
Parse19ms
Export4ms

Snapshot: 017878_20260314T050027Z.html · Show Snapshot · Open in New Tab

No units found in this run.

All Failures for this Facility (5)

parse _WarningAsException scraper no_units_extracted warning Run #N/A | 2026-03-23 03:21:36.953966

No units extracted for 017878

Stack trace
src.reporting.failure_reporter._WarningAsException: No units extracted for 017878
parse _WarningAsException scraper no_units_extracted warning Run #N/A | 2026-03-21 19:14:56.273587

No units extracted for 017878

Stack trace
src.reporting.failure_reporter._WarningAsException: No units extracted for 017878
parse _WarningAsException scraper no_units_extracted warning Run #N/A | 2026-03-14 16:56:30.195770

No units extracted for 017878

Stack trace
src.reporting.failure_reporter._WarningAsException: No units extracted for 017878
parse _WarningAsException scraper no_units_extracted warning Run #N/A | 2026-03-14 05:00:28.003501

No units extracted for 017878

Stack trace
src.reporting.failure_reporter._WarningAsException: No units extracted for 017878
parse _WarningAsException scraper no_units_extracted warning Run #N/A | 2026-03-14 01:02:06.929500

No units extracted for 017878

Stack trace
src.reporting.failure_reporter._WarningAsException: No units extracted for 017878

← Back to dashboard