-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.py
More file actions
155 lines (134 loc) · 4.4 KB
/
Copy pathmain.py
File metadata and controls
155 lines (134 loc) · 4.4 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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
#!/usr/bin/env python3
"""Main entry point for Logic-Immo scraper."""
import argparse
import csv
import logging
import sys
from typing import List
from scraper import LogicImmoScraper
from models import Property
from config import LOCATIONS, CONTRACT_TYPES, PROPERTY_TYPES, DEFAULT_MAX_WORKERS
def setup_logging(verbose: bool = False) -> None:
"""Configure logging settings."""
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 export_to_csv(properties: List[Property], output_path: str) -> None:
"""Export properties to CSV file.
Args:
properties: List of Property objects
output_path: Path to output CSV file
"""
if not properties:
logging.warning("No properties to export")
return
fieldnames = list(properties[0].to_dict().keys())
with open(output_path, "w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=fieldnames, quoting=csv.QUOTE_MINIMAL)
writer.writeheader()
for prop in properties:
writer.writerow(prop.to_dict())
logging.info(f"Exported {len(properties)} properties to {output_path}")
def main():
"""Main entry point."""
parser = argparse.ArgumentParser(
description="Scrape property listings from Logic-Immo.com",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
%(prog)s --location paris --property apartment
%(prog)s --location lyon --contract rent --property house
%(prog)s --location marseille --max-pages 5
%(prog)s --location toulouse --limit 100
%(prog)s --location nice --max-pages 3 --no-details
%(prog)s --location bordeaux --max-pages 3 -v
""",
)
parser.add_argument(
"--location", "-l",
default="paris",
help=f"Location to search. Available: {', '.join(LOCATIONS.keys())} (default: paris)",
)
parser.add_argument(
"--contract", "-c",
choices=list(CONTRACT_TYPES.keys()),
default="buy",
help="Contract type (default: buy)",
)
parser.add_argument(
"--property", "-p",
choices=list(PROPERTY_TYPES.keys()),
default="all",
help="Property type (default: all)",
)
parser.add_argument(
"--output", "-o",
default="properties.csv",
help="Output CSV file path (default: properties.csv)",
)
parser.add_argument(
"--limit",
type=int,
help="Maximum number of properties to scrape",
)
parser.add_argument(
"--max-pages",
type=int,
help="Maximum number of listing pages to scrape",
)
parser.add_argument(
"--max-workers", "-w",
type=int,
default=DEFAULT_MAX_WORKERS,
help=f"Maximum parallel requests (default: {DEFAULT_MAX_WORKERS})",
)
parser.add_argument(
"--no-details",
action="store_true",
help="Skip fetching detail pages (faster but less data)",
)
parser.add_argument(
"--api-key", "-k",
help="ScrapingAnt API key (overrides environment variable)",
)
parser.add_argument(
"--verbose", "-v",
action="store_true",
help="Enable verbose logging",
)
args = parser.parse_args()
setup_logging(args.verbose)
logger = logging.getLogger(__name__)
try:
scraper = LogicImmoScraper(
api_key=args.api_key,
max_workers=args.max_workers,
)
properties = scraper.scrape(
location=args.location,
contract_type=args.contract,
property_type=args.property,
max_pages=args.max_pages,
limit=args.limit,
fetch_details=not args.no_details,
)
if properties:
export_to_csv(properties, args.output)
logger.info(f"Successfully scraped {len(properties)} properties")
else:
logger.warning("No properties found")
sys.exit(1)
except ValueError as e:
logger.error(f"Configuration error: {e}")
sys.exit(1)
except KeyboardInterrupt:
logger.info("Scraping interrupted by user")
sys.exit(130)
except Exception as e:
logger.exception(f"Unexpected error: {e}")
sys.exit(1)
if __name__ == "__main__":
main()