Facility: 003456

Stor N Lok

Stale Data Warning: This facility has not been successfully scraped in 26 days (threshold: 3 days). Data may be outdated.
Facility Information active
Facility ID
003456
Name
Stor N Lok
URL
https://cisnorthplatte.com/properties/stor-n-lok/
Address
1111 E 7th St, North Platte, NE 69101, USA, North Platte, Nebraska 69101
Platform
custom_facility_003456
Parser File
src/parsers/custom/facility_003456_parser.py
Last Scraped
2026-03-27 13:56:12.545805
Created
2026-03-14 16:21:53.706708
Updated
2026-03-27 13:56:12.591103
Parser & Healing Diagnosis working
Parser Status
✓ Working
Status Reason
N/A
Last Healing Attempt
Not attempted
Parser Source (src/parsers/custom/facility_003456_parser.py)
"""Parser for Stor N Lok (cisnorthplatte.com)."""

from __future__ import annotations

import re

from bs4 import BeautifulSoup

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


class Facility003456Parser(BaseParser):
    """Extract storage units from Stor N Lok.

    The facility page lists unit sizes in <div> elements under a description
    section.  Each line has the format:  WxH  door_type,
    where door_type is W (walk-through) or O (overhead).
    No pricing is published; prices require calling the facility.
    """

    platform = "custom_facility_003456"

    # Matches e.g. "4×8", "10 x 20", "5x10"
    _SIZE_RE = re.compile(r"(\d+)\s*[xX\u00d7]\s*(\d+)")

    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()

        # Look for the description section containing unit sizes
        description_section = soup.find("section", class_="description-wrap")
        if not description_section:
            description_section = soup

        seen: set[tuple[int, int]] = set()

        for div in description_section.find_all("div"):
            text = div.get_text(strip=True)
            if not text:
                continue

            matches = list(self._SIZE_RE.finditer(text))
            if not matches:
                continue

            for match in matches:
                width = int(match.group(1))
                length = int(match.group(2))

                # Skip tiny dimensions (favicon sizes, etc.)
                if width < 3 or length < 3:
                    continue

                key = (width, length)
                if key in seen:
                    continue
                seen.add(key)

                size_str = f"{width}x{length}"
                unit = UnitResult()
                unit.size = size_str
                w_norm, l_norm, sqft = self.normalize_size(size_str)
                if w_norm is not None:
                    unit.metadata = {"width": w_norm, "length": l_norm, "sqft": sqft}

                door_type = self._extract_door_type(text)
                if door_type:
                    unit.description = f"{size_str} - {door_type}"
                    if unit.metadata:
                        unit.metadata["door_type"] = door_type
                else:
                    unit.description = size_str

                result.units.append(unit)

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

        return result

    @staticmethod
    def _extract_door_type(text: str) -> str | None:
        """Extract door type from text like '4x8  W,' or '10x10  O,'."""
        if re.search(r"\bO\s*&\s*W\b", text):
            return "Overhead & Walk-through"
        if re.search(r"\bW\b", text) and not re.search(r"\bO\b", text):
            return "Walk-through"
        if re.search(r"\bO\b", text) and not re.search(r"\bW\b", text):
            return "Overhead"
        return None

Scrape Runs (5)

Run #1225 Details

Status
exported
Parser Used
Facility003456Parser
Platform Detected
table_layout
Units Found
16
Stage Reached
exported
Timestamp
2026-03-23 02:57:21.622986
Timing
Stage Duration
Fetch8604ms
Detect36ms
Parse29ms
Export5ms

Snapshot: 003456_20260323T025730Z.html · Show Snapshot · Open in New Tab

Parsed Units (16)

4x8

No price

5x9

No price

5x10

No price

7x7

No price

6x10

No price

5x16

No price

6x15

No price

5x20

No price

7x15

No price

7x16

No price

8x14

No price

10x10

No price

10x15

No price

10x20

No price

10x30

No price

10x50

No price

← Back to dashboard