Facility: 003661

Century Square Self Storage

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
003661
Name
Century Square Self Storage
URL
http://www.soundselfstorage.com/century-square-self-storage/
Address
1120 South 324th St, Federal Way, WA 98003, USA, Federal Way, Washington 98003
Platform
custom_facility_003661
Parser File
src/parsers/custom/facility_003661_parser.py
Last Scraped
2026-03-27 13:56:56.572329
Created
2026-03-14 16:21:53.706708
Updated
2026-03-27 13:56:56.602940
Parser & Healing Diagnosis working
Parser Status
✓ Working
Status Reason
N/A
Last Healing Attempt
Not attempted
Parser Source (src/parsers/custom/facility_003661_parser.py)
"""Parser for KJ Storage (storewithkjstorage.com)."""

from __future__ import annotations

import json

from bs4 import BeautifulSoup

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


class Facility003661Parser(BaseParser):
    """Extract storage units from KJ Storage via embedded JSON in <pre> tags."""

    platform = "custom_facility_003661"

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

        # Unit data is embedded as JSON inside <pre> tags within tables.
        # Each <pre> contains a JSON object with dcWidth, dcLength, dcPreferredRate, sTypeName.
        # The first <pre> is a summary record (fields are dicts with min/max); skip those.
        for pre in soup.find_all("pre"):
            text = pre.get_text(strip=True)
            if not text.startswith("{"):
                continue
            try:
                data = json.loads(text)
            except (json.JSONDecodeError, ValueError):
                continue

            width = data.get("dcWidth")
            length = data.get("dcLength")
            rate = data.get("dcPreferredRate")
            type_name = data.get("sTypeName", "")

            # Skip summary records where fields are dicts (not scalar values)
            if isinstance(width, dict) or isinstance(length, dict):
                continue
            if not (width and length):
                continue

            size_str = f"{int(width)}x{int(length)}"
            size = self.normalize_size(size_str)
            price = self.normalize_price(str(rate)) if rate is not None else None
            climate = data.get("bClimate", False)
            type_name_str = type_name if isinstance(type_name, str) else ""
            desc_parts = [type_name_str] if type_name_str else []
            if climate:
                desc_parts.append("Climate Controlled")
            if "parking" in type_name_str.lower() or "vehicle" in type_name_str.lower():
                desc_parts.append("Parking")

            vacant = data.get("iTotalVacant", 1)
            scarcity = None if (vacant and int(vacant) > 0) else "sold out"

            result.units.append(
                UnitResult(
                    size=size,
                    price=price,
                    description=" | ".join(desc_parts) if desc_parts else None,
                    scarcity=scarcity,
                )
            )

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

Scrape Runs (5)

Run #1945 Details

Status
exported
Parser Used
Facility003661Parser
Platform Detected
storageunitsoftware
Units Found
14
Stage Reached
exported
Timestamp
2026-03-27 13:56:45.242430
Timing
Stage Duration
Fetch10953ms
Detect81ms
Parse36ms
Export18ms

Snapshot: 003661_20260327T135656Z.html · Show Snapshot · Open in New Tab

Parsed Units (14)

(4.0,5.0,20.0)

$50.00/mo

(5.0,5.0,25.0)

$70.00/mo

(4.0,10.0,40.0)

$89.00/mo

(5.0,10.0,50.0)

$85.00/mo

(5.0,15.0,75.0)

$125.00/mo

(10.0,10.0,100.0)

$145.00/mo

(10.0,15.0,150.0)

$220.00/mo
sold out

(10.0,15.0,150.0)

$275.00/mo

(9.0,20.0,180.0)

$160.00/mo
sold out

(10.0,20.0,200.0)

$299.00/mo

(10.0,25.0,250.0)

$278.00/mo
sold out

(10.0,25.0,250.0)

$325.00/mo
sold out

(10.0,30.0,300.0)

$370.00/mo
sold out

(10.0,35.0,350.0)

$455.00/mo
sold out

← Back to dashboard