Cache postcodes
This commit is contained in:
parent
f59d01227b
commit
8e4c56bb0d
7 changed files with 502 additions and 141 deletions
|
|
@ -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"):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue