Cache postcodes

This commit is contained in:
Andras Schmelczer 2026-06-14 14:50:38 +01:00
parent f59d01227b
commit 8e4c56bb0d
7 changed files with 502 additions and 141 deletions

View file

@ -5,10 +5,12 @@ import tempfile
import time
from pathlib import Path
import shutdown
from constants import DATA_DIR, REPO_DIR
SOURCE_CHOICES = ("rightmove", "onthemarket", "zoopla", "all")
SOURCES = ("rightmove", "onthemarket", "zoopla")
SOURCE_CHOICES = (*SOURCES, "all")
TEST_MAX_PROPERTIES_PER_SOURCE = 100
TEST_OUTCODES = (
"E1",
@ -47,9 +49,13 @@ def parse_args() -> argparse.Namespace:
)
parser.add_argument(
"--source",
choices=SOURCE_CHOICES,
default="all",
help="Portal to scrape. 'all' runs Rightmove, OnTheMarket, and Zoopla.",
metavar="SOURCES",
help=(
"Comma-separated portal(s) to scrape: any of "
f"{', '.join(SOURCES)}, or 'all' (default). "
"E.g. --source rightmove,onthemarket."
),
)
parser.add_argument(
"--output-dir",
@ -100,15 +106,35 @@ def configure_logging() -> None:
def selected_sources(source: str) -> list[str]:
if source == "all":
return ["rightmove", "onthemarket", "zoopla"]
return [source]
"""Resolve --source: one or more comma-separated portals, or 'all'.
Accepts e.g. ``rightmove,onthemarket`` (whitespace and case tolerant).
Unknown values are rejected; the result is deduplicated and returned in the
canonical ``SOURCES`` order so downstream merge/dedup stays deterministic."""
requested = [part.strip().lower() for part in source.split(",")]
requested = [part for part in requested if part]
if not requested:
raise SystemExit("--source was empty")
unknown = sorted(set(requested) - set(SOURCE_CHOICES))
if unknown:
raise SystemExit(
f"Unknown --source value(s): {', '.join(unknown)}. "
f"Choose from {', '.join(SOURCE_CHOICES)} (comma-separated)."
)
if "all" in requested:
return list(SOURCES)
return [portal for portal in SOURCES if portal in requested]
def main() -> int:
args = parse_args()
configure_standalone_runtime()
configure_logging()
# Ctrl+C (and SIGTERM, e.g. `docker stop`) asks the scrapers to wind down
# gracefully — each source stops at its next outcode boundary and the run
# still persists detail caches and writes the listings collected so far.
shutdown.install_signal_handlers()
if args.limit_outcodes is not None and args.limit_outcodes < 1:
raise SystemExit("--limit-outcodes must be greater than zero")
@ -182,6 +208,14 @@ def main() -> int:
)
elapsed = time.monotonic() - started
if shutdown.stop_requested():
log.warning(
"Scrape interrupted after %.1fs; partial results written to %s",
elapsed,
result.get("path"),
)
log.info("Result: %s", result)
return 130 # 128 + SIGINT, the conventional Ctrl+C exit code.
log.info("Scrape finished in %.1fs", elapsed)
log.info("Result: %s", result)
if args.test and result.get("errors"):