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.
- carinfo_py-0.1.0/LICENSE +21 -0
- carinfo_py-0.1.0/PKG-INFO +204 -0
- carinfo_py-0.1.0/README.md +174 -0
- carinfo_py-0.1.0/carinfo/__init__.py +56 -0
- carinfo_py-0.1.0/carinfo/cli.py +110 -0
- carinfo_py-0.1.0/carinfo/client.py +92 -0
- carinfo_py-0.1.0/carinfo/crypto.py +55 -0
- carinfo_py-0.1.0/carinfo/exceptions.py +30 -0
- carinfo_py-0.1.0/carinfo/exporters.py +73 -0
- carinfo_py-0.1.0/carinfo/models.py +131 -0
- carinfo_py-0.1.0/carinfo/providers/__init__.py +6 -0
- carinfo_py-0.1.0/carinfo/providers/base.py +13 -0
- carinfo_py-0.1.0/carinfo/providers/carinfo.py +110 -0
- carinfo_py-0.1.0/carinfo/scraper.py +77 -0
- carinfo_py-0.1.0/carinfo/transport.py +44 -0
- carinfo_py-0.1.0/carinfo/vip.py +67 -0
- carinfo_py-0.1.0/carinfo_py.egg-info/PKG-INFO +204 -0
- carinfo_py-0.1.0/carinfo_py.egg-info/SOURCES.txt +24 -0
- carinfo_py-0.1.0/carinfo_py.egg-info/dependency_links.txt +1 -0
- carinfo_py-0.1.0/carinfo_py.egg-info/entry_points.txt +2 -0
- carinfo_py-0.1.0/carinfo_py.egg-info/requires.txt +6 -0
- carinfo_py-0.1.0/carinfo_py.egg-info/top_level.txt +1 -0
- carinfo_py-0.1.0/pyproject.toml +48 -0
- carinfo_py-0.1.0/setup.cfg +4 -0
- carinfo_py-0.1.0/setup.py +26 -0
- carinfo_py-0.1.0/tests/test_carinfo.py +101 -0
carinfo_py-0.1.0/LICENSE
ADDED
|
@@ -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
|
+
[](https://pypi.org/project/carinfo-py/)
|
|
34
|
+
[](https://pypi.org/project/carinfo-py/)
|
|
35
|
+
[](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
|
+
[](https://pypi.org/project/carinfo-py/)
|
|
4
|
+
[](https://pypi.org/project/carinfo-py/)
|
|
5
|
+
[](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
|