apikeyrotator 0.0.2__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.
- apikeyrotator-0.0.2/LICENSE +21 -0
- apikeyrotator-0.0.2/PKG-INFO +335 -0
- apikeyrotator-0.0.2/README.md +290 -0
- apikeyrotator-0.0.2/apikeyrotator/__init__.py +13 -0
- apikeyrotator-0.0.2/apikeyrotator/exceptions.py +11 -0
- apikeyrotator-0.0.2/apikeyrotator/rotator.py +162 -0
- apikeyrotator-0.0.2/apikeyrotator/utils.py +29 -0
- apikeyrotator-0.0.2/apikeyrotator.egg-info/PKG-INFO +335 -0
- apikeyrotator-0.0.2/apikeyrotator.egg-info/SOURCES.txt +12 -0
- apikeyrotator-0.0.2/apikeyrotator.egg-info/dependency_links.txt +1 -0
- apikeyrotator-0.0.2/apikeyrotator.egg-info/requires.txt +1 -0
- apikeyrotator-0.0.2/apikeyrotator.egg-info/top_level.txt +1 -0
- apikeyrotator-0.0.2/pyproject.toml +32 -0
- apikeyrotator-0.0.2/setup.cfg +4 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Prime Evolution
|
|
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,335 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: apikeyrotator
|
|
3
|
+
Version: 0.0.2
|
|
4
|
+
Summary: Ultra simple API key rotation for bypassing rate limits
|
|
5
|
+
Author-email: Prime Evolution <develop@eclips-team.ru>
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2024 Prime Evolution
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
Project-URL: Homepage, https://github.com/PrimeevolutionZ/apikeyrotator
|
|
28
|
+
Project-URL: Repository, https://github.com/PrimeevolutionZ/apikeyrotator
|
|
29
|
+
Project-URL: Issues, https://github.com/PrimeevolutionZ/apikeyrotator/issues
|
|
30
|
+
Keywords: api,rotation,rate limit,requests
|
|
31
|
+
Classifier: Development Status :: 4 - Beta
|
|
32
|
+
Classifier: Intended Audience :: Developers
|
|
33
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
34
|
+
Classifier: Programming Language :: Python :: 3
|
|
35
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
36
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
37
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
38
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
39
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
40
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
41
|
+
Description-Content-Type: text/markdown
|
|
42
|
+
License-File: LICENSE
|
|
43
|
+
Requires-Dist: requests>=2.25.0
|
|
44
|
+
Dynamic: license-file
|
|
45
|
+
|
|
46
|
+
# API Key Rotator π
|
|
47
|
+
|
|
48
|
+
ΠΡΠΎΡΡΠ°Ρ, Π½ΠΎ ΠΌΠΎΡΠ½Π°Ρ Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠ° Π΄Π»Ρ Π°Π²ΡΠΎΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠΎΠ³ΠΎ Π²ΡΠ°ΡΠ΅Π½ΠΈΡ API ΠΊΠ»ΡΡΠ΅ΠΉ. ΠΠ±Ρ
ΠΎΠ΄ΠΈΡ Π»ΠΈΠΌΠΈΡΡ Π·Π°ΠΏΡΠΎΡΠΎΠ², ΠΎΠ±ΡΠ°Π±Π°ΡΡΠ²Π°Π΅Ρ ΠΎΡΠΈΠ±ΠΊΠΈ 429 ΠΈ Π΄Π΅Π»Π°Π΅Ρ Π²Π°ΡΠΈ Π·Π°ΠΏΡΠΎΡΡ Π½Π΅ΡΡΠ·Π²ΠΈΠΌΡΠΌΠΈ ΠΊ ΠΎΠ³ΡΠ°Π½ΠΈΡΠ΅Π½ΠΈΡΠΌ API.
|
|
49
|
+
|
|
50
|
+
## ΠΡΠΎΠ±Π΅Π½Π½ΠΎΡΡΠΈ β¨
|
|
51
|
+
|
|
52
|
+
- π **ΠΠ²ΡΠΎΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠΎΠ΅ Π²ΡΠ°ΡΠ΅Π½ΠΈΠ΅ ΠΊΠ»ΡΡΠ΅ΠΉ** - Round-robin Π°Π»Π³ΠΎΡΠΈΡΠΌ
|
|
53
|
+
- β‘ **ΠΠ²ΡΠΎΠ΄Π΅ΡΠ΅ΠΊΡ Π°Π²ΡΠΎΡΠΈΠ·Π°ΡΠΈΠΈ** - Π‘Π°ΠΌ ΠΎΠΏΡΠ΅Π΄Π΅Π»ΡΠ΅Ρ Bearer/API-Key ΡΠΎΡΠΌΠ°Ρ
|
|
54
|
+
- π **Π£ΠΌΠ½ΡΠ΅ ΡΠ΅ΡΡΠ°ΠΈ** - ΠΠΊΡΠΏΠΎΠ½Π΅Π½ΡΠΈΠ°Π»ΡΠ½Π°Ρ Π·Π°Π΄Π΅ΡΠΆΠΊΠ° ΠΏΡΠΈ ΠΎΡΠΈΠ±ΠΊΠ°Ρ
|
|
55
|
+
- π‘οΈ **ΠΠ±ΡΠ°Π±ΠΎΡΠΊΠ° ΠΎΡΠΈΠ±ΠΎΠΊ** - 429, 500, 502, 503, 504 Π°Π²ΡΠΎΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠΈ
|
|
56
|
+
- π» **ΠΠΎΠ»Π½Π°Ρ ΡΠΎΠ²ΠΌΠ΅ΡΡΠΈΠΌΠΎΡΡΡ** Ρ Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠΎΠΉ `requests`
|
|
57
|
+
- π **ΠΠΎΠ½ΡΡΠ½ΡΠ΅ ΠΎΡΠΈΠ±ΠΊΠΈ** - ΠΠΎΠ΄ΡΠΊΠ°Π·ΠΊΠΈ ΠΊΠ°ΠΊ ΠΈΡΠΏΡΠ°Π²ΠΈΡΡ ΠΏΡΠΎΠ±Π»Π΅ΠΌΡ
|
|
58
|
+
|
|
59
|
+
## Π£ΡΡΠ°Π½ΠΎΠ²ΠΊΠ° π¦
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
pip install apikeyrotator
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
ΠΠ»ΠΈ ΠΈΠ· ΠΈΡΡ
ΠΎΠ΄Π½ΠΈΠΊΠΎΠ²:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
git clone https://github.com/PrimeevolutionZ/apikeyrotator.git
|
|
69
|
+
cd apikeyrotator
|
|
70
|
+
pip install -e .
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## ΠΡΡΡΡΡΠΉ ΡΡΠ°ΡΡ π
|
|
74
|
+
|
|
75
|
+
### Π‘ΠΏΠΎΡΠΎΠ± 1: ΠΠ΅ΡΠ΅Π΄Π°ΡΠ° ΠΊΠ»ΡΡΠ΅ΠΉ Π½Π°ΠΏΡΡΠΌΡΡ
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
from apikeyrotator import APIKeyRotator
|
|
79
|
+
|
|
80
|
+
# ΠΡΠΎΡΡΠΎ ΠΏΠ΅ΡΠ΅Π΄Π°ΠΉΡΠ΅ ΠΊΠ»ΡΡΠΈ ΡΠΏΠΈΡΠΊΠΎΠΌ
|
|
81
|
+
rotator = APIKeyRotator(api_keys=["key1", "key2", "key3"])
|
|
82
|
+
|
|
83
|
+
# ΠΠ»ΠΈ ΡΡΡΠΎΠΊΠΎΠΉ ΡΠ΅ΡΠ΅Π· Π·Π°ΠΏΡΡΡΡ
|
|
84
|
+
rotator = APIKeyRotator(api_keys="key1,key2,key3")
|
|
85
|
+
|
|
86
|
+
# ΠΡΠΏΠΎΠ»ΡΠ·ΡΠΉΡΠ΅ ΠΊΠ°ΠΊ ΠΎΠ±ΡΡΠ½ΡΠΉ requests!
|
|
87
|
+
response = rotator.get("https://api.example.com/data")
|
|
88
|
+
print(response.json())
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Π‘ΠΏΠΎΡΠΎΠ± 2: ΠΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠ΅ ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΡ
ΠΎΠΊΡΡΠΆΠ΅Π½ΠΈΡ
|
|
92
|
+
|
|
93
|
+
Π‘ΠΎΠ·Π΄Π°ΠΉΡΠ΅ ΡΠ°ΠΉΠ» `.env` Π² ΠΊΠΎΡΠ½Π΅ ΠΏΡΠΎΠ΅ΠΊΡΠ°:
|
|
94
|
+
```env
|
|
95
|
+
API_KEYS=your_key_1,your_key_2,your_key_3
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
ΠΠ»ΠΈ ΡΡΡΠ°Π½ΠΎΠ²ΠΈΡΠ΅ ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΡ ΠΎΠΊΡΡΠΆΠ΅Π½ΠΈΡ:
|
|
99
|
+
```bash
|
|
100
|
+
# Linux/Mac
|
|
101
|
+
export API_KEYS="your_key_1,your_key_2,your_key_3"
|
|
102
|
+
|
|
103
|
+
# Windows
|
|
104
|
+
set API_KEYS=your_key_1,your_key_2,your_key_3
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Π’Π΅ΠΏΠ΅ΡΡ ΠΏΡΠΎΡΡΠΎ ΠΈΠ½ΠΈΡΠΈΠ°Π»ΠΈΠ·ΠΈΡΡΠΉΡΠ΅ ΡΠΎΡΠ°ΡΠΎΡ:
|
|
108
|
+
```python
|
|
109
|
+
from apikeyrotator import APIKeyRotator
|
|
110
|
+
|
|
111
|
+
rotator = APIKeyRotator() # ΠΠ²ΡΠΎΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠΈ Π½Π°ΠΉΠ΄Π΅Ρ API_KEYS
|
|
112
|
+
|
|
113
|
+
response = rotator.get("https://api.example.com/data")
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## ΠΡΠΈΠΌΠ΅ΡΡ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΡ π
|
|
117
|
+
|
|
118
|
+
### ΠΠ°Π·ΠΎΠ²ΡΠ΅ HTTP-Π·Π°ΠΏΡΠΎΡΡ
|
|
119
|
+
|
|
120
|
+
```python
|
|
121
|
+
from apikeyrotator import APIKeyRotator
|
|
122
|
+
|
|
123
|
+
rotator = APIKeyRotator(api_keys=["key1", "key2", "key3"])
|
|
124
|
+
|
|
125
|
+
# GET Π·Π°ΠΏΡΠΎΡ
|
|
126
|
+
response = rotator.get("https://api.example.com/users")
|
|
127
|
+
|
|
128
|
+
# POST Π·Π°ΠΏΡΠΎΡ Ρ Π΄Π°Π½Π½ΡΠΌΠΈ
|
|
129
|
+
response = rotator.post(
|
|
130
|
+
"https://api.example.com/users",
|
|
131
|
+
json={"name": "John", "email": "john@example.com"}
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
# PUT Π·Π°ΠΏΡΠΎΡ
|
|
135
|
+
response = rotator.put(
|
|
136
|
+
"https://api.example.com/users/1",
|
|
137
|
+
json={"name": "John Updated"}
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
# DELETE Π·Π°ΠΏΡΠΎΡ
|
|
141
|
+
response = rotator.delete("https://api.example.com/users/1")
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### ΠΠ°ΡΡΠΎΠΌΠ½ΡΠ΅ Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΊΠΈ Π°Π²ΡΠΎΡΠΈΠ·Π°ΡΠΈΠΈ
|
|
145
|
+
|
|
146
|
+
```python
|
|
147
|
+
from apikeyrotator import APIKeyRotator
|
|
148
|
+
|
|
149
|
+
rotator = APIKeyRotator(api_keys=["key1", "key2"])
|
|
150
|
+
|
|
151
|
+
# ΠΠ°ΡΡΠΎΠΌΠ½ΡΠΉ Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΎΠΊ Π°Π²ΡΠΎΡΠΈΠ·Π°ΡΠΈΠΈ
|
|
152
|
+
response = rotator.get(
|
|
153
|
+
"https://api.example.com/data",
|
|
154
|
+
headers={"X-Custom-Auth": "custom_value"}
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
# ΠΠ»ΠΈ ΠΏΠ΅ΡΠ΅ΠΎΠΏΡΠ΅Π΄Π΅Π»ΠΈΡΠ΅ Π°Π²ΡΠΎΡΠΈΠ·Π°ΡΠΈΡ ΠΏΠΎΠ»Π½ΠΎΡΡΡΡ
|
|
158
|
+
response = rotator.get(
|
|
159
|
+
"https://api.example.com/data",
|
|
160
|
+
headers={"Authorization": "Custom your_token_here"}
|
|
161
|
+
)
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Π Π°Π±ΠΎΡΠ° Ρ ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΠ°ΠΌΠΈ Π·Π°ΠΏΡΠΎΡΠ°
|
|
165
|
+
|
|
166
|
+
```python
|
|
167
|
+
from apikeyrotator import APIKeyRotator
|
|
168
|
+
|
|
169
|
+
rotator = APIKeyRotator(api_keys=["key1", "key2", "key3"])
|
|
170
|
+
|
|
171
|
+
# ΠΠ°ΡΠ°ΠΌΠ΅ΡΡΡ Π·Π°ΠΏΡΠΎΡΠ°
|
|
172
|
+
response = rotator.get(
|
|
173
|
+
"https://api.example.com/search",
|
|
174
|
+
params={"query": "python", "limit": 10}
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
# JSON Π΄Π°Π½Π½ΡΠ΅
|
|
178
|
+
response = rotator.post(
|
|
179
|
+
"https://api.example.com/items",
|
|
180
|
+
json={"name": "New Item", "price": 99.99}
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
# Π’Π°ΠΉΠΌΠ°ΡΡ
|
|
184
|
+
response = rotator.get(
|
|
185
|
+
"https://api.example.com/data",
|
|
186
|
+
timeout=10
|
|
187
|
+
)
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### ΠΠ±ΡΠ°Π±ΠΎΡΠΊΠ° ΠΎΡΠΈΠ±ΠΎΠΊ
|
|
191
|
+
|
|
192
|
+
```python
|
|
193
|
+
from apikeyrotator import APIKeyRotator, AllKeysExhaustedError
|
|
194
|
+
|
|
195
|
+
rotator = APIKeyRotator(api_keys=["key1", "key2"], max_retries=5)
|
|
196
|
+
|
|
197
|
+
try:
|
|
198
|
+
response = rotator.get("https://api.example.com/limited")
|
|
199
|
+
print("Π£ΡΠΏΠ΅Ρ
!", response.json())
|
|
200
|
+
except AllKeysExhaustedError as e:
|
|
201
|
+
print("ΠΡΠ΅ ΠΊΠ»ΡΡΠΈ ΠΈΡΡΠ΅ΡΠΏΠ°Π½Ρ:", e)
|
|
202
|
+
except Exception as e:
|
|
203
|
+
print("ΠΡΡΠ³Π°Ρ ΠΎΡΠΈΠ±ΠΊΠ°:", e)
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## Π Π°ΡΡΠΈΡΠ΅Π½Π½ΡΠ΅ Π½Π°ΡΡΡΠΎΠΉΠΊΠΈ βοΈ
|
|
207
|
+
|
|
208
|
+
### ΠΠ°ΡΡΠΎΠΌΠΈΠ·Π°ΡΠΈΡ ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΠΎΠ²
|
|
209
|
+
|
|
210
|
+
```python
|
|
211
|
+
from apikeyrotator import APIKeyRotator
|
|
212
|
+
|
|
213
|
+
# ΠΡΠ΅ ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΡ Π½Π°ΡΡΡΠΎΠΉΠΊΠΈ
|
|
214
|
+
rotator = APIKeyRotator(
|
|
215
|
+
api_keys=["key1", "key2", "key3"], # ΠΠ»ΡΡΠΈ
|
|
216
|
+
env_var="CUSTOM_API_KEYS", # ΠΠ°ΡΡΠΎΠΌΠ½Π°Ρ ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½Π°Ρ ΠΎΠΊΡΡΠΆΠ΅Π½ΠΈΡ
|
|
217
|
+
max_retries=5, # ΠΠ°ΠΊΡΠΈΠΌΡΠΌ ΠΏΠΎΠΏΡΡΠΎΠΊ
|
|
218
|
+
base_delay=2.0 # ΠΠ°Π·ΠΎΠ²Π°Ρ Π·Π°Π΄Π΅ΡΠΆΠΊΠ° ΠΌΠ΅ΠΆΠ΄Ρ ΠΏΠΎΠΏΡΡΠΊΠ°ΠΌΠΈ
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
print(f"ΠΠΎΠ»ΠΈΡΠ΅ΡΡΠ²ΠΎ ΠΊΠ»ΡΡΠ΅ΠΉ: {len(rotator)}")
|
|
222
|
+
print(f"ΠΠ°ΠΊΡΠΈΠΌΡΠΌ ΠΏΠΎΠΏΡΡΠΎΠΊ: {rotator.max_retries}")
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### ΠΠ½ΡΠ΅Π³ΡΠ°ΡΠΈΡ Ρ ΡΡΡΠ΅ΡΡΠ²ΡΡΡΠΈΠΌ ΠΊΠΎΠ΄ΠΎΠΌ
|
|
226
|
+
|
|
227
|
+
```python
|
|
228
|
+
from apikeyrotator import APIKeyRotator
|
|
229
|
+
import requests
|
|
230
|
+
|
|
231
|
+
# Π‘ΡΡΠ΅ΡΡΠ²ΡΡΡΠΈΠΉ ΠΊΠΎΠ΄ Ρ requests
|
|
232
|
+
response = requests.get("https://api.example.com/data")
|
|
233
|
+
|
|
234
|
+
# ΠΠ΅Π³ΠΊΠΎ Π·Π°ΠΌΠ΅Π½ΠΈΡΠ΅ Π½Π° APIKeyRotator
|
|
235
|
+
rotator = APIKeyRotator(api_keys=["key1", "key2", "key3"])
|
|
236
|
+
response = rotator.get("https://api.example.com/data") # Π’ΠΎΡ ΠΆΠ΅ API!
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## Best Practices β
|
|
240
|
+
|
|
241
|
+
### 1. ΠΡΠΏΠΎΠ»ΡΠ·ΡΠΉΡΠ΅ ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΠ΅ ΠΎΠΊΡΡΠΆΠ΅Π½ΠΈΡ Π΄Π»Ρ Π±Π΅Π·ΠΎΠΏΠ°ΡΠ½ΠΎΡΡΠΈ
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
# ΠΠΈΠΊΠΎΠ³Π΄Π° Π½Π΅ Ρ
ΡΠ°Π½ΠΈΡΠ΅ ΠΊΠ»ΡΡΠΈ Π² ΠΊΠΎΠ΄Π΅!
|
|
245
|
+
# ΠΠΌΠ΅ΡΡΠΎ ΡΡΠΎΠ³ΠΎ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠΉΡΠ΅ .env ΡΠ°ΠΉΠ» ΠΈΠ»ΠΈ ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΠ΅ ΠΎΠΊΡΡΠΆΠ΅Π½ΠΈΡ
|
|
246
|
+
export API_KEYS="your_production_key_1,your_production_key_2"
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### 2. ΠΠ°ΡΡΡΠΎΠΉΡΠ΅ Π°Π΄Π΅ΠΊΠ²Π°ΡΠ½ΠΎΠ΅ ΠΊΠΎΠ»ΠΈΡΠ΅ΡΡΠ²ΠΎ ΠΏΠΎΠΏΡΡΠΎΠΊ
|
|
250
|
+
|
|
251
|
+
```python
|
|
252
|
+
# ΠΠ»Ρ 3 ΠΊΠ»ΡΡΠ΅ΠΉ ΠΈ 2 ΠΏΠΎΠΏΡΡΠΎΠΊ Π½Π° ΠΊΠ»ΡΡ = 6 Π²ΡΠ΅Π³ΠΎ ΠΏΠΎΠΏΡΡΠΎΠΊ
|
|
253
|
+
rotator = APIKeyRotator(
|
|
254
|
+
api_keys=["key1", "key2", "key3"],
|
|
255
|
+
max_retries=6
|
|
256
|
+
)
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### 3. ΠΠΎΠ½ΠΈΡΠΎΡΠΈΠ½Π³ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΡ ΠΊΠ»ΡΡΠ΅ΠΉ
|
|
260
|
+
|
|
261
|
+
```python
|
|
262
|
+
rotator = APIKeyRotator(api_keys=["key1", "key2", "key3"])
|
|
263
|
+
|
|
264
|
+
# ΠΠΎΡΠ»Π΅ Π½Π΅ΡΠΊΠΎΠ»ΡΠΊΠΈΡ
Π·Π°ΠΏΡΠΎΡΠΎΠ² ΠΌΠΎΠΆΠ½ΠΎ ΠΏΠΎΡΠΌΠΎΡΡΠ΅ΡΡ ΡΡΠ°ΡΠΈΡΡΠΈΠΊΡ
|
|
265
|
+
for i in range(10):
|
|
266
|
+
rotator.get("https://api.example.com/test")
|
|
267
|
+
|
|
268
|
+
print("Π ΠΎΡΠ°ΡΠΎΡ ΠΎΡΡΠ°Π±ΠΎΡΠ°Π»", len(rotator), "Π·Π°ΠΏΡΠΎΡΠΎΠ²")
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
## ΠΠ±ΡΠ°Π±ΠΎΡΠΊΠ° ΠΎΡΠΈΠ±ΠΎΠΊ β
|
|
272
|
+
|
|
273
|
+
ΠΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠ° ΠΏΡΠ΅Π΄ΠΎΡΡΠ°Π²Π»ΡΠ΅Ρ ΠΏΠΎΠ½ΡΡΠ½ΡΠ΅ ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΡ ΠΎΠ± ΠΎΡΠΈΠ±ΠΊΠ°Ρ
:
|
|
274
|
+
|
|
275
|
+
### ΠΡΠ»ΠΈ ΠΊΠ»ΡΡΠΈ Π½Π΅ Π½Π°ΠΉΠ΄Π΅Π½Ρ
|
|
276
|
+
|
|
277
|
+
```
|
|
278
|
+
β No API keys found.
|
|
279
|
+
Please either:
|
|
280
|
+
1. Pass keys directly: APIKeyRotator(api_keys=['key1', 'key2'])
|
|
281
|
+
2. Set environment variable: export API_KEYS='key1,key2'
|
|
282
|
+
3. Create .env file with: API_KEYS=key1,key2
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### ΠΡΠ»ΠΈ Π²ΡΠ΅ ΠΊΠ»ΡΡΠΈ ΠΈΡΡΠ΅ΡΠΏΠ°Π½Ρ
|
|
286
|
+
|
|
287
|
+
```python
|
|
288
|
+
try:
|
|
289
|
+
response = rotator.get("https://api.example.com/limited")
|
|
290
|
+
except AllKeysExhaustedError as e:
|
|
291
|
+
print(e) # "All 3 keys exhausted after 6 attempts"
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
## Π‘ΠΎΠ²ΠΌΠ΅ΡΡΠΈΠΌΠΎΡΡΡ π
|
|
295
|
+
|
|
296
|
+
- **Python**: 3.7+
|
|
297
|
+
- **ΠΠ°Π²ΠΈΡΠΈΠΌΠΎΡΡΠΈ**: ΡΠΎΠ»ΡΠΊΠΎ `requests>=2.25.0`
|
|
298
|
+
|
|
299
|
+
## Π Π°Π·ΡΠ°Π±ΠΎΡΠΊΠ° π οΈ
|
|
300
|
+
|
|
301
|
+
### Π£ΡΡΠ°Π½ΠΎΠ²ΠΊΠ° Π΄Π»Ρ ΡΠ°Π·ΡΠ°Π±ΠΎΡΠΊΠΈ
|
|
302
|
+
|
|
303
|
+
```bash
|
|
304
|
+
git clone https://github.com/PrimeevolutionZ/apikeyrotator.git
|
|
305
|
+
cd apikeyrotator
|
|
306
|
+
python -m venv venv
|
|
307
|
+
source venv/bin/activate # Linux/Mac
|
|
308
|
+
# ΠΈΠ»ΠΈ
|
|
309
|
+
venv\Scripts\activate # Windows
|
|
310
|
+
pip install -e .[dev]
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
### ΠΠ°ΠΏΡΡΠΊ ΡΠ΅ΡΡΠΎΠ²
|
|
314
|
+
|
|
315
|
+
```bash
|
|
316
|
+
pytest tests/
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### Π‘Π±ΠΎΡΠΊΠ° ΠΏΠ°ΠΊΠ΅ΡΠ°
|
|
320
|
+
|
|
321
|
+
```bash
|
|
322
|
+
python setup.py sdist bdist_wheel
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
## ΠΠΈΡΠ΅Π½Π·ΠΈΡ π
|
|
326
|
+
|
|
327
|
+
MIT License - ΡΠΌΠΎΡΡΠΈΡΠ΅ ΡΠ°ΠΉΠ» [LICENSE](LICENSE) Π΄Π»Ρ Π΄Π΅ΡΠ°Π»Π΅ΠΉ.
|
|
328
|
+
|
|
329
|
+
## ΠΠΎΠ΄Π΄Π΅ΡΠΆΠΊΠ° π€
|
|
330
|
+
|
|
331
|
+
ΠΠ°ΡΠ»ΠΈ Π±Π°Π³ ΠΈΠ»ΠΈ Π΅ΡΡΡ ΠΏΡΠ΅Π΄Π»ΠΎΠΆΠ΅Π½ΠΈΡ? [Π‘ΠΎΠ·Π΄Π°ΠΉΡΠ΅ issue](https://github.com/yourusername/apikeyrotator/issues) Π½Π° GitHub!
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
335
|
+
**API Key Rotator** - ΡΠ΄Π΅Π»Π°ΠΉΡΠ΅ Π²Π°ΡΠΈ API Π·Π°ΠΏΡΠΎΡΡ Π½Π΅ΡΡΠ·Π²ΠΈΠΌΡΠΌΠΈ ΠΊ ΠΎΠ³ΡΠ°Π½ΠΈΡΠ΅Π½ΠΈΡΠΌ! π
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
# API Key Rotator π
|
|
2
|
+
|
|
3
|
+
ΠΡΠΎΡΡΠ°Ρ, Π½ΠΎ ΠΌΠΎΡΠ½Π°Ρ Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠ° Π΄Π»Ρ Π°Π²ΡΠΎΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠΎΠ³ΠΎ Π²ΡΠ°ΡΠ΅Π½ΠΈΡ API ΠΊΠ»ΡΡΠ΅ΠΉ. ΠΠ±Ρ
ΠΎΠ΄ΠΈΡ Π»ΠΈΠΌΠΈΡΡ Π·Π°ΠΏΡΠΎΡΠΎΠ², ΠΎΠ±ΡΠ°Π±Π°ΡΡΠ²Π°Π΅Ρ ΠΎΡΠΈΠ±ΠΊΠΈ 429 ΠΈ Π΄Π΅Π»Π°Π΅Ρ Π²Π°ΡΠΈ Π·Π°ΠΏΡΠΎΡΡ Π½Π΅ΡΡΠ·Π²ΠΈΠΌΡΠΌΠΈ ΠΊ ΠΎΠ³ΡΠ°Π½ΠΈΡΠ΅Π½ΠΈΡΠΌ API.
|
|
4
|
+
|
|
5
|
+
## ΠΡΠΎΠ±Π΅Π½Π½ΠΎΡΡΠΈ β¨
|
|
6
|
+
|
|
7
|
+
- π **ΠΠ²ΡΠΎΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠΎΠ΅ Π²ΡΠ°ΡΠ΅Π½ΠΈΠ΅ ΠΊΠ»ΡΡΠ΅ΠΉ** - Round-robin Π°Π»Π³ΠΎΡΠΈΡΠΌ
|
|
8
|
+
- β‘ **ΠΠ²ΡΠΎΠ΄Π΅ΡΠ΅ΠΊΡ Π°Π²ΡΠΎΡΠΈΠ·Π°ΡΠΈΠΈ** - Π‘Π°ΠΌ ΠΎΠΏΡΠ΅Π΄Π΅Π»ΡΠ΅Ρ Bearer/API-Key ΡΠΎΡΠΌΠ°Ρ
|
|
9
|
+
- π **Π£ΠΌΠ½ΡΠ΅ ΡΠ΅ΡΡΠ°ΠΈ** - ΠΠΊΡΠΏΠΎΠ½Π΅Π½ΡΠΈΠ°Π»ΡΠ½Π°Ρ Π·Π°Π΄Π΅ΡΠΆΠΊΠ° ΠΏΡΠΈ ΠΎΡΠΈΠ±ΠΊΠ°Ρ
|
|
10
|
+
- π‘οΈ **ΠΠ±ΡΠ°Π±ΠΎΡΠΊΠ° ΠΎΡΠΈΠ±ΠΎΠΊ** - 429, 500, 502, 503, 504 Π°Π²ΡΠΎΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠΈ
|
|
11
|
+
- π» **ΠΠΎΠ»Π½Π°Ρ ΡΠΎΠ²ΠΌΠ΅ΡΡΠΈΠΌΠΎΡΡΡ** Ρ Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠΎΠΉ `requests`
|
|
12
|
+
- π **ΠΠΎΠ½ΡΡΠ½ΡΠ΅ ΠΎΡΠΈΠ±ΠΊΠΈ** - ΠΠΎΠ΄ΡΠΊΠ°Π·ΠΊΠΈ ΠΊΠ°ΠΊ ΠΈΡΠΏΡΠ°Π²ΠΈΡΡ ΠΏΡΠΎΠ±Π»Π΅ΠΌΡ
|
|
13
|
+
|
|
14
|
+
## Π£ΡΡΠ°Π½ΠΎΠ²ΠΊΠ° π¦
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pip install apikeyrotator
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
ΠΠ»ΠΈ ΠΈΠ· ΠΈΡΡ
ΠΎΠ΄Π½ΠΈΠΊΠΎΠ²:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
git clone https://github.com/PrimeevolutionZ/apikeyrotator.git
|
|
24
|
+
cd apikeyrotator
|
|
25
|
+
pip install -e .
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## ΠΡΡΡΡΡΠΉ ΡΡΠ°ΡΡ π
|
|
29
|
+
|
|
30
|
+
### Π‘ΠΏΠΎΡΠΎΠ± 1: ΠΠ΅ΡΠ΅Π΄Π°ΡΠ° ΠΊΠ»ΡΡΠ΅ΠΉ Π½Π°ΠΏΡΡΠΌΡΡ
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
from apikeyrotator import APIKeyRotator
|
|
34
|
+
|
|
35
|
+
# ΠΡΠΎΡΡΠΎ ΠΏΠ΅ΡΠ΅Π΄Π°ΠΉΡΠ΅ ΠΊΠ»ΡΡΠΈ ΡΠΏΠΈΡΠΊΠΎΠΌ
|
|
36
|
+
rotator = APIKeyRotator(api_keys=["key1", "key2", "key3"])
|
|
37
|
+
|
|
38
|
+
# ΠΠ»ΠΈ ΡΡΡΠΎΠΊΠΎΠΉ ΡΠ΅ΡΠ΅Π· Π·Π°ΠΏΡΡΡΡ
|
|
39
|
+
rotator = APIKeyRotator(api_keys="key1,key2,key3")
|
|
40
|
+
|
|
41
|
+
# ΠΡΠΏΠΎΠ»ΡΠ·ΡΠΉΡΠ΅ ΠΊΠ°ΠΊ ΠΎΠ±ΡΡΠ½ΡΠΉ requests!
|
|
42
|
+
response = rotator.get("https://api.example.com/data")
|
|
43
|
+
print(response.json())
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Π‘ΠΏΠΎΡΠΎΠ± 2: ΠΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠ΅ ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΡ
ΠΎΠΊΡΡΠΆΠ΅Π½ΠΈΡ
|
|
47
|
+
|
|
48
|
+
Π‘ΠΎΠ·Π΄Π°ΠΉΡΠ΅ ΡΠ°ΠΉΠ» `.env` Π² ΠΊΠΎΡΠ½Π΅ ΠΏΡΠΎΠ΅ΠΊΡΠ°:
|
|
49
|
+
```env
|
|
50
|
+
API_KEYS=your_key_1,your_key_2,your_key_3
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
ΠΠ»ΠΈ ΡΡΡΠ°Π½ΠΎΠ²ΠΈΡΠ΅ ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΡ ΠΎΠΊΡΡΠΆΠ΅Π½ΠΈΡ:
|
|
54
|
+
```bash
|
|
55
|
+
# Linux/Mac
|
|
56
|
+
export API_KEYS="your_key_1,your_key_2,your_key_3"
|
|
57
|
+
|
|
58
|
+
# Windows
|
|
59
|
+
set API_KEYS=your_key_1,your_key_2,your_key_3
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Π’Π΅ΠΏΠ΅ΡΡ ΠΏΡΠΎΡΡΠΎ ΠΈΠ½ΠΈΡΠΈΠ°Π»ΠΈΠ·ΠΈΡΡΠΉΡΠ΅ ΡΠΎΡΠ°ΡΠΎΡ:
|
|
63
|
+
```python
|
|
64
|
+
from apikeyrotator import APIKeyRotator
|
|
65
|
+
|
|
66
|
+
rotator = APIKeyRotator() # ΠΠ²ΡΠΎΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠΈ Π½Π°ΠΉΠ΄Π΅Ρ API_KEYS
|
|
67
|
+
|
|
68
|
+
response = rotator.get("https://api.example.com/data")
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## ΠΡΠΈΠΌΠ΅ΡΡ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΡ π
|
|
72
|
+
|
|
73
|
+
### ΠΠ°Π·ΠΎΠ²ΡΠ΅ HTTP-Π·Π°ΠΏΡΠΎΡΡ
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
from apikeyrotator import APIKeyRotator
|
|
77
|
+
|
|
78
|
+
rotator = APIKeyRotator(api_keys=["key1", "key2", "key3"])
|
|
79
|
+
|
|
80
|
+
# GET Π·Π°ΠΏΡΠΎΡ
|
|
81
|
+
response = rotator.get("https://api.example.com/users")
|
|
82
|
+
|
|
83
|
+
# POST Π·Π°ΠΏΡΠΎΡ Ρ Π΄Π°Π½Π½ΡΠΌΠΈ
|
|
84
|
+
response = rotator.post(
|
|
85
|
+
"https://api.example.com/users",
|
|
86
|
+
json={"name": "John", "email": "john@example.com"}
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
# PUT Π·Π°ΠΏΡΠΎΡ
|
|
90
|
+
response = rotator.put(
|
|
91
|
+
"https://api.example.com/users/1",
|
|
92
|
+
json={"name": "John Updated"}
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
# DELETE Π·Π°ΠΏΡΠΎΡ
|
|
96
|
+
response = rotator.delete("https://api.example.com/users/1")
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### ΠΠ°ΡΡΠΎΠΌΠ½ΡΠ΅ Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΊΠΈ Π°Π²ΡΠΎΡΠΈΠ·Π°ΡΠΈΠΈ
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
from apikeyrotator import APIKeyRotator
|
|
103
|
+
|
|
104
|
+
rotator = APIKeyRotator(api_keys=["key1", "key2"])
|
|
105
|
+
|
|
106
|
+
# ΠΠ°ΡΡΠΎΠΌΠ½ΡΠΉ Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΎΠΊ Π°Π²ΡΠΎΡΠΈΠ·Π°ΡΠΈΠΈ
|
|
107
|
+
response = rotator.get(
|
|
108
|
+
"https://api.example.com/data",
|
|
109
|
+
headers={"X-Custom-Auth": "custom_value"}
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
# ΠΠ»ΠΈ ΠΏΠ΅ΡΠ΅ΠΎΠΏΡΠ΅Π΄Π΅Π»ΠΈΡΠ΅ Π°Π²ΡΠΎΡΠΈΠ·Π°ΡΠΈΡ ΠΏΠΎΠ»Π½ΠΎΡΡΡΡ
|
|
113
|
+
response = rotator.get(
|
|
114
|
+
"https://api.example.com/data",
|
|
115
|
+
headers={"Authorization": "Custom your_token_here"}
|
|
116
|
+
)
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Π Π°Π±ΠΎΡΠ° Ρ ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΠ°ΠΌΠΈ Π·Π°ΠΏΡΠΎΡΠ°
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
from apikeyrotator import APIKeyRotator
|
|
123
|
+
|
|
124
|
+
rotator = APIKeyRotator(api_keys=["key1", "key2", "key3"])
|
|
125
|
+
|
|
126
|
+
# ΠΠ°ΡΠ°ΠΌΠ΅ΡΡΡ Π·Π°ΠΏΡΠΎΡΠ°
|
|
127
|
+
response = rotator.get(
|
|
128
|
+
"https://api.example.com/search",
|
|
129
|
+
params={"query": "python", "limit": 10}
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
# JSON Π΄Π°Π½Π½ΡΠ΅
|
|
133
|
+
response = rotator.post(
|
|
134
|
+
"https://api.example.com/items",
|
|
135
|
+
json={"name": "New Item", "price": 99.99}
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
# Π’Π°ΠΉΠΌΠ°ΡΡ
|
|
139
|
+
response = rotator.get(
|
|
140
|
+
"https://api.example.com/data",
|
|
141
|
+
timeout=10
|
|
142
|
+
)
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### ΠΠ±ΡΠ°Π±ΠΎΡΠΊΠ° ΠΎΡΠΈΠ±ΠΎΠΊ
|
|
146
|
+
|
|
147
|
+
```python
|
|
148
|
+
from apikeyrotator import APIKeyRotator, AllKeysExhaustedError
|
|
149
|
+
|
|
150
|
+
rotator = APIKeyRotator(api_keys=["key1", "key2"], max_retries=5)
|
|
151
|
+
|
|
152
|
+
try:
|
|
153
|
+
response = rotator.get("https://api.example.com/limited")
|
|
154
|
+
print("Π£ΡΠΏΠ΅Ρ
!", response.json())
|
|
155
|
+
except AllKeysExhaustedError as e:
|
|
156
|
+
print("ΠΡΠ΅ ΠΊΠ»ΡΡΠΈ ΠΈΡΡΠ΅ΡΠΏΠ°Π½Ρ:", e)
|
|
157
|
+
except Exception as e:
|
|
158
|
+
print("ΠΡΡΠ³Π°Ρ ΠΎΡΠΈΠ±ΠΊΠ°:", e)
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Π Π°ΡΡΠΈΡΠ΅Π½Π½ΡΠ΅ Π½Π°ΡΡΡΠΎΠΉΠΊΠΈ βοΈ
|
|
162
|
+
|
|
163
|
+
### ΠΠ°ΡΡΠΎΠΌΠΈΠ·Π°ΡΠΈΡ ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΠΎΠ²
|
|
164
|
+
|
|
165
|
+
```python
|
|
166
|
+
from apikeyrotator import APIKeyRotator
|
|
167
|
+
|
|
168
|
+
# ΠΡΠ΅ ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΡ Π½Π°ΡΡΡΠΎΠΉΠΊΠΈ
|
|
169
|
+
rotator = APIKeyRotator(
|
|
170
|
+
api_keys=["key1", "key2", "key3"], # ΠΠ»ΡΡΠΈ
|
|
171
|
+
env_var="CUSTOM_API_KEYS", # ΠΠ°ΡΡΠΎΠΌΠ½Π°Ρ ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½Π°Ρ ΠΎΠΊΡΡΠΆΠ΅Π½ΠΈΡ
|
|
172
|
+
max_retries=5, # ΠΠ°ΠΊΡΠΈΠΌΡΠΌ ΠΏΠΎΠΏΡΡΠΎΠΊ
|
|
173
|
+
base_delay=2.0 # ΠΠ°Π·ΠΎΠ²Π°Ρ Π·Π°Π΄Π΅ΡΠΆΠΊΠ° ΠΌΠ΅ΠΆΠ΄Ρ ΠΏΠΎΠΏΡΡΠΊΠ°ΠΌΠΈ
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
print(f"ΠΠΎΠ»ΠΈΡΠ΅ΡΡΠ²ΠΎ ΠΊΠ»ΡΡΠ΅ΠΉ: {len(rotator)}")
|
|
177
|
+
print(f"ΠΠ°ΠΊΡΠΈΠΌΡΠΌ ΠΏΠΎΠΏΡΡΠΎΠΊ: {rotator.max_retries}")
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### ΠΠ½ΡΠ΅Π³ΡΠ°ΡΠΈΡ Ρ ΡΡΡΠ΅ΡΡΠ²ΡΡΡΠΈΠΌ ΠΊΠΎΠ΄ΠΎΠΌ
|
|
181
|
+
|
|
182
|
+
```python
|
|
183
|
+
from apikeyrotator import APIKeyRotator
|
|
184
|
+
import requests
|
|
185
|
+
|
|
186
|
+
# Π‘ΡΡΠ΅ΡΡΠ²ΡΡΡΠΈΠΉ ΠΊΠΎΠ΄ Ρ requests
|
|
187
|
+
response = requests.get("https://api.example.com/data")
|
|
188
|
+
|
|
189
|
+
# ΠΠ΅Π³ΠΊΠΎ Π·Π°ΠΌΠ΅Π½ΠΈΡΠ΅ Π½Π° APIKeyRotator
|
|
190
|
+
rotator = APIKeyRotator(api_keys=["key1", "key2", "key3"])
|
|
191
|
+
response = rotator.get("https://api.example.com/data") # Π’ΠΎΡ ΠΆΠ΅ API!
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## Best Practices β
|
|
195
|
+
|
|
196
|
+
### 1. ΠΡΠΏΠΎΠ»ΡΠ·ΡΠΉΡΠ΅ ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΠ΅ ΠΎΠΊΡΡΠΆΠ΅Π½ΠΈΡ Π΄Π»Ρ Π±Π΅Π·ΠΎΠΏΠ°ΡΠ½ΠΎΡΡΠΈ
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
# ΠΠΈΠΊΠΎΠ³Π΄Π° Π½Π΅ Ρ
ΡΠ°Π½ΠΈΡΠ΅ ΠΊΠ»ΡΡΠΈ Π² ΠΊΠΎΠ΄Π΅!
|
|
200
|
+
# ΠΠΌΠ΅ΡΡΠΎ ΡΡΠΎΠ³ΠΎ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠΉΡΠ΅ .env ΡΠ°ΠΉΠ» ΠΈΠ»ΠΈ ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΠ΅ ΠΎΠΊΡΡΠΆΠ΅Π½ΠΈΡ
|
|
201
|
+
export API_KEYS="your_production_key_1,your_production_key_2"
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### 2. ΠΠ°ΡΡΡΠΎΠΉΡΠ΅ Π°Π΄Π΅ΠΊΠ²Π°ΡΠ½ΠΎΠ΅ ΠΊΠΎΠ»ΠΈΡΠ΅ΡΡΠ²ΠΎ ΠΏΠΎΠΏΡΡΠΎΠΊ
|
|
205
|
+
|
|
206
|
+
```python
|
|
207
|
+
# ΠΠ»Ρ 3 ΠΊΠ»ΡΡΠ΅ΠΉ ΠΈ 2 ΠΏΠΎΠΏΡΡΠΎΠΊ Π½Π° ΠΊΠ»ΡΡ = 6 Π²ΡΠ΅Π³ΠΎ ΠΏΠΎΠΏΡΡΠΎΠΊ
|
|
208
|
+
rotator = APIKeyRotator(
|
|
209
|
+
api_keys=["key1", "key2", "key3"],
|
|
210
|
+
max_retries=6
|
|
211
|
+
)
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### 3. ΠΠΎΠ½ΠΈΡΠΎΡΠΈΠ½Π³ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΡ ΠΊΠ»ΡΡΠ΅ΠΉ
|
|
215
|
+
|
|
216
|
+
```python
|
|
217
|
+
rotator = APIKeyRotator(api_keys=["key1", "key2", "key3"])
|
|
218
|
+
|
|
219
|
+
# ΠΠΎΡΠ»Π΅ Π½Π΅ΡΠΊΠΎΠ»ΡΠΊΠΈΡ
Π·Π°ΠΏΡΠΎΡΠΎΠ² ΠΌΠΎΠΆΠ½ΠΎ ΠΏΠΎΡΠΌΠΎΡΡΠ΅ΡΡ ΡΡΠ°ΡΠΈΡΡΠΈΠΊΡ
|
|
220
|
+
for i in range(10):
|
|
221
|
+
rotator.get("https://api.example.com/test")
|
|
222
|
+
|
|
223
|
+
print("Π ΠΎΡΠ°ΡΠΎΡ ΠΎΡΡΠ°Π±ΠΎΡΠ°Π»", len(rotator), "Π·Π°ΠΏΡΠΎΡΠΎΠ²")
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## ΠΠ±ΡΠ°Π±ΠΎΡΠΊΠ° ΠΎΡΠΈΠ±ΠΎΠΊ β
|
|
227
|
+
|
|
228
|
+
ΠΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠ° ΠΏΡΠ΅Π΄ΠΎΡΡΠ°Π²Π»ΡΠ΅Ρ ΠΏΠΎΠ½ΡΡΠ½ΡΠ΅ ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΡ ΠΎΠ± ΠΎΡΠΈΠ±ΠΊΠ°Ρ
:
|
|
229
|
+
|
|
230
|
+
### ΠΡΠ»ΠΈ ΠΊΠ»ΡΡΠΈ Π½Π΅ Π½Π°ΠΉΠ΄Π΅Π½Ρ
|
|
231
|
+
|
|
232
|
+
```
|
|
233
|
+
β No API keys found.
|
|
234
|
+
Please either:
|
|
235
|
+
1. Pass keys directly: APIKeyRotator(api_keys=['key1', 'key2'])
|
|
236
|
+
2. Set environment variable: export API_KEYS='key1,key2'
|
|
237
|
+
3. Create .env file with: API_KEYS=key1,key2
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### ΠΡΠ»ΠΈ Π²ΡΠ΅ ΠΊΠ»ΡΡΠΈ ΠΈΡΡΠ΅ΡΠΏΠ°Π½Ρ
|
|
241
|
+
|
|
242
|
+
```python
|
|
243
|
+
try:
|
|
244
|
+
response = rotator.get("https://api.example.com/limited")
|
|
245
|
+
except AllKeysExhaustedError as e:
|
|
246
|
+
print(e) # "All 3 keys exhausted after 6 attempts"
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
## Π‘ΠΎΠ²ΠΌΠ΅ΡΡΠΈΠΌΠΎΡΡΡ π
|
|
250
|
+
|
|
251
|
+
- **Python**: 3.7+
|
|
252
|
+
- **ΠΠ°Π²ΠΈΡΠΈΠΌΠΎΡΡΠΈ**: ΡΠΎΠ»ΡΠΊΠΎ `requests>=2.25.0`
|
|
253
|
+
|
|
254
|
+
## Π Π°Π·ΡΠ°Π±ΠΎΡΠΊΠ° π οΈ
|
|
255
|
+
|
|
256
|
+
### Π£ΡΡΠ°Π½ΠΎΠ²ΠΊΠ° Π΄Π»Ρ ΡΠ°Π·ΡΠ°Π±ΠΎΡΠΊΠΈ
|
|
257
|
+
|
|
258
|
+
```bash
|
|
259
|
+
git clone https://github.com/PrimeevolutionZ/apikeyrotator.git
|
|
260
|
+
cd apikeyrotator
|
|
261
|
+
python -m venv venv
|
|
262
|
+
source venv/bin/activate # Linux/Mac
|
|
263
|
+
# ΠΈΠ»ΠΈ
|
|
264
|
+
venv\Scripts\activate # Windows
|
|
265
|
+
pip install -e .[dev]
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### ΠΠ°ΠΏΡΡΠΊ ΡΠ΅ΡΡΠΎΠ²
|
|
269
|
+
|
|
270
|
+
```bash
|
|
271
|
+
pytest tests/
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### Π‘Π±ΠΎΡΠΊΠ° ΠΏΠ°ΠΊΠ΅ΡΠ°
|
|
275
|
+
|
|
276
|
+
```bash
|
|
277
|
+
python setup.py sdist bdist_wheel
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
## ΠΠΈΡΠ΅Π½Π·ΠΈΡ π
|
|
281
|
+
|
|
282
|
+
MIT License - ΡΠΌΠΎΡΡΠΈΡΠ΅ ΡΠ°ΠΉΠ» [LICENSE](LICENSE) Π΄Π»Ρ Π΄Π΅ΡΠ°Π»Π΅ΠΉ.
|
|
283
|
+
|
|
284
|
+
## ΠΠΎΠ΄Π΄Π΅ΡΠΆΠΊΠ° π€
|
|
285
|
+
|
|
286
|
+
ΠΠ°ΡΠ»ΠΈ Π±Π°Π³ ΠΈΠ»ΠΈ Π΅ΡΡΡ ΠΏΡΠ΅Π΄Π»ΠΎΠΆΠ΅Π½ΠΈΡ? [Π‘ΠΎΠ·Π΄Π°ΠΉΡΠ΅ issue](https://github.com/yourusername/apikeyrotator/issues) Π½Π° GitHub!
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
**API Key Rotator** - ΡΠ΄Π΅Π»Π°ΠΉΡΠ΅ Π²Π°ΡΠΈ API Π·Π°ΠΏΡΠΎΡΡ Π½Π΅ΡΡΠ·Π²ΠΈΠΌΡΠΌΠΈ ΠΊ ΠΎΠ³ΡΠ°Π½ΠΈΡΠ΅Π½ΠΈΡΠΌ! π
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from .rotator import APIKeyRotator
|
|
2
|
+
from .exceptions import APIKeyError, NoAPIKeysError, AllKeysExhaustedError
|
|
3
|
+
|
|
4
|
+
__version__ = "0.0.2"
|
|
5
|
+
__author__ = "Prime Evolution"
|
|
6
|
+
__email__ = "develop@eclps-team.ru"
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
'APIKeyRotator',
|
|
10
|
+
'APIKeyError',
|
|
11
|
+
'NoAPIKeysError',
|
|
12
|
+
'AllKeysExhaustedError'
|
|
13
|
+
]
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
class APIKeyError(Exception):
|
|
2
|
+
"""ΠΠ°Π·ΠΎΠ²ΠΎΠ΅ ΠΈΡΠΊΠ»ΡΡΠ΅Π½ΠΈΠ΅ Π΄Π»Ρ ΠΎΡΠΈΠ±ΠΎΠΊ API ΠΊΠ»ΡΡΠ΅ΠΉ"""
|
|
3
|
+
pass
|
|
4
|
+
|
|
5
|
+
class NoAPIKeysError(APIKeyError):
|
|
6
|
+
"""ΠΠ΅ Π½Π°ΠΉΠ΄Π΅Π½ΠΎ Π½ΠΈ ΠΎΠ΄Π½ΠΎΠ³ΠΎ API ΠΊΠ»ΡΡΠ°"""
|
|
7
|
+
pass
|
|
8
|
+
|
|
9
|
+
class AllKeysExhaustedError(APIKeyError):
|
|
10
|
+
"""ΠΡΠ΅ ΠΊΠ»ΡΡΠΈ ΠΈΡΡΠ΅ΡΠΏΠ°Π½Ρ"""
|
|
11
|
+
pass
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import time
|
|
3
|
+
import requests
|
|
4
|
+
from typing import List, Optional, Dict, Union, Callable
|
|
5
|
+
from .exceptions import NoAPIKeysError, AllKeysExhaustedError
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class APIKeyRotator:
|
|
9
|
+
"""
|
|
10
|
+
Π‘ΡΠΏΠ΅Ρ-ΠΏΡΠΎΡΡΠΎΠΉ Π² ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠΈ, Π½ΠΎ ΠΌΠΎΡΠ½ΡΠΉ ΡΠΎΡΠ°ΡΠΎΡ API ΠΊΠ»ΡΡΠ΅ΠΉ.
|
|
11
|
+
ΠΠ²ΡΠΎΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠΈ ΠΎΠ±ΡΠ°Π±Π°ΡΡΠ²Π°Π΅Ρ Π»ΠΈΠΌΠΈΡΡ, ΠΎΡΠΈΠ±ΠΊΠΈ ΠΈ ΡΠ΅ΡΡΠ°ΠΈ.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
def __init__(
|
|
15
|
+
self,
|
|
16
|
+
api_keys: Optional[Union[List[str], str]] = None,
|
|
17
|
+
env_var: str = "API_KEYS",
|
|
18
|
+
max_retries: int = 3,
|
|
19
|
+
base_delay: float = 1.0
|
|
20
|
+
):
|
|
21
|
+
"""
|
|
22
|
+
ΠΡΠΎΡΡΠ°Ρ ΠΈΠ½ΠΈΡΠΈΠ°Π»ΠΈΠ·Π°ΡΠΈΡ - ΠΏΠ΅ΡΠ΅Π΄Π°ΠΉ ΠΊΠ»ΡΡΠΈ ΠΈΠ»ΠΈ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠΉ ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΠ΅ ΠΎΠΊΡΡΠΆΠ΅Π½ΠΈΡ
|
|
23
|
+
|
|
24
|
+
:param api_keys: Π‘ΠΏΠΈΡΠΎΠΊ ΠΊΠ»ΡΡΠ΅ΠΉ ΠΈΠ»ΠΈ ΡΡΡΠΎΠΊΠ° Ρ ΠΊΠ»ΡΡΠ°ΠΌΠΈ ΡΠ΅ΡΠ΅Π· Π·Π°ΠΏΡΡΡΡ
|
|
25
|
+
:param env_var: ΠΠΌΡ ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΠΎΠΉ ΠΎΠΊΡΡΠΆΠ΅Π½ΠΈΡ
|
|
26
|
+
:param max_retries: ΠΠ°ΠΊΡΠΈΠΌΠ°Π»ΡΠ½ΠΎΠ΅ ΠΊΠΎΠ»ΠΈΡΠ΅ΡΡΠ²ΠΎ ΠΏΠΎΠΏΡΡΠΎΠΊ
|
|
27
|
+
:param base_delay: ΠΠ°Π·ΠΎΠ²Π°Ρ Π·Π°Π΄Π΅ΡΠΆΠΊΠ° ΠΌΠ΅ΠΆΠ΄Ρ ΠΏΠΎΠΏΡΡΠΊΠ°ΠΌΠΈ
|
|
28
|
+
"""
|
|
29
|
+
self.keys = self._parse_keys(api_keys, env_var)
|
|
30
|
+
self.max_retries = max_retries
|
|
31
|
+
self.base_delay = base_delay
|
|
32
|
+
self.current_index = 0
|
|
33
|
+
self.session = requests.Session()
|
|
34
|
+
print(f"β
APIKeyRotator ΠΈΠ½ΠΈΡΠΈΠ°Π»ΠΈΠ·ΠΈΡΠΎΠ²Π°Π½ Ρ {len(self.keys)} ΠΊΠ»ΡΡΠ°ΠΌΠΈ")
|
|
35
|
+
|
|
36
|
+
def _parse_keys(self, api_keys, env_var) -> List[str]:
|
|
37
|
+
"""Π£ΠΌΠ½ΡΠΉ ΠΏΠ°ΡΡΠΈΠ½Π³ ΠΊΠ»ΡΡΠ΅ΠΉ ΠΈΠ· ΡΠ°Π·Π½ΡΡ
ΠΈΡΡΠΎΡΠ½ΠΈΠΊΠΎΠ² Ρ ΠΏΠΎΠ½ΡΡΠ½ΡΠΌΠΈ ΠΎΡΠΈΠ±ΠΊΠ°ΠΌΠΈ"""
|
|
38
|
+
# ΠΡΠ»ΠΈ ΠΊΠ»ΡΡΠΈ ΠΏΠ΅ΡΠ΅Π΄Π°Π½Ρ Π½Π°ΠΏΡΡΠΌΡΡ
|
|
39
|
+
if api_keys is not None:
|
|
40
|
+
if isinstance(api_keys, str):
|
|
41
|
+
keys = [k.strip() for k in api_keys.split(",") if k.strip()]
|
|
42
|
+
elif isinstance(api_keys, list):
|
|
43
|
+
keys = api_keys
|
|
44
|
+
else:
|
|
45
|
+
raise NoAPIKeysError("β API keys must be a list or comma-separated string")
|
|
46
|
+
|
|
47
|
+
if not keys:
|
|
48
|
+
raise NoAPIKeysError("β No API keys provided in the api_keys parameter")
|
|
49
|
+
|
|
50
|
+
return keys
|
|
51
|
+
|
|
52
|
+
# ΠΡΠ»ΠΈ ΠΊΠ»ΡΡΠΈ ΠΈΡΠ΅ΠΌ Π² ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΡ
ΠΎΠΊΡΡΠΆΠ΅Π½ΠΈΡ
|
|
53
|
+
keys_str = os.getenv(env_var)
|
|
54
|
+
|
|
55
|
+
if keys_str is None:
|
|
56
|
+
raise NoAPIKeysError(
|
|
57
|
+
f"β No API keys found.\n"
|
|
58
|
+
f" Please either:\n"
|
|
59
|
+
f" 1. Pass keys directly: APIKeyRotator(api_keys=['key1', 'key2'])\n"
|
|
60
|
+
f" 2. Set environment variable: export {env_var}='key1,key2'\n"
|
|
61
|
+
f" 3. Create .env file with: {env_var}=key1,key2"
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
if not keys_str.strip():
|
|
65
|
+
raise NoAPIKeysError(
|
|
66
|
+
f"β Environment variable ${env_var} is empty.\n"
|
|
67
|
+
f" Please set it with: export {env_var}='your_key1,your_key2'"
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
keys = [k.strip() for k in keys_str.split(",") if k.strip()]
|
|
71
|
+
|
|
72
|
+
if not keys:
|
|
73
|
+
raise NoAPIKeysError(
|
|
74
|
+
f"β No valid API keys found in ${env_var}.\n"
|
|
75
|
+
f" Format should be: key1,key2,key3\n"
|
|
76
|
+
f" Current value: '{keys_str}'"
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
return keys
|
|
80
|
+
|
|
81
|
+
def get_next_key(self) -> str:
|
|
82
|
+
"""ΠΠΎΠ»ΡΡΠΈΡΡ ΡΠ»Π΅Π΄ΡΡΡΠΈΠΉ ΠΊΠ»ΡΡ"""
|
|
83
|
+
key = self.keys[self.current_index]
|
|
84
|
+
self.current_index = (self.current_index + 1) % len(self.keys)
|
|
85
|
+
return key
|
|
86
|
+
|
|
87
|
+
def _should_retry(self, response: requests.Response) -> bool:
|
|
88
|
+
"""ΠΠΏΡΠ΅Π΄Π΅Π»ΡΠ΅Ρ, Π½ΡΠΆΠ½ΠΎ Π»ΠΈ ΠΏΠΎΠ²ΡΠΎΡΡΡΡ Π·Π°ΠΏΡΠΎΡ"""
|
|
89
|
+
return response.status_code in [429, 500, 502, 503, 504]
|
|
90
|
+
|
|
91
|
+
def _prepare_headers(self, key: str, custom_headers: dict) -> dict:
|
|
92
|
+
"""ΠΠΎΠ΄Π³ΠΎΡΠ°Π²Π»ΠΈΠ²Π°Π΅Ρ Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΊΠΈ Ρ Π°Π²ΡΠΎΡΠΈΠ·Π°ΡΠΈΠ΅ΠΉ"""
|
|
93
|
+
headers = custom_headers.copy() if custom_headers else {}
|
|
94
|
+
|
|
95
|
+
# ΠΠ²ΡΠΎΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠΈ ΠΎΠΏΡΠ΅Π΄Π΅Π»ΡΠ΅ΠΌ ΡΠΈΠΏ Π°Π²ΡΠΎΡΠΈΠ·Π°ΡΠΈΠΈ
|
|
96
|
+
if "Authorization" not in headers:
|
|
97
|
+
if key.startswith("sk-") or key.startswith("pk-"): # OpenAI style
|
|
98
|
+
headers["Authorization"] = f"Bearer {key}"
|
|
99
|
+
elif len(key) == 32: # API key style
|
|
100
|
+
headers["X-API-Key"] = key
|
|
101
|
+
else: # Default
|
|
102
|
+
headers["Authorization"] = f"Key {key}"
|
|
103
|
+
|
|
104
|
+
return headers
|
|
105
|
+
|
|
106
|
+
def request(
|
|
107
|
+
self,
|
|
108
|
+
method: str,
|
|
109
|
+
url: str,
|
|
110
|
+
**kwargs
|
|
111
|
+
) -> requests.Response:
|
|
112
|
+
"""
|
|
113
|
+
ΠΡΠΏΠΎΠ»Π½ΡΠ΅Ρ Π·Π°ΠΏΡΠΎΡ. ΠΡΠΎΡΡΠΎ ΠΊΠ°ΠΊ requests, Π½ΠΎ Ρ ΡΠΎΡΠ°ΡΠΈΠ΅ΠΉ ΠΊΠ»ΡΡΠ΅ΠΉ!
|
|
114
|
+
"""
|
|
115
|
+
|
|
116
|
+
for attempt in range(self.max_retries):
|
|
117
|
+
key = self.get_next_key()
|
|
118
|
+
|
|
119
|
+
# ΠΠ²ΡΠΎΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠΈ Π³ΠΎΡΠΎΠ²ΠΈΠΌ Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΊΠΈ
|
|
120
|
+
kwargs['headers'] = self._prepare_headers(key, kwargs.get('headers', {}))
|
|
121
|
+
|
|
122
|
+
try:
|
|
123
|
+
response = self.session.request(method, url, **kwargs)
|
|
124
|
+
|
|
125
|
+
if not self._should_retry(response):
|
|
126
|
+
return response
|
|
127
|
+
|
|
128
|
+
print(f"β» Attempt {attempt + 1}/{self.max_retries}. Key {key[:8]}... rate limited")
|
|
129
|
+
|
|
130
|
+
except requests.RequestException as e:
|
|
131
|
+
print(f"β οΈ Network error: {e}. Trying next key...")
|
|
132
|
+
|
|
133
|
+
# ΠΠΊΡΠΏΠΎΠ½Π΅Π½ΡΠΈΠ°Π»ΡΠ½Π°Ρ Π·Π°Π΄Π΅ΡΠΆΠΊΠ°
|
|
134
|
+
if attempt < self.max_retries - 1:
|
|
135
|
+
delay = self.base_delay * (2 ** attempt)
|
|
136
|
+
time.sleep(delay)
|
|
137
|
+
|
|
138
|
+
raise AllKeysExhaustedError(f"All {len(self.keys)} keys exhausted after {self.max_retries} attempts")
|
|
139
|
+
|
|
140
|
+
# ΠΡΠΎΡΡΠ΅ΠΉΡΠΈΠ΅ ΠΌΠ΅ΡΠΎΠ΄Ρ-ΠΎΠ±Π΅ΡΡΠΊΠΈ
|
|
141
|
+
def get(self, url, **kwargs):
|
|
142
|
+
return self.request('GET', url, **kwargs)
|
|
143
|
+
|
|
144
|
+
def post(self, url, **kwargs):
|
|
145
|
+
return self.request('POST', url, **kwargs)
|
|
146
|
+
|
|
147
|
+
def put(self, url, **kwargs):
|
|
148
|
+
return self.request('PUT', url, **kwargs)
|
|
149
|
+
|
|
150
|
+
def delete(self, url, **kwargs):
|
|
151
|
+
return self.request('DELETE', url, **kwargs)
|
|
152
|
+
|
|
153
|
+
# Π£Π΄ΠΎΠ±Π½ΡΠ΅ ΡΠ²ΠΎΠΉΡΡΠ²Π°
|
|
154
|
+
@property
|
|
155
|
+
def key_count(self):
|
|
156
|
+
return len(self.keys)
|
|
157
|
+
|
|
158
|
+
def __len__(self):
|
|
159
|
+
return len(self.keys)
|
|
160
|
+
|
|
161
|
+
def __repr__(self):
|
|
162
|
+
return f"<APIKeyRotator keys={self.key_count} retries={self.max_retries}>"
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import time
|
|
2
|
+
from typing import Callable, Any, Type
|
|
3
|
+
import requests
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def retry_with_backoff(
|
|
7
|
+
func: Callable,
|
|
8
|
+
retries: int = 3,
|
|
9
|
+
backoff_factor: float = 0.5,
|
|
10
|
+
exceptions: Type[Exception] = Exception
|
|
11
|
+
) -> Any:
|
|
12
|
+
"""
|
|
13
|
+
Π£Π½ΠΈΠ²Π΅ΡΡΠ°Π»ΡΠ½Π°Ρ ΡΡΠ½ΠΊΡΠΈΡ Π΄Π»Ρ ΠΏΠΎΠ²ΡΠΎΡΠ½ΡΡ
ΠΏΠΎΠΏΡΡΠΎΠΊ Ρ ΡΠΊΡΠΏΠΎΠ½Π΅Π½ΡΠΈΠ°Π»ΡΠ½ΠΎΠΉ Π·Π°Π΄Π΅ΡΠΆΠΊΠΎΠΉ.
|
|
14
|
+
|
|
15
|
+
ΠΡΠΈΠΌΠ΅Ρ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΡ:
|
|
16
|
+
response = retry_with_backoff(
|
|
17
|
+
lambda: requests.get('https://api.example.com'),
|
|
18
|
+
exceptions=requests.RequestException
|
|
19
|
+
)
|
|
20
|
+
"""
|
|
21
|
+
for attempt in range(retries):
|
|
22
|
+
try:
|
|
23
|
+
return func()
|
|
24
|
+
except exceptions as e:
|
|
25
|
+
if attempt == retries - 1:
|
|
26
|
+
raise e
|
|
27
|
+
delay = backoff_factor * (2 ** attempt)
|
|
28
|
+
time.sleep(delay)
|
|
29
|
+
print(f"Retry {attempt + 1}/{retries} after {delay:.1f}s delay")
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: apikeyrotator
|
|
3
|
+
Version: 0.0.2
|
|
4
|
+
Summary: Ultra simple API key rotation for bypassing rate limits
|
|
5
|
+
Author-email: Prime Evolution <develop@eclips-team.ru>
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2024 Prime Evolution
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
Project-URL: Homepage, https://github.com/PrimeevolutionZ/apikeyrotator
|
|
28
|
+
Project-URL: Repository, https://github.com/PrimeevolutionZ/apikeyrotator
|
|
29
|
+
Project-URL: Issues, https://github.com/PrimeevolutionZ/apikeyrotator/issues
|
|
30
|
+
Keywords: api,rotation,rate limit,requests
|
|
31
|
+
Classifier: Development Status :: 4 - Beta
|
|
32
|
+
Classifier: Intended Audience :: Developers
|
|
33
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
34
|
+
Classifier: Programming Language :: Python :: 3
|
|
35
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
36
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
37
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
38
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
39
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
40
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
41
|
+
Description-Content-Type: text/markdown
|
|
42
|
+
License-File: LICENSE
|
|
43
|
+
Requires-Dist: requests>=2.25.0
|
|
44
|
+
Dynamic: license-file
|
|
45
|
+
|
|
46
|
+
# API Key Rotator π
|
|
47
|
+
|
|
48
|
+
ΠΡΠΎΡΡΠ°Ρ, Π½ΠΎ ΠΌΠΎΡΠ½Π°Ρ Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠ° Π΄Π»Ρ Π°Π²ΡΠΎΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠΎΠ³ΠΎ Π²ΡΠ°ΡΠ΅Π½ΠΈΡ API ΠΊΠ»ΡΡΠ΅ΠΉ. ΠΠ±Ρ
ΠΎΠ΄ΠΈΡ Π»ΠΈΠΌΠΈΡΡ Π·Π°ΠΏΡΠΎΡΠΎΠ², ΠΎΠ±ΡΠ°Π±Π°ΡΡΠ²Π°Π΅Ρ ΠΎΡΠΈΠ±ΠΊΠΈ 429 ΠΈ Π΄Π΅Π»Π°Π΅Ρ Π²Π°ΡΠΈ Π·Π°ΠΏΡΠΎΡΡ Π½Π΅ΡΡΠ·Π²ΠΈΠΌΡΠΌΠΈ ΠΊ ΠΎΠ³ΡΠ°Π½ΠΈΡΠ΅Π½ΠΈΡΠΌ API.
|
|
49
|
+
|
|
50
|
+
## ΠΡΠΎΠ±Π΅Π½Π½ΠΎΡΡΠΈ β¨
|
|
51
|
+
|
|
52
|
+
- π **ΠΠ²ΡΠΎΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠΎΠ΅ Π²ΡΠ°ΡΠ΅Π½ΠΈΠ΅ ΠΊΠ»ΡΡΠ΅ΠΉ** - Round-robin Π°Π»Π³ΠΎΡΠΈΡΠΌ
|
|
53
|
+
- β‘ **ΠΠ²ΡΠΎΠ΄Π΅ΡΠ΅ΠΊΡ Π°Π²ΡΠΎΡΠΈΠ·Π°ΡΠΈΠΈ** - Π‘Π°ΠΌ ΠΎΠΏΡΠ΅Π΄Π΅Π»ΡΠ΅Ρ Bearer/API-Key ΡΠΎΡΠΌΠ°Ρ
|
|
54
|
+
- π **Π£ΠΌΠ½ΡΠ΅ ΡΠ΅ΡΡΠ°ΠΈ** - ΠΠΊΡΠΏΠΎΠ½Π΅Π½ΡΠΈΠ°Π»ΡΠ½Π°Ρ Π·Π°Π΄Π΅ΡΠΆΠΊΠ° ΠΏΡΠΈ ΠΎΡΠΈΠ±ΠΊΠ°Ρ
|
|
55
|
+
- π‘οΈ **ΠΠ±ΡΠ°Π±ΠΎΡΠΊΠ° ΠΎΡΠΈΠ±ΠΎΠΊ** - 429, 500, 502, 503, 504 Π°Π²ΡΠΎΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠΈ
|
|
56
|
+
- π» **ΠΠΎΠ»Π½Π°Ρ ΡΠΎΠ²ΠΌΠ΅ΡΡΠΈΠΌΠΎΡΡΡ** Ρ Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠΎΠΉ `requests`
|
|
57
|
+
- π **ΠΠΎΠ½ΡΡΠ½ΡΠ΅ ΠΎΡΠΈΠ±ΠΊΠΈ** - ΠΠΎΠ΄ΡΠΊΠ°Π·ΠΊΠΈ ΠΊΠ°ΠΊ ΠΈΡΠΏΡΠ°Π²ΠΈΡΡ ΠΏΡΠΎΠ±Π»Π΅ΠΌΡ
|
|
58
|
+
|
|
59
|
+
## Π£ΡΡΠ°Π½ΠΎΠ²ΠΊΠ° π¦
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
pip install apikeyrotator
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
ΠΠ»ΠΈ ΠΈΠ· ΠΈΡΡ
ΠΎΠ΄Π½ΠΈΠΊΠΎΠ²:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
git clone https://github.com/PrimeevolutionZ/apikeyrotator.git
|
|
69
|
+
cd apikeyrotator
|
|
70
|
+
pip install -e .
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## ΠΡΡΡΡΡΠΉ ΡΡΠ°ΡΡ π
|
|
74
|
+
|
|
75
|
+
### Π‘ΠΏΠΎΡΠΎΠ± 1: ΠΠ΅ΡΠ΅Π΄Π°ΡΠ° ΠΊΠ»ΡΡΠ΅ΠΉ Π½Π°ΠΏΡΡΠΌΡΡ
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
from apikeyrotator import APIKeyRotator
|
|
79
|
+
|
|
80
|
+
# ΠΡΠΎΡΡΠΎ ΠΏΠ΅ΡΠ΅Π΄Π°ΠΉΡΠ΅ ΠΊΠ»ΡΡΠΈ ΡΠΏΠΈΡΠΊΠΎΠΌ
|
|
81
|
+
rotator = APIKeyRotator(api_keys=["key1", "key2", "key3"])
|
|
82
|
+
|
|
83
|
+
# ΠΠ»ΠΈ ΡΡΡΠΎΠΊΠΎΠΉ ΡΠ΅ΡΠ΅Π· Π·Π°ΠΏΡΡΡΡ
|
|
84
|
+
rotator = APIKeyRotator(api_keys="key1,key2,key3")
|
|
85
|
+
|
|
86
|
+
# ΠΡΠΏΠΎΠ»ΡΠ·ΡΠΉΡΠ΅ ΠΊΠ°ΠΊ ΠΎΠ±ΡΡΠ½ΡΠΉ requests!
|
|
87
|
+
response = rotator.get("https://api.example.com/data")
|
|
88
|
+
print(response.json())
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Π‘ΠΏΠΎΡΠΎΠ± 2: ΠΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠ΅ ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΡ
ΠΎΠΊΡΡΠΆΠ΅Π½ΠΈΡ
|
|
92
|
+
|
|
93
|
+
Π‘ΠΎΠ·Π΄Π°ΠΉΡΠ΅ ΡΠ°ΠΉΠ» `.env` Π² ΠΊΠΎΡΠ½Π΅ ΠΏΡΠΎΠ΅ΠΊΡΠ°:
|
|
94
|
+
```env
|
|
95
|
+
API_KEYS=your_key_1,your_key_2,your_key_3
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
ΠΠ»ΠΈ ΡΡΡΠ°Π½ΠΎΠ²ΠΈΡΠ΅ ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΡ ΠΎΠΊΡΡΠΆΠ΅Π½ΠΈΡ:
|
|
99
|
+
```bash
|
|
100
|
+
# Linux/Mac
|
|
101
|
+
export API_KEYS="your_key_1,your_key_2,your_key_3"
|
|
102
|
+
|
|
103
|
+
# Windows
|
|
104
|
+
set API_KEYS=your_key_1,your_key_2,your_key_3
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Π’Π΅ΠΏΠ΅ΡΡ ΠΏΡΠΎΡΡΠΎ ΠΈΠ½ΠΈΡΠΈΠ°Π»ΠΈΠ·ΠΈΡΡΠΉΡΠ΅ ΡΠΎΡΠ°ΡΠΎΡ:
|
|
108
|
+
```python
|
|
109
|
+
from apikeyrotator import APIKeyRotator
|
|
110
|
+
|
|
111
|
+
rotator = APIKeyRotator() # ΠΠ²ΡΠΎΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠΈ Π½Π°ΠΉΠ΄Π΅Ρ API_KEYS
|
|
112
|
+
|
|
113
|
+
response = rotator.get("https://api.example.com/data")
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## ΠΡΠΈΠΌΠ΅ΡΡ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΡ π
|
|
117
|
+
|
|
118
|
+
### ΠΠ°Π·ΠΎΠ²ΡΠ΅ HTTP-Π·Π°ΠΏΡΠΎΡΡ
|
|
119
|
+
|
|
120
|
+
```python
|
|
121
|
+
from apikeyrotator import APIKeyRotator
|
|
122
|
+
|
|
123
|
+
rotator = APIKeyRotator(api_keys=["key1", "key2", "key3"])
|
|
124
|
+
|
|
125
|
+
# GET Π·Π°ΠΏΡΠΎΡ
|
|
126
|
+
response = rotator.get("https://api.example.com/users")
|
|
127
|
+
|
|
128
|
+
# POST Π·Π°ΠΏΡΠΎΡ Ρ Π΄Π°Π½Π½ΡΠΌΠΈ
|
|
129
|
+
response = rotator.post(
|
|
130
|
+
"https://api.example.com/users",
|
|
131
|
+
json={"name": "John", "email": "john@example.com"}
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
# PUT Π·Π°ΠΏΡΠΎΡ
|
|
135
|
+
response = rotator.put(
|
|
136
|
+
"https://api.example.com/users/1",
|
|
137
|
+
json={"name": "John Updated"}
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
# DELETE Π·Π°ΠΏΡΠΎΡ
|
|
141
|
+
response = rotator.delete("https://api.example.com/users/1")
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### ΠΠ°ΡΡΠΎΠΌΠ½ΡΠ΅ Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΊΠΈ Π°Π²ΡΠΎΡΠΈΠ·Π°ΡΠΈΠΈ
|
|
145
|
+
|
|
146
|
+
```python
|
|
147
|
+
from apikeyrotator import APIKeyRotator
|
|
148
|
+
|
|
149
|
+
rotator = APIKeyRotator(api_keys=["key1", "key2"])
|
|
150
|
+
|
|
151
|
+
# ΠΠ°ΡΡΠΎΠΌΠ½ΡΠΉ Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΎΠΊ Π°Π²ΡΠΎΡΠΈΠ·Π°ΡΠΈΠΈ
|
|
152
|
+
response = rotator.get(
|
|
153
|
+
"https://api.example.com/data",
|
|
154
|
+
headers={"X-Custom-Auth": "custom_value"}
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
# ΠΠ»ΠΈ ΠΏΠ΅ΡΠ΅ΠΎΠΏΡΠ΅Π΄Π΅Π»ΠΈΡΠ΅ Π°Π²ΡΠΎΡΠΈΠ·Π°ΡΠΈΡ ΠΏΠΎΠ»Π½ΠΎΡΡΡΡ
|
|
158
|
+
response = rotator.get(
|
|
159
|
+
"https://api.example.com/data",
|
|
160
|
+
headers={"Authorization": "Custom your_token_here"}
|
|
161
|
+
)
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Π Π°Π±ΠΎΡΠ° Ρ ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΠ°ΠΌΠΈ Π·Π°ΠΏΡΠΎΡΠ°
|
|
165
|
+
|
|
166
|
+
```python
|
|
167
|
+
from apikeyrotator import APIKeyRotator
|
|
168
|
+
|
|
169
|
+
rotator = APIKeyRotator(api_keys=["key1", "key2", "key3"])
|
|
170
|
+
|
|
171
|
+
# ΠΠ°ΡΠ°ΠΌΠ΅ΡΡΡ Π·Π°ΠΏΡΠΎΡΠ°
|
|
172
|
+
response = rotator.get(
|
|
173
|
+
"https://api.example.com/search",
|
|
174
|
+
params={"query": "python", "limit": 10}
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
# JSON Π΄Π°Π½Π½ΡΠ΅
|
|
178
|
+
response = rotator.post(
|
|
179
|
+
"https://api.example.com/items",
|
|
180
|
+
json={"name": "New Item", "price": 99.99}
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
# Π’Π°ΠΉΠΌΠ°ΡΡ
|
|
184
|
+
response = rotator.get(
|
|
185
|
+
"https://api.example.com/data",
|
|
186
|
+
timeout=10
|
|
187
|
+
)
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### ΠΠ±ΡΠ°Π±ΠΎΡΠΊΠ° ΠΎΡΠΈΠ±ΠΎΠΊ
|
|
191
|
+
|
|
192
|
+
```python
|
|
193
|
+
from apikeyrotator import APIKeyRotator, AllKeysExhaustedError
|
|
194
|
+
|
|
195
|
+
rotator = APIKeyRotator(api_keys=["key1", "key2"], max_retries=5)
|
|
196
|
+
|
|
197
|
+
try:
|
|
198
|
+
response = rotator.get("https://api.example.com/limited")
|
|
199
|
+
print("Π£ΡΠΏΠ΅Ρ
!", response.json())
|
|
200
|
+
except AllKeysExhaustedError as e:
|
|
201
|
+
print("ΠΡΠ΅ ΠΊΠ»ΡΡΠΈ ΠΈΡΡΠ΅ΡΠΏΠ°Π½Ρ:", e)
|
|
202
|
+
except Exception as e:
|
|
203
|
+
print("ΠΡΡΠ³Π°Ρ ΠΎΡΠΈΠ±ΠΊΠ°:", e)
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## Π Π°ΡΡΠΈΡΠ΅Π½Π½ΡΠ΅ Π½Π°ΡΡΡΠΎΠΉΠΊΠΈ βοΈ
|
|
207
|
+
|
|
208
|
+
### ΠΠ°ΡΡΠΎΠΌΠΈΠ·Π°ΡΠΈΡ ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΠΎΠ²
|
|
209
|
+
|
|
210
|
+
```python
|
|
211
|
+
from apikeyrotator import APIKeyRotator
|
|
212
|
+
|
|
213
|
+
# ΠΡΠ΅ ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΡ Π½Π°ΡΡΡΠΎΠΉΠΊΠΈ
|
|
214
|
+
rotator = APIKeyRotator(
|
|
215
|
+
api_keys=["key1", "key2", "key3"], # ΠΠ»ΡΡΠΈ
|
|
216
|
+
env_var="CUSTOM_API_KEYS", # ΠΠ°ΡΡΠΎΠΌΠ½Π°Ρ ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½Π°Ρ ΠΎΠΊΡΡΠΆΠ΅Π½ΠΈΡ
|
|
217
|
+
max_retries=5, # ΠΠ°ΠΊΡΠΈΠΌΡΠΌ ΠΏΠΎΠΏΡΡΠΎΠΊ
|
|
218
|
+
base_delay=2.0 # ΠΠ°Π·ΠΎΠ²Π°Ρ Π·Π°Π΄Π΅ΡΠΆΠΊΠ° ΠΌΠ΅ΠΆΠ΄Ρ ΠΏΠΎΠΏΡΡΠΊΠ°ΠΌΠΈ
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
print(f"ΠΠΎΠ»ΠΈΡΠ΅ΡΡΠ²ΠΎ ΠΊΠ»ΡΡΠ΅ΠΉ: {len(rotator)}")
|
|
222
|
+
print(f"ΠΠ°ΠΊΡΠΈΠΌΡΠΌ ΠΏΠΎΠΏΡΡΠΎΠΊ: {rotator.max_retries}")
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### ΠΠ½ΡΠ΅Π³ΡΠ°ΡΠΈΡ Ρ ΡΡΡΠ΅ΡΡΠ²ΡΡΡΠΈΠΌ ΠΊΠΎΠ΄ΠΎΠΌ
|
|
226
|
+
|
|
227
|
+
```python
|
|
228
|
+
from apikeyrotator import APIKeyRotator
|
|
229
|
+
import requests
|
|
230
|
+
|
|
231
|
+
# Π‘ΡΡΠ΅ΡΡΠ²ΡΡΡΠΈΠΉ ΠΊΠΎΠ΄ Ρ requests
|
|
232
|
+
response = requests.get("https://api.example.com/data")
|
|
233
|
+
|
|
234
|
+
# ΠΠ΅Π³ΠΊΠΎ Π·Π°ΠΌΠ΅Π½ΠΈΡΠ΅ Π½Π° APIKeyRotator
|
|
235
|
+
rotator = APIKeyRotator(api_keys=["key1", "key2", "key3"])
|
|
236
|
+
response = rotator.get("https://api.example.com/data") # Π’ΠΎΡ ΠΆΠ΅ API!
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## Best Practices β
|
|
240
|
+
|
|
241
|
+
### 1. ΠΡΠΏΠΎΠ»ΡΠ·ΡΠΉΡΠ΅ ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΠ΅ ΠΎΠΊΡΡΠΆΠ΅Π½ΠΈΡ Π΄Π»Ρ Π±Π΅Π·ΠΎΠΏΠ°ΡΠ½ΠΎΡΡΠΈ
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
# ΠΠΈΠΊΠΎΠ³Π΄Π° Π½Π΅ Ρ
ΡΠ°Π½ΠΈΡΠ΅ ΠΊΠ»ΡΡΠΈ Π² ΠΊΠΎΠ΄Π΅!
|
|
245
|
+
# ΠΠΌΠ΅ΡΡΠΎ ΡΡΠΎΠ³ΠΎ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠΉΡΠ΅ .env ΡΠ°ΠΉΠ» ΠΈΠ»ΠΈ ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΠ΅ ΠΎΠΊΡΡΠΆΠ΅Π½ΠΈΡ
|
|
246
|
+
export API_KEYS="your_production_key_1,your_production_key_2"
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### 2. ΠΠ°ΡΡΡΠΎΠΉΡΠ΅ Π°Π΄Π΅ΠΊΠ²Π°ΡΠ½ΠΎΠ΅ ΠΊΠΎΠ»ΠΈΡΠ΅ΡΡΠ²ΠΎ ΠΏΠΎΠΏΡΡΠΎΠΊ
|
|
250
|
+
|
|
251
|
+
```python
|
|
252
|
+
# ΠΠ»Ρ 3 ΠΊΠ»ΡΡΠ΅ΠΉ ΠΈ 2 ΠΏΠΎΠΏΡΡΠΎΠΊ Π½Π° ΠΊΠ»ΡΡ = 6 Π²ΡΠ΅Π³ΠΎ ΠΏΠΎΠΏΡΡΠΎΠΊ
|
|
253
|
+
rotator = APIKeyRotator(
|
|
254
|
+
api_keys=["key1", "key2", "key3"],
|
|
255
|
+
max_retries=6
|
|
256
|
+
)
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### 3. ΠΠΎΠ½ΠΈΡΠΎΡΠΈΠ½Π³ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΡ ΠΊΠ»ΡΡΠ΅ΠΉ
|
|
260
|
+
|
|
261
|
+
```python
|
|
262
|
+
rotator = APIKeyRotator(api_keys=["key1", "key2", "key3"])
|
|
263
|
+
|
|
264
|
+
# ΠΠΎΡΠ»Π΅ Π½Π΅ΡΠΊΠΎΠ»ΡΠΊΠΈΡ
Π·Π°ΠΏΡΠΎΡΠΎΠ² ΠΌΠΎΠΆΠ½ΠΎ ΠΏΠΎΡΠΌΠΎΡΡΠ΅ΡΡ ΡΡΠ°ΡΠΈΡΡΠΈΠΊΡ
|
|
265
|
+
for i in range(10):
|
|
266
|
+
rotator.get("https://api.example.com/test")
|
|
267
|
+
|
|
268
|
+
print("Π ΠΎΡΠ°ΡΠΎΡ ΠΎΡΡΠ°Π±ΠΎΡΠ°Π»", len(rotator), "Π·Π°ΠΏΡΠΎΡΠΎΠ²")
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
## ΠΠ±ΡΠ°Π±ΠΎΡΠΊΠ° ΠΎΡΠΈΠ±ΠΎΠΊ β
|
|
272
|
+
|
|
273
|
+
ΠΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠ° ΠΏΡΠ΅Π΄ΠΎΡΡΠ°Π²Π»ΡΠ΅Ρ ΠΏΠΎΠ½ΡΡΠ½ΡΠ΅ ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΡ ΠΎΠ± ΠΎΡΠΈΠ±ΠΊΠ°Ρ
:
|
|
274
|
+
|
|
275
|
+
### ΠΡΠ»ΠΈ ΠΊΠ»ΡΡΠΈ Π½Π΅ Π½Π°ΠΉΠ΄Π΅Π½Ρ
|
|
276
|
+
|
|
277
|
+
```
|
|
278
|
+
β No API keys found.
|
|
279
|
+
Please either:
|
|
280
|
+
1. Pass keys directly: APIKeyRotator(api_keys=['key1', 'key2'])
|
|
281
|
+
2. Set environment variable: export API_KEYS='key1,key2'
|
|
282
|
+
3. Create .env file with: API_KEYS=key1,key2
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### ΠΡΠ»ΠΈ Π²ΡΠ΅ ΠΊΠ»ΡΡΠΈ ΠΈΡΡΠ΅ΡΠΏΠ°Π½Ρ
|
|
286
|
+
|
|
287
|
+
```python
|
|
288
|
+
try:
|
|
289
|
+
response = rotator.get("https://api.example.com/limited")
|
|
290
|
+
except AllKeysExhaustedError as e:
|
|
291
|
+
print(e) # "All 3 keys exhausted after 6 attempts"
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
## Π‘ΠΎΠ²ΠΌΠ΅ΡΡΠΈΠΌΠΎΡΡΡ π
|
|
295
|
+
|
|
296
|
+
- **Python**: 3.7+
|
|
297
|
+
- **ΠΠ°Π²ΠΈΡΠΈΠΌΠΎΡΡΠΈ**: ΡΠΎΠ»ΡΠΊΠΎ `requests>=2.25.0`
|
|
298
|
+
|
|
299
|
+
## Π Π°Π·ΡΠ°Π±ΠΎΡΠΊΠ° π οΈ
|
|
300
|
+
|
|
301
|
+
### Π£ΡΡΠ°Π½ΠΎΠ²ΠΊΠ° Π΄Π»Ρ ΡΠ°Π·ΡΠ°Π±ΠΎΡΠΊΠΈ
|
|
302
|
+
|
|
303
|
+
```bash
|
|
304
|
+
git clone https://github.com/PrimeevolutionZ/apikeyrotator.git
|
|
305
|
+
cd apikeyrotator
|
|
306
|
+
python -m venv venv
|
|
307
|
+
source venv/bin/activate # Linux/Mac
|
|
308
|
+
# ΠΈΠ»ΠΈ
|
|
309
|
+
venv\Scripts\activate # Windows
|
|
310
|
+
pip install -e .[dev]
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
### ΠΠ°ΠΏΡΡΠΊ ΡΠ΅ΡΡΠΎΠ²
|
|
314
|
+
|
|
315
|
+
```bash
|
|
316
|
+
pytest tests/
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### Π‘Π±ΠΎΡΠΊΠ° ΠΏΠ°ΠΊΠ΅ΡΠ°
|
|
320
|
+
|
|
321
|
+
```bash
|
|
322
|
+
python setup.py sdist bdist_wheel
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
## ΠΠΈΡΠ΅Π½Π·ΠΈΡ π
|
|
326
|
+
|
|
327
|
+
MIT License - ΡΠΌΠΎΡΡΠΈΡΠ΅ ΡΠ°ΠΉΠ» [LICENSE](LICENSE) Π΄Π»Ρ Π΄Π΅ΡΠ°Π»Π΅ΠΉ.
|
|
328
|
+
|
|
329
|
+
## ΠΠΎΠ΄Π΄Π΅ΡΠΆΠΊΠ° π€
|
|
330
|
+
|
|
331
|
+
ΠΠ°ΡΠ»ΠΈ Π±Π°Π³ ΠΈΠ»ΠΈ Π΅ΡΡΡ ΠΏΡΠ΅Π΄Π»ΠΎΠΆΠ΅Π½ΠΈΡ? [Π‘ΠΎΠ·Π΄Π°ΠΉΡΠ΅ issue](https://github.com/yourusername/apikeyrotator/issues) Π½Π° GitHub!
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
335
|
+
**API Key Rotator** - ΡΠ΄Π΅Π»Π°ΠΉΡΠ΅ Π²Π°ΡΠΈ API Π·Π°ΠΏΡΠΎΡΡ Π½Π΅ΡΡΠ·Π²ΠΈΠΌΡΠΌΠΈ ΠΊ ΠΎΠ³ΡΠ°Π½ΠΈΡΠ΅Π½ΠΈΡΠΌ! π
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
apikeyrotator/__init__.py
|
|
5
|
+
apikeyrotator/exceptions.py
|
|
6
|
+
apikeyrotator/rotator.py
|
|
7
|
+
apikeyrotator/utils.py
|
|
8
|
+
apikeyrotator.egg-info/PKG-INFO
|
|
9
|
+
apikeyrotator.egg-info/SOURCES.txt
|
|
10
|
+
apikeyrotator.egg-info/dependency_links.txt
|
|
11
|
+
apikeyrotator.egg-info/requires.txt
|
|
12
|
+
apikeyrotator.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
requests>=2.25.0
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
apikeyrotator
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "apikeyrotator"
|
|
7
|
+
version = "0.0.2"
|
|
8
|
+
description = "Ultra simple API key rotation for bypassing rate limits"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
authors = [
|
|
11
|
+
{ name = "Prime Evolution", email = "develop@eclips-team.ru" }
|
|
12
|
+
]
|
|
13
|
+
license = { file = "LICENSE" }
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 4 - Beta",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"License :: OSI Approved :: MIT License",
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Programming Language :: Python :: 3.7",
|
|
20
|
+
"Programming Language :: Python :: 3.8",
|
|
21
|
+
"Programming Language :: Python :: 3.9",
|
|
22
|
+
"Programming Language :: Python :: 3.10",
|
|
23
|
+
"Programming Language :: Python :: 3.11",
|
|
24
|
+
"Programming Language :: Python :: 3.12",
|
|
25
|
+
]
|
|
26
|
+
keywords = ["api", "rotation", "rate limit", "requests"]
|
|
27
|
+
dependencies = ["requests>=2.25.0"]
|
|
28
|
+
|
|
29
|
+
[project.urls]
|
|
30
|
+
Homepage = "https://github.com/PrimeevolutionZ/apikeyrotator"
|
|
31
|
+
Repository = "https://github.com/PrimeevolutionZ/apikeyrotator"
|
|
32
|
+
Issues = "https://github.com/PrimeevolutionZ/apikeyrotator/issues"
|