carinfo-py 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Bhavya Kachhadiya
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,204 @@
1
+ Metadata-Version: 2.4
2
+ Name: carinfo-py
3
+ Version: 0.1.0
4
+ Summary: A lightweight Python client and scraper for CarInfo vehicle RC details and VIP numbers.
5
+ Author: Bhavya Kachhadiya
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/bhavyakachhadiya/carinfo-py
8
+ Project-URL: Repository, https://github.com/bhavyakachhadiya/carinfo-py
9
+ Keywords: carinfo,rc-details,vahan,rto,vehicle-info,vip-numbers,scraper
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.8
14
+ Classifier: Programming Language :: Python :: 3.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
19
+ Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
20
+ Requires-Python: >=3.8
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+ Requires-Dist: cryptography>=3.4.0
24
+ Provides-Extra: dev
25
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
26
+ Requires-Dist: build>=0.10.0; extra == "dev"
27
+ Requires-Dist: twine>=4.0.0; extra == "dev"
28
+ Dynamic: license-file
29
+ Dynamic: requires-python
30
+
31
+ # carinfo-py 🚗⚡
32
+
33
+ [![PyPI version](https://img.shields.io/pypi/v/carinfo-py.svg)](https://pypi.org/project/carinfo-py/)
34
+ [![Python versions](https://img.shields.io/pypi/pyversions/carinfo-py.svg)](https://pypi.org/project/carinfo-py/)
35
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
36
+
37
+ A lightweight, high-performance Python library and CLI tool for fetching vehicle registration (RC) details, owner information, RTO data, and VIP/fancy numbers from CarInfo.
38
+
39
+ ---
40
+
41
+ ## ✨ Features
42
+
43
+ - **No Heavy Browser Required:** Directly decrypts the Next.js AES payload in pure Python using `cryptography`. Up to **10x faster** than browser-based scrapers.
44
+ - **Rich Data Fields:**
45
+ - Registration Number
46
+ - Make & Model
47
+ - Owner Name (Masked for privacy)
48
+ - Registered RTO Office, Address, State & Phone
49
+ - Insurance Expiry Date & Flags
50
+ - Vehicle Catalog Image URL
51
+ - **VIP & Fancy Number Scraper:**
52
+ - Built-in database of Indian / Gujarat RTO VIP and Fancy numbers (e.g., `0001`–`0009`, `1111`–`9999`, `1000`–`9000`, `0786`, `8055`, palindromes, repeating pairs).
53
+ - **Export Options:** Seamless export to structured **JSON** and **CSV**.
54
+ - **CLI Included:** Comes with a `carinfo` command-line utility for quick terminal lookups.
55
+
56
+ ---
57
+
58
+ ## 📦 Installation
59
+
60
+ Install from PyPI:
61
+ ```bash
62
+ pip install carinfo-py
63
+ ```
64
+
65
+ Or install directly from source / GitHub:
66
+ ```bash
67
+ git clone https://github.com/bhavyakachhadiya/carinfo-py.git
68
+ cd carinfo-py
69
+ pip install .
70
+ ```
71
+
72
+ ---
73
+
74
+ ## 🚀 Python Library Usage
75
+
76
+ ### 1. Fetch a Single Vehicle
77
+
78
+ ```python
79
+ from carinfo import CarInfoClient
80
+
81
+ client = CarInfoClient()
82
+ vehicle = client.get_rc_details("GJ11CQ0786")
83
+
84
+ print("Status:", vehicle.status) # Found
85
+ print("Make & Model:", vehicle.make_and_model) # I20 SPORTZ(O) 1.2 KAPPA MT SE
86
+ print("Owner Name:", vehicle.owner_name) # S****A A****M
87
+ print("RTO Office:", vehicle.registered_rto) # Majevadi Darvaja, Junagadh, Gujarat - 362001
88
+ print("Insurance Expiry:", vehicle.insurance_expiry) # 06-Jan-2029
89
+ print("Image URL:", vehicle.image_url)
90
+ ```
91
+
92
+ ### 2. Scrape a Sequential Range (e.g., 0001 to 0020)
93
+
94
+ ```python
95
+ from carinfo import CarInfoClient
96
+
97
+ client = CarInfoClient()
98
+
99
+ # Scrapes GJ11CQ0001 to GJ11CQ0020, saves to JSON & CSV
100
+ results = client.scrape_range(
101
+ prefix="GJ11CQ",
102
+ start=1,
103
+ end=20,
104
+ delay_range=(1.2, 2.5), # Randomized polite jitter
105
+ save_csv="rc_gj11cq.csv",
106
+ save_json="rc_gj11cq.json"
107
+ )
108
+
109
+ print(f"Scraped {len(results)} records.")
110
+ ```
111
+
112
+ ### 3. Scrape VIP / Fancy Numbers
113
+
114
+ ```python
115
+ from carinfo import CarInfoClient
116
+
117
+ client = CarInfoClient()
118
+
119
+ # mode='top' fetches 58 core VIP numbers (0001-0009, 1111-9999, 1000-9000, 0786, 8055...)
120
+ # mode='fancy' fetches 173 extended auction numbers (palindromes, pairs, sequentials)
121
+ vip_records = client.scrape_vip(
122
+ prefix="GJ03QD",
123
+ mode="top",
124
+ save_csv="rc_gj03qd_vip.csv",
125
+ save_json="rc_gj03qd_vip.json"
126
+ )
127
+ ```
128
+
129
+ ---
130
+
131
+ ## 💻 Command Line Interface (CLI)
132
+
133
+ After installing `carinfo-py`, use the `carinfo` command in your terminal:
134
+
135
+ ### Look up a single car:
136
+ ```bash
137
+ carinfo get GJ11CQ0786
138
+ ```
139
+
140
+ ### Scrape a range:
141
+ ```bash
142
+ carinfo scrape GJ11CQ --start 1 --end 20 --csv rc_gj11cq.csv --json rc_gj11cq.json
143
+ ```
144
+
145
+ ### Scrape VIP numbers:
146
+ ```bash
147
+ # Top 58 VIP numbers
148
+ carinfo vip GJ11CQ --mode top
149
+
150
+ # All 173 Fancy auction numbers
151
+ carinfo vip GJ03QD --mode fancy
152
+ ```
153
+
154
+ ---
155
+
156
+ ## 📤 How to Build and Publish to PyPI
157
+
158
+ If you are the package maintainer and want to publish `carinfo-py` to [PyPI](https://pypi.org/):
159
+
160
+ ### 1. Install Build and Publishing Tools
161
+ ```bash
162
+ pip install --upgrade build twine
163
+ ```
164
+
165
+ ### 2. Build Source Distribution and Wheel
166
+ From the project root directory:
167
+ ```bash
168
+ python3 -m build
169
+ ```
170
+ This generates `.tar.gz` and `.whl` files inside the `dist/` directory:
171
+ ```
172
+ dist/
173
+ ├── carinfo_py-0.1.0-py3-none-any.whl
174
+ └── carinfo_py-0.1.0.tar.gz
175
+ ```
176
+
177
+ ### 3. Check Package Integrity
178
+ ```bash
179
+ twine check dist/*
180
+ ```
181
+
182
+ ### 4. Upload to TestPyPI (Recommended First Step)
183
+ To test without affecting production:
184
+ ```bash
185
+ twine upload --repository testpypi dist/*
186
+ ```
187
+
188
+ ### 5. Upload to Official PyPI
189
+ Once tested, upload to the live Python Package Index:
190
+ ```bash
191
+ twine upload dist/*
192
+ ```
193
+ *You will be prompted for your PyPI API token (`pypi-...`).*
194
+
195
+ Users will then be able to install your package via:
196
+ ```bash
197
+ pip install carinfo-py
198
+ ```
199
+
200
+ ---
201
+
202
+ ## 📄 License
203
+
204
+ This project is licensed under the [MIT License](LICENSE).
@@ -0,0 +1,174 @@
1
+ # carinfo-py 🚗⚡
2
+
3
+ [![PyPI version](https://img.shields.io/pypi/v/carinfo-py.svg)](https://pypi.org/project/carinfo-py/)
4
+ [![Python versions](https://img.shields.io/pypi/pyversions/carinfo-py.svg)](https://pypi.org/project/carinfo-py/)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
+
7
+ A lightweight, high-performance Python library and CLI tool for fetching vehicle registration (RC) details, owner information, RTO data, and VIP/fancy numbers from CarInfo.
8
+
9
+ ---
10
+
11
+ ## ✨ Features
12
+
13
+ - **No Heavy Browser Required:** Directly decrypts the Next.js AES payload in pure Python using `cryptography`. Up to **10x faster** than browser-based scrapers.
14
+ - **Rich Data Fields:**
15
+ - Registration Number
16
+ - Make & Model
17
+ - Owner Name (Masked for privacy)
18
+ - Registered RTO Office, Address, State & Phone
19
+ - Insurance Expiry Date & Flags
20
+ - Vehicle Catalog Image URL
21
+ - **VIP & Fancy Number Scraper:**
22
+ - Built-in database of Indian / Gujarat RTO VIP and Fancy numbers (e.g., `0001`–`0009`, `1111`–`9999`, `1000`–`9000`, `0786`, `8055`, palindromes, repeating pairs).
23
+ - **Export Options:** Seamless export to structured **JSON** and **CSV**.
24
+ - **CLI Included:** Comes with a `carinfo` command-line utility for quick terminal lookups.
25
+
26
+ ---
27
+
28
+ ## 📦 Installation
29
+
30
+ Install from PyPI:
31
+ ```bash
32
+ pip install carinfo-py
33
+ ```
34
+
35
+ Or install directly from source / GitHub:
36
+ ```bash
37
+ git clone https://github.com/bhavyakachhadiya/carinfo-py.git
38
+ cd carinfo-py
39
+ pip install .
40
+ ```
41
+
42
+ ---
43
+
44
+ ## 🚀 Python Library Usage
45
+
46
+ ### 1. Fetch a Single Vehicle
47
+
48
+ ```python
49
+ from carinfo import CarInfoClient
50
+
51
+ client = CarInfoClient()
52
+ vehicle = client.get_rc_details("GJ11CQ0786")
53
+
54
+ print("Status:", vehicle.status) # Found
55
+ print("Make & Model:", vehicle.make_and_model) # I20 SPORTZ(O) 1.2 KAPPA MT SE
56
+ print("Owner Name:", vehicle.owner_name) # S****A A****M
57
+ print("RTO Office:", vehicle.registered_rto) # Majevadi Darvaja, Junagadh, Gujarat - 362001
58
+ print("Insurance Expiry:", vehicle.insurance_expiry) # 06-Jan-2029
59
+ print("Image URL:", vehicle.image_url)
60
+ ```
61
+
62
+ ### 2. Scrape a Sequential Range (e.g., 0001 to 0020)
63
+
64
+ ```python
65
+ from carinfo import CarInfoClient
66
+
67
+ client = CarInfoClient()
68
+
69
+ # Scrapes GJ11CQ0001 to GJ11CQ0020, saves to JSON & CSV
70
+ results = client.scrape_range(
71
+ prefix="GJ11CQ",
72
+ start=1,
73
+ end=20,
74
+ delay_range=(1.2, 2.5), # Randomized polite jitter
75
+ save_csv="rc_gj11cq.csv",
76
+ save_json="rc_gj11cq.json"
77
+ )
78
+
79
+ print(f"Scraped {len(results)} records.")
80
+ ```
81
+
82
+ ### 3. Scrape VIP / Fancy Numbers
83
+
84
+ ```python
85
+ from carinfo import CarInfoClient
86
+
87
+ client = CarInfoClient()
88
+
89
+ # mode='top' fetches 58 core VIP numbers (0001-0009, 1111-9999, 1000-9000, 0786, 8055...)
90
+ # mode='fancy' fetches 173 extended auction numbers (palindromes, pairs, sequentials)
91
+ vip_records = client.scrape_vip(
92
+ prefix="GJ03QD",
93
+ mode="top",
94
+ save_csv="rc_gj03qd_vip.csv",
95
+ save_json="rc_gj03qd_vip.json"
96
+ )
97
+ ```
98
+
99
+ ---
100
+
101
+ ## 💻 Command Line Interface (CLI)
102
+
103
+ After installing `carinfo-py`, use the `carinfo` command in your terminal:
104
+
105
+ ### Look up a single car:
106
+ ```bash
107
+ carinfo get GJ11CQ0786
108
+ ```
109
+
110
+ ### Scrape a range:
111
+ ```bash
112
+ carinfo scrape GJ11CQ --start 1 --end 20 --csv rc_gj11cq.csv --json rc_gj11cq.json
113
+ ```
114
+
115
+ ### Scrape VIP numbers:
116
+ ```bash
117
+ # Top 58 VIP numbers
118
+ carinfo vip GJ11CQ --mode top
119
+
120
+ # All 173 Fancy auction numbers
121
+ carinfo vip GJ03QD --mode fancy
122
+ ```
123
+
124
+ ---
125
+
126
+ ## 📤 How to Build and Publish to PyPI
127
+
128
+ If you are the package maintainer and want to publish `carinfo-py` to [PyPI](https://pypi.org/):
129
+
130
+ ### 1. Install Build and Publishing Tools
131
+ ```bash
132
+ pip install --upgrade build twine
133
+ ```
134
+
135
+ ### 2. Build Source Distribution and Wheel
136
+ From the project root directory:
137
+ ```bash
138
+ python3 -m build
139
+ ```
140
+ This generates `.tar.gz` and `.whl` files inside the `dist/` directory:
141
+ ```
142
+ dist/
143
+ ├── carinfo_py-0.1.0-py3-none-any.whl
144
+ └── carinfo_py-0.1.0.tar.gz
145
+ ```
146
+
147
+ ### 3. Check Package Integrity
148
+ ```bash
149
+ twine check dist/*
150
+ ```
151
+
152
+ ### 4. Upload to TestPyPI (Recommended First Step)
153
+ To test without affecting production:
154
+ ```bash
155
+ twine upload --repository testpypi dist/*
156
+ ```
157
+
158
+ ### 5. Upload to Official PyPI
159
+ Once tested, upload to the live Python Package Index:
160
+ ```bash
161
+ twine upload dist/*
162
+ ```
163
+ *You will be prompted for your PyPI API token (`pypi-...`).*
164
+
165
+ Users will then be able to install your package via:
166
+ ```bash
167
+ pip install carinfo-py
168
+ ```
169
+
170
+ ---
171
+
172
+ ## 📄 License
173
+
174
+ This project is licensed under the [MIT License](LICENSE).
@@ -0,0 +1,56 @@
1
+ """CarInfo Python Library: Fetch Indian vehicle RC details and VIP numbers."""
2
+
3
+ from .client import CarInfo, CarInfoClient
4
+ from .models import VehicleDetails, RtoDetails, InsuranceDetails
5
+ from .transport import BaseTransport, UrllibTransport
6
+ from .providers.base import BaseVehicleProvider
7
+ from .providers.carinfo import CarInfoProvider
8
+ from .exporters import BaseExporter, JsonExporter, CsvExporter
9
+ from .scraper import BatchScraper
10
+ from .vip import get_vip_numbers, TOP_VIP_NUMBERS, EXTENDED_FANCY_NUMBERS
11
+ from .crypto import decrypt_carinfo_payload
12
+ from .exceptions import (
13
+ CarInfoError,
14
+ RateLimitError,
15
+ VehicleNotFoundError,
16
+ DecryptionError,
17
+ NetworkError,
18
+ CarInfoRateLimitError
19
+ )
20
+
21
+ __version__ = "0.1.0"
22
+
23
+ __all__ = [
24
+ # Main Facade
25
+ "CarInfo",
26
+ "CarInfoClient",
27
+ # Domain Models
28
+ "VehicleDetails",
29
+ "RtoDetails",
30
+ "InsuranceDetails",
31
+ # Transport Abstraction
32
+ "BaseTransport",
33
+ "UrllibTransport",
34
+ # Provider Abstraction
35
+ "BaseVehicleProvider",
36
+ "CarInfoProvider",
37
+ # Exporters
38
+ "BaseExporter",
39
+ "JsonExporter",
40
+ "CsvExporter",
41
+ # Scraping Engine
42
+ "BatchScraper",
43
+ # VIP Catalogs
44
+ "get_vip_numbers",
45
+ "TOP_VIP_NUMBERS",
46
+ "EXTENDED_FANCY_NUMBERS",
47
+ # Crypto
48
+ "decrypt_carinfo_payload",
49
+ # Exceptions
50
+ "CarInfoError",
51
+ "RateLimitError",
52
+ "VehicleNotFoundError",
53
+ "DecryptionError",
54
+ "NetworkError",
55
+ "CarInfoRateLimitError"
56
+ ]
@@ -0,0 +1,110 @@
1
+ """Command-line interface (CLI) for the carinfo package."""
2
+
3
+ import argparse
4
+ import sys
5
+ from .client import CarInfoClient
6
+ from .models import VehicleDetails
7
+
8
+
9
+ def main():
10
+ parser = argparse.ArgumentParser(
11
+ prog="carinfo",
12
+ description="CarInfo Python CLI: Fetch vehicle details and scrape series from CarInfo"
13
+ )
14
+ subparsers = parser.add_subparsers(dest="command", help="Available commands")
15
+
16
+ # Subcommand: get
17
+ get_parser = subparsers.add_parser("get", help="Fetch details for a single vehicle")
18
+ get_parser.add_argument("rc", help="Vehicle Registration Number (e.g. GJ11CQ0786)")
19
+
20
+ # Subcommand: scrape
21
+ scrape_parser = subparsers.add_parser("scrape", help="Scrape a range of registration numbers")
22
+ scrape_parser.add_argument("prefix", help="Series prefix (e.g. GJ11CQ)")
23
+ scrape_parser.add_argument("--start", type=int, default=1, help="Start number (default: 1)")
24
+ scrape_parser.add_argument("--end", type=int, default=20, help="End number (default: 20)")
25
+ scrape_parser.add_argument("--json", dest="json_file", help="Save output to JSON file")
26
+ scrape_parser.add_argument("--csv", dest="csv_file", help="Save output to CSV file")
27
+
28
+ # Subcommand: vip
29
+ vip_parser = subparsers.add_parser("vip", help="Scrape VIP / fancy numbers for a series")
30
+ vip_parser.add_argument("prefix", help="Series prefix (e.g. GJ11CQ)")
31
+ vip_parser.add_argument("--mode", choices=["top", "fancy"], default="top", help="VIP mode: 'top' (58) or 'fancy' (173)")
32
+ vip_parser.add_argument("--json", dest="json_file", help="Save output to JSON file")
33
+ vip_parser.add_argument("--csv", dest="csv_file", help="Save output to CSV file")
34
+
35
+ args = parser.parse_args()
36
+
37
+ if not args.command:
38
+ parser.print_help()
39
+ sys.exit(1)
40
+
41
+ client = CarInfoClient()
42
+
43
+ if args.command == "get":
44
+ print(f"Fetching vehicle details for {args.rc}...")
45
+ res = client.get_rc_details(args.rc)
46
+ print("\n--- Vehicle Details ---")
47
+ print(f"RC Number: {res.rc_number}")
48
+ print(f"Status: {res.status}")
49
+ if res.status == "Found":
50
+ print(f"Make & Model: {res.make_and_model}")
51
+ print(f"Owner Name: {res.owner_name}")
52
+ print(f"RTO: {res.rto_number} ({res.registered_rto})")
53
+ print(f"State: {res.state}")
54
+ print(f"Insurance Expiry: {res.insurance_expiry or 'N/A'}")
55
+ if res.image_url:
56
+ print(f"Image: {res.image_url}")
57
+ print("-----------------------\n")
58
+
59
+ elif args.command == "scrape":
60
+ clean_prefix = args.prefix.upper().replace(" ", "")
61
+ json_out = args.json_file or f"rc_{clean_prefix.lower()}.json"
62
+ csv_out = args.csv_file or f"rc_{clean_prefix.lower()}.csv"
63
+
64
+ print(f"Scraping {clean_prefix} range {args.start:04d} to {args.end:04d}...")
65
+ print(f"Output: {json_out} / {csv_out}")
66
+
67
+ def progress(curr, total, item: VehicleDetails):
68
+ if item.status == "Found":
69
+ print(f"[{curr}/{total}] {item.rc_number} ✓ {item.make_and_model} | {item.owner_name}")
70
+ else:
71
+ print(f"[{curr}/{total}] {item.rc_number} - {item.status}")
72
+
73
+ results = client.scrape_range(
74
+ clean_prefix,
75
+ start=args.start,
76
+ end=args.end,
77
+ save_csv=csv_out,
78
+ save_json=json_out,
79
+ progress_callback=progress
80
+ )
81
+ print(f"\nDone! Scraped {len(results)} records.")
82
+
83
+ elif args.command == "vip":
84
+ clean_prefix = args.prefix.upper().replace(" ", "")
85
+ json_out = args.json_file or f"rc_{clean_prefix.lower()}_vip.json"
86
+ csv_out = args.csv_file or f"rc_{clean_prefix.lower()}_vip.csv"
87
+
88
+ print(f"Scraping VIP numbers for {clean_prefix} (Mode: {args.mode.upper()})...")
89
+ print(f"Output: {json_out} / {csv_out}")
90
+
91
+ def progress(curr, total, item: VehicleDetails):
92
+ if item.status == "Found":
93
+ print(f"[{curr}/{total}] {item.rc_number} ✓ {item.make_and_model} | {item.owner_name}")
94
+ elif item.status == "Unassigned":
95
+ print(f"[{curr}/{total}] {item.rc_number} ⚪ Unassigned")
96
+ else:
97
+ print(f"[{curr}/{total}] {item.rc_number} - {item.status}")
98
+
99
+ results = client.scrape_vip(
100
+ clean_prefix,
101
+ mode=args.mode,
102
+ save_csv=csv_out,
103
+ save_json=json_out,
104
+ progress_callback=progress
105
+ )
106
+ print(f"\nDone! Scraped {len(results)} VIP records.")
107
+
108
+
109
+ if __name__ == "__main__":
110
+ main()
@@ -0,0 +1,92 @@
1
+ """High-level facade client for CarInfo."""
2
+
3
+ from typing import List, Optional, Tuple, Callable
4
+
5
+ from .models import VehicleDetails
6
+ from .transport import BaseTransport, UrllibTransport
7
+ from .providers.base import BaseVehicleProvider
8
+ from .providers.carinfo import CarInfoProvider
9
+ from .exporters import JsonExporter, CsvExporter, BaseExporter
10
+ from .scraper import BatchScraper
11
+ from .exceptions import CarInfoRateLimitError, CarInfoError, RateLimitError
12
+
13
+
14
+ class CarInfoClient:
15
+ """Unified client for CarInfo API and scraping workflows.
16
+
17
+ Example:
18
+ >>> from carinfo import CarInfoClient
19
+ >>> client = CarInfoClient()
20
+ >>> vehicle = client.get_rc_details("GJ11CQ0786")
21
+ >>> print(vehicle.make_and_model)
22
+ """
23
+
24
+ def __init__(
25
+ self,
26
+ provider: Optional[BaseVehicleProvider] = None,
27
+ transport: Optional[BaseTransport] = None,
28
+ session_token: Optional[str] = None
29
+ ):
30
+ self.transport = transport or UrllibTransport()
31
+ self.provider = provider or CarInfoProvider(
32
+ transport=self.transport,
33
+ session_token=session_token
34
+ )
35
+
36
+ def get_rc_details(self, rc_number: str) -> VehicleDetails:
37
+ """Fetch details for a single vehicle registration number."""
38
+ return self.provider.fetch_vehicle(rc_number)
39
+
40
+ # Short alias for convenience
41
+ get = get_rc_details
42
+
43
+ def scrape_range(
44
+ self,
45
+ prefix: str,
46
+ start: int = 1,
47
+ end: int = 20,
48
+ delay_range: Tuple[float, float] = (1.2, 2.4),
49
+ save_csv: Optional[str] = None,
50
+ save_json: Optional[str] = None,
51
+ progress_callback: Optional[Callable[[int, int, VehicleDetails], None]] = None
52
+ ) -> List[VehicleDetails]:
53
+ """Scrape a sequential range of vehicle numbers."""
54
+ exporters: List[BaseExporter] = []
55
+ if save_csv:
56
+ exporters.append(CsvExporter(save_csv))
57
+ if save_json:
58
+ exporters.append(JsonExporter(save_json))
59
+
60
+ scraper = BatchScraper(
61
+ provider=self.provider,
62
+ exporters=exporters,
63
+ delay_range=delay_range
64
+ )
65
+ return scraper.scrape_range(prefix, start, end, progress_callback)
66
+
67
+ def scrape_vip(
68
+ self,
69
+ prefix: str,
70
+ mode: str = "top",
71
+ delay_range: Tuple[float, float] = (1.2, 2.4),
72
+ save_csv: Optional[str] = None,
73
+ save_json: Optional[str] = None,
74
+ progress_callback: Optional[Callable[[int, int, VehicleDetails], None]] = None
75
+ ) -> List[VehicleDetails]:
76
+ """Scrape VIP or fancy numbers for a given prefix."""
77
+ exporters: List[BaseExporter] = []
78
+ if save_csv:
79
+ exporters.append(CsvExporter(save_csv))
80
+ if save_json:
81
+ exporters.append(JsonExporter(save_json))
82
+
83
+ scraper = BatchScraper(
84
+ provider=self.provider,
85
+ exporters=exporters,
86
+ delay_range=delay_range
87
+ )
88
+ return scraper.scrape_vip(prefix, mode, progress_callback)
89
+
90
+
91
+ # Convenient alias
92
+ CarInfo = CarInfoClient