-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.py
More file actions
126 lines (109 loc) · 3.64 KB
/
main.py
File metadata and controls
126 lines (109 loc) · 3.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
import argparse
import logging
import os
import sys
from config import DEFAULT_OUTPUT_PATH, DEFAULT_NUM_HOMES, DEFAULT_SORT
from models import ListingCollection
from scraper import RedfinScraper
def setup_logging(verbose: bool = False) -> None:
level = logging.DEBUG if verbose else logging.INFO
logging.basicConfig(
level=level,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
handlers=[logging.StreamHandler(sys.stdout)],
)
def main() -> int:
parser = argparse.ArgumentParser(
description="Scrape rental listings from Redfin.com via ScrapingAnt"
)
parser.add_argument(
"--polygon",
required=True,
help=(
"Search polygon coordinates: 'lon1 lat1,lon2 lat2,...,lon1 lat1'. "
"Example: '-116.50089 43.30739,-115.96191 43.30739,"
"-115.96191 43.85001,-116.50089 43.85001,-116.50089 43.30739'"
),
)
parser.add_argument(
"--market",
default="boise",
help="Redfin market identifier (default: boise)",
)
parser.add_argument(
"--num-homes",
type=int,
default=DEFAULT_NUM_HOMES,
help=f"Max number of homes to request (default: {DEFAULT_NUM_HOMES})",
)
parser.add_argument(
"--sort",
default=DEFAULT_SORT,
help=f"Sort order (default: {DEFAULT_SORT})",
)
parser.add_argument(
"--uipt",
default="1,2,3",
help="Property types: 1=House,2=Condo,3=Townhouse (default: 1,2,3)",
)
parser.add_argument(
"--sf",
default="1,2,3,5,6,7",
help="Search filters (default: 1,2,3,5,6,7)",
)
parser.add_argument(
"--output", "-o", default=DEFAULT_OUTPUT_PATH, help="Output CSV file path"
)
parser.add_argument(
"--json", action="store_true", help="Also export as JSON"
)
parser.add_argument(
"--api-key", help="ScrapingAnt API key (or set SCRAPINGANT_API_KEY env var)"
)
parser.add_argument(
"--verbose", "-v", action="store_true", help="Enable verbose logging"
)
args = parser.parse_args()
setup_logging(args.verbose)
logger = logging.getLogger(__name__)
api_key = args.api_key or os.environ.get("SCRAPINGANT_API_KEY", "")
if not api_key:
logger.error(
"ScrapingAnt API key required. Set SCRAPINGANT_API_KEY env var or use --api-key."
)
return 1
try:
scraper = RedfinScraper(api_key=api_key)
listings = scraper.scrape(
polygon=args.polygon,
market=args.market,
num_homes=args.num_homes,
sort=args.sort,
uipt=args.uipt,
sf=args.sf,
)
if not listings:
logger.warning("No listings found.")
return 1
collection = ListingCollection()
added = collection.add_many(listings)
logger.info(f"Collected {added} unique listings ({len(listings)} total)")
# Ensure output directory exists
output_dir = os.path.dirname(args.output)
if output_dir:
os.makedirs(output_dir, exist_ok=True)
collection.to_csv(args.output)
logger.info(f"Saved {len(collection)} listings to {args.output}")
if args.json:
json_path = args.output.rsplit(".", 1)[0] + ".json"
collection.to_json(json_path)
logger.info(f"Saved JSON to {json_path}")
return 0
except ValueError as e:
logger.error(f"Configuration error: {e}")
return 1
except KeyboardInterrupt:
logger.info("Interrupted by user")
return 0
if __name__ == "__main__":
sys.exit(main())