Facility: 003456
Stor N Lok
- 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 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)
-
exported Run #19252026-03-27 13:56:08.798003 | 16 units | Facility003456Parser | View Data →
-
exported Run #19242026-03-27 13:56:08.724775 | 16 units | Facility003456Parser | View Data →
-
exported Run #12252026-03-23 02:57:21.622986 | 16 units | Facility003456Parser | View Data →
-
exported Run #7322026-03-21 18:48:59.217562 | 16 units | Facility003456Parser | View Data →
-
exported Run #2812026-03-14 16:30:34.877125 | 16 units | Facility003456Parser | View Data →
Run #1925 Details
- Status
- exported
- Parser Used
- Facility003456Parser
- Platform Detected
- table_layout
- Units Found
- 16
- Stage Reached
- exported
- Timestamp
- 2026-03-27 13:56:08.798003
Timing
| Stage | Duration |
|---|---|
| Fetch | 3623ms |
| Detect | 22ms |
| Parse | 12ms |
| Export | 23ms |
Snapshot: 003456_20260327T135612Z.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